From dbe110db94dbe5a1c7bf5ab6ed8ed7bd3e2d07f8 Mon Sep 17 00:00:00 2001 From: Jason Reslock Date: Mon, 7 Jul 2025 10:07:52 -0400 Subject: [PATCH] fix(auth): SSO source profile authentication fails with invalid token error Problem: Profiles sourced from SSO profiles fail to authenticate with 'The security token included in the request is invalid' error. This is a regression introduced in v3.47.0 by commit 6f6a8c2fe. Solution: - Add recursive credential resolution for source profiles in makeSharedIniFileCredentialsProvider() - Check if source profile has static credentials; if not, recursively resolve them - Use resolved credentials for STS client instead of undefined values - Add test case for SSO source profile authentication Fixes #6782 --- .../providers/sharedCredentialsProvider.ts | 21 ++++++++-- .../sharedCredentialsProvider.test.ts | 41 +++++++++++++++++++ .../next-release/sso-source-profile-fix.json | 4 ++ 3 files changed, 62 insertions(+), 4 deletions(-) create mode 100644 packages/toolkit/.changes/next-release/sso-source-profile-fix.json diff --git a/packages/core/src/auth/providers/sharedCredentialsProvider.ts b/packages/core/src/auth/providers/sharedCredentialsProvider.ts index 717a151a3af..b3e36feee4e 100644 --- a/packages/core/src/auth/providers/sharedCredentialsProvider.ts +++ b/packages/core/src/auth/providers/sharedCredentialsProvider.ts @@ -408,10 +408,23 @@ export class SharedCredentialsProvider implements CredentialsProvider { } // Use source profile to assume IAM role based on role ARN provided. const sourceProfile = iniData[profile.source_profile!] - const stsClient = new DefaultStsClient(this.getDefaultRegion() ?? 'us-east-1', { - accessKeyId: sourceProfile.aws_access_key_id!, - secretAccessKey: sourceProfile.aws_secret_access_key!, - }) + + // Check if source profile has static credentials + let sourceCredentials: AWS.Credentials + if (sourceProfile.aws_access_key_id && sourceProfile.aws_secret_access_key) { + // Source profile has static credentials + sourceCredentials = { + accessKeyId: sourceProfile.aws_access_key_id, + secretAccessKey: sourceProfile.aws_secret_access_key, + sessionToken: sourceProfile.aws_session_token, + } + } else { + // Source profile needs to be resolved (e.g., SSO profile) + const sourceProvider = new SharedCredentialsProvider(profile.source_profile!, this.sections) + sourceCredentials = await sourceProvider.getCredentials() + } + + const stsClient = new DefaultStsClient(this.getDefaultRegion() ?? 'us-east-1', sourceCredentials) // Prompt for MFA Token if needed. const assumeRoleReq = { RoleArn: profile.role_arn, diff --git a/packages/core/src/test/credentials/provider/sharedCredentialsProvider.test.ts b/packages/core/src/test/credentials/provider/sharedCredentialsProvider.test.ts index b98ee128cbd..1166d1e864d 100644 --- a/packages/core/src/test/credentials/provider/sharedCredentialsProvider.test.ts +++ b/packages/core/src/test/credentials/provider/sharedCredentialsProvider.test.ts @@ -516,6 +516,47 @@ describe('SharedCredentialsProvider', async function () { assert.strictEqual(creds.sessionToken, 'token') }) + it('assumes role with SSO source profile', async function () { + const sections = await createTestSections(` + [profile default] + sso_account_id = 012345678910 + sso_role_name = admin + sso_region = us-east-1 + sso_registration_scopes = sso:account:access + sso_start_url = https://d-xxxxxxxxx.awsapps.com/start + region = us-east-1 + output = json + + [profile test] + source_profile = default + role_arn = testarn + region = us-east-1 + `) + + // Mock SSO credentials for the source profile + const ssoClient = stub(SsoClient, { region: 'us-east-1' }) + ssoClient.getRoleCredentials.callsFake(async (request) => { + assert.strictEqual(request.accountId, '012345678910') + assert.strictEqual(request.roleName, 'admin') + + return { + accessKeyId: 'sso-access-key', + secretAccessKey: 'sso-secret-key', + sessionToken: 'sso-session-token', + expiration: new Date(Date.now() + oneDay), + } + }) + sandbox.stub(SsoClient, 'create').returns(ssoClient) + sandbox.stub(SsoAccessTokenProvider.prototype, 'getToken').resolves() + sandbox.stub(SsoAccessTokenProvider.prototype, 'createToken').resolves() + + const sut = new SharedCredentialsProvider('test', sections) + const creds = await sut.getCredentials() + assert.strictEqual(creds.accessKeyId, 'id') + assert.strictEqual(creds.secretAccessKey, 'secret') + assert.strictEqual(creds.sessionToken, 'token') + }) + it('does not assume role when no roleArn is present', async function () { const sut = new SharedCredentialsProvider('default', await createTestSections(defaultSection)) const creds = await sut.getCredentials() diff --git a/packages/toolkit/.changes/next-release/sso-source-profile-fix.json b/packages/toolkit/.changes/next-release/sso-source-profile-fix.json new file mode 100644 index 00000000000..f105348429f --- /dev/null +++ b/packages/toolkit/.changes/next-release/sso-source-profile-fix.json @@ -0,0 +1,4 @@ +{ + "type": "Bug Fix", + "description": "Profiles sourced from SSO profiles fail to authenticate with 'invalid security token' error" +}