SSO Credentials Provider fails to load AWS credentials if the AWS IAM Identity Center access token associated with an SSO session has expired or is about to expire
Describe the bug
When using AWS IAM Identity Center authentication with the sso-session based configuration, the AWS SDK SSO Credential Provider fails to load AWS credentials if the AWS IAM Identity Center access token cached to disk requires a refresh.
Expected Behavior
AWS SDK SSO Credential Provider refreshes the AWS IAM Identity Center access token associated with the SSO session and uses the fresh access token to fetch AWS credentials from AWS IAM Identity Center.
Current Behavior
AWS SDK SSO Credential Provider fails to obtain a fresh AWS IAM Identity Center access token if the previous token requires refresh (has expired or is expiring within next 5 minutes).
Reproduction Steps
- Create file called
configwith following content (fill in your AWS IAM Identity Center details):
[sso-session mysso]
sso_start_url = https://mysso.awsapps.com/start
sso_region = eu-west-1
[profile profile-1]
sso_session = mysso
sso_account_id = 000000000000
sso_role_name = MyRole
- Login to AWS IAM Identity Center:
aws sso login --session-name mysso - Update the
expiresAtfield value to a timestamp that is in the past in the token cache file Step 2 created under~/.aws/sso/cache/(you can also wait for an hour for the access token on disk to expire) - Create file called
index.jswith following content:
const AWS = require("aws-sdk");
const sts = new AWS.STS();
(async () => {console.log(await sts.getCallerIdentity().promise())})();
- Execute following command:
env AWS_CONFIG_FILE=./config AWS_REGION=eu-west-1 AWS_PROFILE=profile-1 node index.js
This should fail with the following error which indicates SSO Credential Provider failed to resolve credentials and SDK fell through to latter providers in the chain (SSO credential provider errors are not logged):
Error [CredentialsError]: Missing credentials in config, if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1
at Timeout.connectTimeout [as _onTimeout] (/sdkbug/node_modules/aws-sdk/lib/http/node.js:69:15)
at listOnTimeout (node:internal/timers:569:17)
at process.processTimers (node:internal/timers:512:7) {
code: 'CredentialsError',
time: 2023-06-08T04:16:52.349Z,
retryable: true,
originalError: {
message: 'Could not load credentials from any providers',
code: 'CredentialsError',
time: 2023-06-08T04:16:52.349Z,
retryable: true,
originalError: {
message: 'EC2 Metadata roleName request returned error',
code: 'TimeoutError',
time: 2023-06-08T04:16:52.348Z,
retryable: true,
originalError: {
message: 'Socket timed out without establishing a connection',
code: 'TimeoutError',
time: 2023-06-08T04:16:52.347Z,
retryable: true
}
}
}
}
Possible Solution
Here's the possible cause for this issue.
SsoCredentials.load() calls SsoCredentials.getToken():
https://github.com/aws/aws-sdk-js/blob/cae1321d3b665dbf3f9844a03bc198a7675b0e68/lib/credentials/sso_credentials.js#L114
SsoCredentials.getToken() creates a new SSOTokenProvider:
https://github.com/aws/aws-sdk-js/blob/cae1321d3b665dbf3f9844a03bc198a7675b0e68/lib/credentials/sso_credentials.js#L176-L178
SSOTokenProvider constructor invokes SSOTokenProvider.get() to load/refresh the token:
https://github.com/aws/aws-sdk-js/blob/cae1321d3b665dbf3f9844a03bc198a7675b0e68/lib/token/sso_token_provider.js#L99
Call to get() triggers a call to SSOTokenProvider.load(). This reads the the expired token from disk, notices it has expired and starts to refresh it in the background (async call). load() also sets the lastRefreshAttemptTime to current time:
https://github.com/aws/aws-sdk-js/blob/cae1321d3b665dbf3f9844a03bc198a7675b0e68/lib/token/sso_token_provider.js#L202
SSOTokenProvider constructor returns as load() made an async call. SsoCredentials.getToken() continues and calls SSOTokenProvider.load() (this is the second call to SSOTokenProvider.load()):
https://github.com/aws/aws-sdk-js/blob/cae1321d3b665dbf3f9844a03bc198a7675b0e68/lib/credentials/sso_credentials.js#L179-L184
SSOTokenProvider notices that the token on disk is still expired (refresh process is still running in the background). But when it checks the lastRefreshAttemptTime, it seems that a refresh was just started and it triggers the SsoCredentials callback without a valid token:
https://github.com/aws/aws-sdk-js/blob/cae1321d3b665dbf3f9844a03bc198a7675b0e68/lib/token/sso_token_provider.js#L180-L184
SsoCredentials.getToken() assumes a token is now available and returns that to SsoCredentials.load():
https://github.com/aws/aws-sdk-js/blob/cae1321d3b665dbf3f9844a03bc198a7675b0e68/lib/credentials/sso_credentials.js#L183
However, since the refresh had not yet completed, ssoTokenProvider.token is undefined and SsoCredentials.load() is unable to fetch AWS credentials with it.
Because of this AWS SDK discards the SsoCredentials provider and moves forward to try other providers in the chain. During this time the initial AWS IAM Identity Center access token refresh might complete and a fresh access token might be written to disk. So the next time SsoCredentials provider tries to load AWS credentials, it might find a valid token from disk and the provider is able to fetch AWS credentials from AWS IAM Identity Center.
If I'm reading the code correctly, SsoCredentials.getToken() should call the SSOTokenProvider.get() instead of SSOTokenProvider.load(). The get method seems to fold multiple token load attempts into one load as per logic in:
https://github.com/aws/aws-sdk-js/blob/cae1321d3b665dbf3f9844a03bc198a7675b0e68/lib/token.js#L175
Additional Information/Context
No response
SDK version used
2.1393.0
Environment details (OS name and version, etc.)
Just to confirm that any fix for these issues will also fix the issue where it seems aws-cdk doesn't engage the token refreshing at all? Because https://github.com/aws/aws-cdk/issues/24782 has been said to be tracked by the changes on ☝️. And so I hope that carries over to this too!
It's not entirely clear if this is the issue that causes the CDK issue you linked to. Currently CDK uses both SDK v2 and v3 internally and I'm not sure which SDK is used for the credential resolution over there.
However, it's very likely that the CDK problem is caused by either this or https://github.com/aws/aws-sdk-js-v3/issues/4798 depending on which SDK version is used there. I guess we'll see that when fix to this and https://github.com/aws/aws-sdk-js-v3/issues/4798 is merged and released, and CDK is updated to take newer version of SDKs into use.
I'm still trying actions in maticnetwork/polygon-edge but nightly build #76 run fails in https://github.com/Ishmaello/polygo-edge/actions/runs/5779793298/job/15662572723 with response "Run aws-actions/configure-aws-credentials@v2 with: audience: sts.amazonaws.com Error: Input required and not supplied: aws-region (node:1703) NOTE: We are formalizing our plans to enter AWS SDK for JavaScript (v2) into maintenance mode in 2023.
Please migrate your code to use AWS SDK for JavaScript (v3).
For more information, check the migration guide at https://a.co/7PzMCcy
(Use node --trace-warnings ... to show where the warning was created"
I believe this is also related, see: https://github.com/aws/aws-cdk/issues/27265#issuecomment-1742139875
@marcusirgens found that removing the sso_session from his ~/.aws/config file fixed his issues. I was able to verify that removing sso_session also fixed my cdk cli issues.
Here is a diff I made which triggers the bug. The inclusion of sso_session does not break using the aws cli, or boto3 session using the same sso profile I've configured. just cdk, which uses aws-sdk-js.
https://github.com/aws/aws-sdk-js/compare/master...rangerthegood:aws-sdk-js:sso_bug
Hi, what's holding back this issue's PR from getting merged?
My team has to log in every hour despite my session length being 12 hours. This is quite frustrating
Hi, could you get this issue's PR in? We are also being affected by this issue and having to log in every hour is a pain.
+1 ^
I hope my aws account has been deleted I'll start afresh