private static async Task <GetAuthorizationTokenResponse> GetAuthorizationToken(string accessKeyId, string secretKey) { Output.Verbose($"Retrieving authorization token {nameof(accessKeyId)}={accessKeyId}, {nameof(secretKey)}={secretKey}."); GetAuthorizationTokenResponse authorizationToken; // We only want to get & save secrets from/to local configuration when we have not provided them var needToStoreSecrets = false; if (accessKeyId == null || secretKey == null) { Output.Verbose("Attempting to load AWS credentials from secrets store."); var securityConfig = GetConfiguration(); if (accessKeyId == null) { accessKeyId = securityConfig[SecretStoreKeys.AccessKeyId]; } if (secretKey == null) { secretKey = securityConfig[SecretStoreKeys.SecretKey]; } } var firstPrompt = true; do { if (accessKeyId == null || secretKey == null) { Output.Verbose("Attempting to prompt for AWS credentials."); needToStoreSecrets = true; if (!ConsoleHelper.IsInteractive) { // CLI is running from within a script in a non-interactive terminal, e.g., from pressing F5 in VS Code. The // auth command will get users to this same code path, but in an interactive terminal. var cliCommand = Program.CliCommandName; var authCommand = new AuthorizeCommand().CommandName; Output.Error(Output.HorizontalLine()); Output.Error("Your machine is missing necessary AWS credentials. Authorize your machine by opening a terminal and " + "running the following commands, then try your actions again. This authorization only needs to be performed once per user/machine:"); Output.Error($"> cd {Directory.GetCurrentDirectory()}"); Output.Error($"> dotnet {cliCommand} {authCommand}"); Output.Error($"If you have further issues, please contact #watch-ops."); Output.Error(Output.HorizontalLine()); return(null); } // TODO add link to docs page for finding/creating AWS access keys and getting help from watch-ops to set up account // or add permissions. if (firstPrompt) { Output.Error("Your machine is missing necessary AWS credentials. The following authorization only needs to be performed " + "once per user/machine. For assistance, please contact #watch-ops."); firstPrompt = false; } Output.Error("Paste in your AWS ACCESS KEY ID:"); accessKeyId = ConsoleHelper.ReadPassword(); Output.Error("Now paste in your AWS SECRET ACCESS KEY:"); secretKey = ConsoleHelper.ReadPassword(); } try { var ecrClient = new AmazonECRClient(accessKeyId, secretKey, _regionEndpoint); // TODO Potentially slow. We might need to create a cached toked since it'll be valid for 12 hours // Another thing is that there is a throttling for GetAuthorizationTokenAsync (1 call / second) // There's not a good way to check the stored credentials without making // an actual call to ECR (e.g. a docker pull). Doing that on every operation // would be relatively slow. Could we write a hidden file on successful auth // check and expire it after 8 hours? Output.Verbose($"Retrieving ECR authentication token"); authorizationToken = await ecrClient.GetAuthorizationTokenAsync(new GetAuthorizationTokenRequest(), new CancellationTokenSource(_awsTimeout).Token); // We have successfully got authorization token let's save our access key and secret in user-secrets. // We only save them when we've not provided access and secret via parameters. if (needToStoreSecrets) { Output.Verbose($"Storing secret {SecretStoreKeys.AccessKeyId}"); await SaveSecret(SecretStoreKeys.AccessKeyId, accessKeyId); Output.Verbose($"Storing secret {SecretStoreKeys.SecretKey}"); await SaveSecret(SecretStoreKeys.SecretKey, secretKey); } break; } catch (Exception e) { var amazonEcrException = e.InnerException as AmazonECRException; if (amazonEcrException != null && amazonEcrException.ErrorCode.Equals("UnrecognizedClientException")) { Output.VerboseException(e); Output.Error("Bad credentials. Please try again."); accessKeyId = null; secretKey = null; continue; } // TODO handle case where permissions are inadequate (?) Output.Error("Something went wrong while connecting to AWS. Please try again later."); Output.Exception(e); throw; } } while (true); return(authorizationToken); }