diff --git a/packages/credential-provider-web-identity/README.md b/packages/credential-provider-web-identity/README.md index b06386b2b38f..cbaab85cfd70 100644 --- a/packages/credential-provider-web-identity/README.md +++ b/packages/credential-provider-web-identity/README.md @@ -7,6 +7,86 @@ This module includes functions which get credentials by calling STS assumeRoleWithWebIdentity API. +## fromWebToken + +The function `fromWebToken` returns `CredentialProvider` that get credentials calling sts:assumeRoleWithWebIdentity +API via `roleAssumerWithWebIdentity`. + +### Supported configuration + +This configuration supports all the input parameters from +[sts:AssumeWithWebIdentity](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-sts/modules/assumerolewithwebidentityrequest.html) API. The following options are supported: + +- `roleArn` - The Amazon Resource Name (ARN) of the role that the caller is assuming. +- `webIdentityToken` - The OAuth 2.0 access token or OpenID Connect ID token that is provided by the identity provider. +- `roleSessionName` - An identifier for the assumed role session. +- `providerId` - The fully qualified host component of the domain name of the identity provider. Do not specify this + value for OpenID Connect ID tokens. +- `policyArns` - The Amazon Resource Names (ARNs) of the IAM managed policies that you want to use as managed session + policies. +- `policy` - An IAM policy in JSON format that you want to use as an inline session policy. +- `durationSeconds` - The duration, in seconds, of the role session. Default to 3600. +- `roleAssumerWithWebIdentity` - A function that assumes a role with web identity + and returns a promise fulfilled with credentials for the assumed role. You may call + `sts:assumeRoleWithWebIdentity` API within this function. + +### Examples + +You can directly configure individual identity providers to access AWS resources using web identity federation. AWS +currently supports authenticating users using web identity federation through several identity providers: + +- [Login with Amazon](https://login.amazon.com/) + +- [Facebook Login](https://developers.facebook.com/docs/facebook-login/web/) + +- [Google Sign-in](https://developers.google.com/identity/) + +You must first register your application with the providers that your application supports. Next, create an IAM role and +set up permissions for it. The IAM role you create is then used to grant the permissions you configured for it through +the respective identity provider. For example, you can set up a role that allows users who logged in through Facebook +to have read access to a specific Amazon S3 bucket you control. + +After you have both an IAM role with configured privileges and an application registered with your chosen identity +providers, you can set up the SDK to get credentials for the IAM role using helper code, as follows: + +```javascript +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { STSClient, AssumeRoleWithWebIdentityCommand } from "@aws-sdk/client-sts"; +import { fromWebToken } from "@aws-sdk/credential-provider-web-identity"; + +const stsClient = new STSClient({}); + +const roleAssumerWithWebIdentity = async (params) => { + const { Credentials } = await stsClient.send( + new AssumeRoleWithWebIdentityCommand(params) + ); + if (!Credentials || !Credentials.AccessKeyId || !Credentials.SecretAccessKey) { + throw new Error(`Invalid response from STS.assumeRole call with role ${params.RoleArn}`); + } + return { + accessKeyId: Credentials.AccessKeyId, + secretAccessKey: Credentials.SecretAccessKey, + sessionToken: Credentials.SessionToken, + expiration: Credentials.Expiration, + }; +}; + +const dynamodb = new DynamoDBClient({ + region, + credentials: fromWebToken({ + roleArn: 'arn:aws:iam::/:role/', + providerId: 'graph.facebook.com|www.amazon.com', // this is null for Google + webIdentityToken: ACCESS_TOKEN // from OpenID token identity provider + roleAssumerWithWebIdentity, + }) +}); + +``` + +The value in the ProviderId parameter depends on the specified identity provider. The value in the WebIdentityToken +parameter is the access token retrieved from a successful login with the identity provider. For more information on how +to configure and retrieve access tokens for each identity provider, see the documentation for the identity provider. + ## fromTokenFile The function `fromTokenFile` returns `CredentialProvider` that reads credentials as follows: diff --git a/packages/credential-provider-web-identity/src/fromTokenFile.spec.ts b/packages/credential-provider-web-identity/src/fromTokenFile.spec.ts index 1097a616c62c..3fd71915a635 100644 --- a/packages/credential-provider-web-identity/src/fromTokenFile.spec.ts +++ b/packages/credential-provider-web-identity/src/fromTokenFile.spec.ts @@ -1,7 +1,9 @@ -import { ProviderError } from "@aws-sdk/property-provider"; import { readFileSync } from "fs"; - -import { AssumeRoleWithWebIdentityParams, fromTokenFile, FromTokenFileInit } from "./fromTokenFile"; +jest.mock("./fromWebToken", () => ({ + fromWebToken: jest.fn().mockReturnValue(() => Promise.resolve(MOCK_CREDS)), +})); +import { fromTokenFile } from "./fromTokenFile"; +import { fromWebToken } from "./fromWebToken"; const ENV_TOKEN_FILE = "AWS_WEB_IDENTITY_TOKEN_FILE"; const ENV_ROLE_ARN = "AWS_ROLE_ARN"; @@ -30,57 +32,6 @@ describe(fromTokenFile.name, () => { jest.restoreAllMocks(); }); - const testRoleAssumerWithWebIdentityNotDefined = async (init: FromTokenFileInit, roleArn: string) => { - try { - // @ts-ignore An argument for 'init' was not provided. - await fromTokenFile(init)(); - fail(`Expected error to be thrown`); - } catch (error) { - expect(error).toEqual( - new ProviderError( - `Role Arn '${roleArn}' needs to be assumed with web identity, but no role assumption callback was provided.`, - false - ) - ); - } - }; - - const testReadFileSyncError = async (init: FromTokenFileInit) => { - const readFileSyncError = new Error("readFileSyncError"); - (readFileSync as jest.Mock).mockImplementation(() => { - throw readFileSyncError; - }); - try { - await fromTokenFile(init)(); - fail(`Expected error to be thrown`); - } catch (error) { - expect(error).toEqual(readFileSyncError); - } - expect(readFileSync).toHaveBeenCalledTimes(1); - }; - - const testRoleAssumerWithWebIdentitySuccess = async (init: FromTokenFileInit) => { - const creds = await fromTokenFile(init)(); - expect(creds).toEqual(MOCK_CREDS); - expect(readFileSync).toHaveBeenCalledTimes(1); - expect(readFileSync).toHaveBeenCalledWith(mockTokenFile, { encoding: "ascii" }); - }; - - const testRandomValueForRoleSessionName = async (init: FromTokenFileInit) => { - const mockDateNow = Date.now(); - const spyDateNow = jest.spyOn(Date, "now").mockReturnValueOnce(mockDateNow); - - const creds = await fromTokenFile({ - ...init, - roleAssumerWithWebIdentity: async (params: AssumeRoleWithWebIdentityParams) => { - expect(params.RoleSessionName).toEqual(`aws-sdk-js-session-${mockDateNow}`); - return MOCK_CREDS; - }, - })(); - expect(creds).toEqual(MOCK_CREDS); - expect(spyDateNow).toHaveBeenCalledTimes(1); - }; - describe("reads config from env", () => { const original_ENV_TOKEN_FILE = process.env[ENV_TOKEN_FILE]; const original_ENV_ROLE_ARN = process.env[ENV_ROLE_ARN]; @@ -98,83 +49,70 @@ describe(fromTokenFile.name, () => { process.env[ENV_ROLE_SESSION_NAME] = original_ENV_ROLE_SESSION_NAME; }); - it("throws if roleAssumerWithWebIdentity is not defined", async () => { - return testRoleAssumerWithWebIdentityNotDefined({}, process.env[ENV_ROLE_ARN]); + it(`passes values to ${fromWebToken.name}`, async () => { + const roleAssumerWithWebIdentity = jest.fn(); + const creds = await fromTokenFile({ + roleAssumerWithWebIdentity, + })(); + expect(creds).toEqual(MOCK_CREDS); + expect(fromWebToken as jest.Mock).toBeCalledTimes(1); + const webTokenInit = (fromWebToken as jest.Mock).mock.calls[0][0]; + expect(webTokenInit.webIdentityToken).toBe(mockTokenValue); + expect(webTokenInit.roleSessionName).toBe(mockRoleSessionName); + expect(webTokenInit.roleArn).toBe(mockRoleArn); + expect(webTokenInit.roleAssumerWithWebIdentity).toBe(roleAssumerWithWebIdentity); }); - it("throws if ENV_TOKEN_FILE read from disk failed", async () => { - return testReadFileSyncError({ - roleAssumerWithWebIdentity: async (params: AssumeRoleWithWebIdentityParams) => { - return MOCK_CREDS; - }, - }); + it("prefers init parameters over environmental variables", async () => { + const roleAssumerWithWebIdentity = jest.fn(); + const init = { + webIdentityTokenFile: "anotherTokenFile", + roleArn: "anotherRoleArn", + roleSessionName: "anotherRoleSessionName", + roleAssumerWithWebIdentity, + }; + const creds = await fromTokenFile(init)(); + expect(creds).toEqual(MOCK_CREDS); + expect(fromWebToken as jest.Mock).toBeCalledTimes(1); + const webTokenInit = (fromWebToken as jest.Mock).mock.calls[0][0]; + expect(webTokenInit.roleSessionName).toBe(init.roleSessionName); + expect(webTokenInit.roleArn).toBe(init.roleArn); + expect(webTokenInit.roleAssumerWithWebIdentity).toBe(roleAssumerWithWebIdentity); + expect(readFileSync as jest.Mock).toBeCalledTimes(1); + expect((readFileSync as jest.Mock).mock.calls[0][0]).toBe(init.webIdentityTokenFile); }); - it("passes values to roleAssumerWithWebIdentity", async () => { - return testRoleAssumerWithWebIdentitySuccess({ - roleAssumerWithWebIdentity: async (params: AssumeRoleWithWebIdentityParams) => { - expect(params.WebIdentityToken).toEqual(mockTokenValue); - expect(params.RoleArn).toEqual(mockRoleArn); - expect(params.RoleSessionName).toEqual(mockRoleSessionName); - return MOCK_CREDS; - }, + it("throws if ENV_TOKEN_FILE read from disk failed", async () => { + const readFileSyncError = new Error("readFileSyncError"); + (readFileSync as jest.Mock).mockImplementation(() => { + throw readFileSyncError; }); - }); - - it("generates a random value for RoleSessionName if not available", async () => { - delete process.env[ENV_ROLE_SESSION_NAME]; - return testRandomValueForRoleSessionName({}); - }); - }); - - describe("reads config from configuration keys", () => { - const original_ENV_TOKEN_FILE = process.env[ENV_TOKEN_FILE]; - const original_ENV_ROLE_ARN = process.env[ENV_ROLE_ARN]; - const original_ENV_ROLE_SESSION_NAME = process.env[ENV_ROLE_SESSION_NAME]; - - beforeAll(() => { - delete process.env[ENV_TOKEN_FILE]; - delete process.env[ENV_ROLE_ARN]; - delete process.env[ENV_ROLE_SESSION_NAME]; - }); - - afterAll(() => { - process.env[ENV_TOKEN_FILE] = original_ENV_TOKEN_FILE; - process.env[ENV_ROLE_ARN] = original_ENV_ROLE_ARN; - process.env[ENV_ROLE_SESSION_NAME] = original_ENV_ROLE_SESSION_NAME; - }); - - it("throws if roleAssumerWithWebIdentity is not defined", async () => { - return testRoleAssumerWithWebIdentityNotDefined({ roleArn: mockRoleArn }, mockRoleArn); + try { + await fromTokenFile({ roleAssumerWithWebIdentity: jest.fn() })(); + fail(`Expected error to be thrown`); + } catch (error) { + expect(error).toEqual(readFileSyncError); + } + expect(readFileSync).toHaveBeenCalledTimes(1); }); it("throws if web_identity_token_file read from disk failed", async () => { - return testReadFileSyncError({ - webIdentityTokenFile: mockTokenFile, - roleArn: mockRoleArn, - roleSessionName: mockRoleSessionName, - roleAssumerWithWebIdentity: async (params: AssumeRoleWithWebIdentityParams) => { - return MOCK_CREDS; - }, - }); - }); - - it("passes values to roleAssumerWithWebIdentity", async () => { - return testRoleAssumerWithWebIdentitySuccess({ - webIdentityTokenFile: mockTokenFile, - roleArn: mockRoleArn, - roleSessionName: mockRoleSessionName, - roleAssumerWithWebIdentity: async (params: AssumeRoleWithWebIdentityParams) => { - expect(params.WebIdentityToken).toEqual(mockTokenValue); - expect(params.RoleArn).toEqual(mockRoleArn); - expect(params.RoleSessionName).toEqual(mockRoleSessionName); - return MOCK_CREDS; - }, + const readFileSyncError = new Error("readFileSyncError"); + (readFileSync as jest.Mock).mockImplementation(() => { + throw readFileSyncError; }); - }); - - it("generates a random value for RoleSessionName if not available", async () => { - return testRandomValueForRoleSessionName({ webIdentityTokenFile: mockTokenFile, roleArn: mockRoleArn }); + try { + await fromTokenFile({ + webIdentityTokenFile: mockTokenFile, + roleArn: mockRoleArn, + roleSessionName: mockRoleSessionName, + roleAssumerWithWebIdentity: jest.fn(), + })(); + fail(`Expected error to be thrown`); + } catch (error) { + expect(error).toEqual(readFileSyncError); + } + expect(readFileSync).toHaveBeenCalledTimes(1); }); }); }); diff --git a/packages/credential-provider-web-identity/src/fromTokenFile.ts b/packages/credential-provider-web-identity/src/fromTokenFile.ts index 5d1ffc9c58bb..3b598fcc437f 100644 --- a/packages/credential-provider-web-identity/src/fromTokenFile.ts +++ b/packages/credential-provider-web-identity/src/fromTokenFile.ts @@ -1,78 +1,29 @@ -import { ProviderError } from "@aws-sdk/property-provider"; -import { CredentialProvider, Credentials } from "@aws-sdk/types"; +import { CredentialProvider } from "@aws-sdk/types"; import { readFileSync } from "fs"; +import { fromWebToken, FromWebTokenInit } from "./fromWebToken"; + const ENV_TOKEN_FILE = "AWS_WEB_IDENTITY_TOKEN_FILE"; const ENV_ROLE_ARN = "AWS_ROLE_ARN"; const ENV_ROLE_SESSION_NAME = "AWS_ROLE_SESSION_NAME"; -export interface AssumeRoleWithWebIdentityParams { - /** - *

The Amazon Resource Name (ARN) of the role that the caller is assuming.

- */ - RoleArn: string; - /** - *

An identifier for the assumed role session. Typically, you pass the name or identifier - * that is associated with the user who is using your application. That way, the temporary - * security credentials that your application will use are associated with that user. This - * session name is included as part of the ARN and assumed role ID in the - * AssumedRoleUser response element.

- *

The regex used to validate this parameter is a string of characters - * consisting of upper- and lower-case alphanumeric characters with no spaces. You can - * also include underscores or any of the following characters: =,.@-

- */ - RoleSessionName: string; - /** - *

The OAuth 2.0 access token or OpenID Connect ID token that is provided by the identity - * provider. Your application must get this token by authenticating the user who is using your - * application with a web identity provider before the application makes an - * AssumeRoleWithWebIdentity call.

- */ - WebIdentityToken: string; -} -export interface FromTokenFileInit { +export interface FromTokenFileInit extends Partial> { /** * File location of where the `OIDC` token is stored. */ webIdentityTokenFile?: string; - - /** - * The IAM role wanting to be assumed. - */ - roleArn?: string; - - /** - * The IAM session name used to distinguish sessions. - */ - roleSessionName?: string; - - /** - * A function that assumes a role with web identity and returns a promise fulfilled with - * credentials for the assumed role. - * - * @param sourceCreds The credentials with which to assume a role. - * @param params - */ - roleAssumerWithWebIdentity?: (params: AssumeRoleWithWebIdentityParams) => Promise; } /** * Represents OIDC credentials from a file on disk. */ -export const fromTokenFile = (init: FromTokenFileInit): CredentialProvider => async () => { - const { webIdentityTokenFile, roleArn, roleSessionName, roleAssumerWithWebIdentity } = init; - - if (!roleAssumerWithWebIdentity) { - throw new ProviderError( - `Role Arn '${roleArn ?? process.env[ENV_ROLE_ARN]}' needs to be assumed with web identity,` + - ` but no role assumption callback was provided.`, - false - ); - } - - return roleAssumerWithWebIdentity({ - WebIdentityToken: readFileSync(webIdentityTokenFile ?? process.env[ENV_TOKEN_FILE]!, { encoding: "ascii" }), - RoleArn: roleArn ?? process.env[ENV_ROLE_ARN]!, - RoleSessionName: roleSessionName ?? process.env[ENV_ROLE_SESSION_NAME] ?? `aws-sdk-js-session-${Date.now()}`, +export const fromTokenFile = (init: FromTokenFileInit): CredentialProvider => { + const { webIdentityTokenFile, roleArn, roleSessionName } = init; + + return fromWebToken({ + ...init, + webIdentityToken: readFileSync(webIdentityTokenFile ?? process.env[ENV_TOKEN_FILE]!, { encoding: "ascii" }), + roleArn: roleArn ?? process.env[ENV_ROLE_ARN]!, + roleSessionName: roleSessionName ?? process.env[ENV_ROLE_SESSION_NAME], }); }; diff --git a/packages/credential-provider-web-identity/src/fromWebToken.spec.ts b/packages/credential-provider-web-identity/src/fromWebToken.spec.ts new file mode 100644 index 000000000000..56931b1d764a --- /dev/null +++ b/packages/credential-provider-web-identity/src/fromWebToken.spec.ts @@ -0,0 +1,84 @@ +import { ProviderError } from "@aws-sdk/property-provider"; + +import { fromWebToken } from "./fromWebToken"; + +const mockToken = "exampletoken"; +const mockRoleArn = "mockRoleArn"; +const mockRoleSessionName = "mockRoleSessionName"; +const mockPolicy = "mockPolicy"; +const mockPolicyArns = [{ arn: "policyArn" }]; +const mockProviderId = "mockProviderId"; +const mockDurationSeconds = 7200; +const MOCK_CREDS = { + accessKeyId: "accessKeyId", + secretAccessKey: "secretAccessKey", + sessionToken: "sessionToken", +}; + +describe("fromWebToken", () => { + afterEach(() => { + jest.clearAllMocks(); + jest.restoreAllMocks(); + }); + it("throws if roleAssumerWithWebIdentity is not defined", async () => { + try { + await fromWebToken({ + webIdentityToken: mockToken, + roleArn: mockRoleArn, + })(); + fail(`Expected error to be thrown`); + } catch (error) { + expect(error).toEqual( + new ProviderError( + `Role Arn '${mockRoleArn}' needs to be assumed with web identity, but no role assumption callback was provided.`, + false + ) + ); + } + }); + + it("passes values to roleAssumerWithWebIdentity", async () => { + expect.assertions(2); + const init = { + webIdentityToken: mockToken, + roleArn: mockRoleArn, + roleSessionName: mockRoleSessionName, + providerId: mockProviderId, + policyArns: mockPolicyArns, + policy: mockPolicy, + durationSeconds: mockDurationSeconds, + }; + const creds = await fromWebToken({ + ...init, + roleAssumerWithWebIdentity: async (params) => { + expect(params).toMatchObject({ + WebIdentityToken: mockToken, + RoleArn: mockRoleArn, + RoleSessionName: mockRoleSessionName, + ProviderId: mockProviderId, + PolicyArns: mockPolicyArns, + Policy: mockPolicy, + DurationSeconds: mockDurationSeconds, + }); + return MOCK_CREDS; + }, + })(); + expect(creds).toEqual(MOCK_CREDS); + }); + + it("generates a random value for RoleSessionName if not available", async () => { + const mockDateNow = Date.now(); + const spyDateNow = jest.spyOn(Date, "now").mockReturnValueOnce(mockDateNow); + + const creds = await fromWebToken({ + webIdentityToken: mockToken, + roleArn: mockRoleArn, + roleAssumerWithWebIdentity: async (params) => { + expect(params.RoleSessionName).toEqual(`aws-sdk-js-session-${mockDateNow}`); + return MOCK_CREDS; + }, + })(); + expect(creds).toEqual(MOCK_CREDS); + expect(spyDateNow).toHaveBeenCalledTimes(1); + }); +}); diff --git a/packages/credential-provider-web-identity/src/fromWebToken.ts b/packages/credential-provider-web-identity/src/fromWebToken.ts new file mode 100644 index 000000000000..2093e7fea668 --- /dev/null +++ b/packages/credential-provider-web-identity/src/fromWebToken.ts @@ -0,0 +1,158 @@ +import { ProviderError } from "@aws-sdk/property-provider"; +import { CredentialProvider, Credentials } from "@aws-sdk/types"; + +export interface AssumeRoleWithWebIdentityParams { + /** + *

The Amazon Resource Name (ARN) of the role that the caller is assuming.

+ */ + RoleArn: string; + /** + *

An identifier for the assumed role session. Typically, you pass the name or identifier + * that is associated with the user who is using your application. That way, the temporary + * security credentials that your application will use are associated with that user. This + * session name is included as part of the ARN and assumed role ID in the + * AssumedRoleUser response element.

+ *

The regex used to validate this parameter is a string of characters + * consisting of upper- and lower-case alphanumeric characters with no spaces. You can + * also include underscores or any of the following characters: =,.@-

+ */ + RoleSessionName: string; + /** + *

The OAuth 2.0 access token or OpenID Connect ID token that is provided by the identity + * provider. Your application must get this token by authenticating the user who is using your + * application with a web identity provider before the application makes an + * AssumeRoleWithWebIdentity call.

+ */ + WebIdentityToken: string; + + /** + *

The fully qualified host component of the domain name of the identity provider.

+ *

Specify this value only for OAuth 2.0 access tokens. Currently + * www.amazon.com and graph.facebook.com are the only supported + * identity providers for OAuth 2.0 access tokens. Do not include URL schemes and port + * numbers.

+ *

Do not specify this value for OpenID Connect ID tokens.

+ */ + ProviderId?: string; + + /** + *

The Amazon Resource Names (ARNs) of the IAM managed policies that you want to use as + * managed session policies. The policies must exist in the same account as the role.

+ *

This parameter is optional. You can provide up to 10 managed policy ARNs. However, the + * plain text that you use for both inline and managed session policies can't exceed 2,048 + * characters. For more information about ARNs, see Amazon Resource Names (ARNs) and AWS + * Service Namespaces in the AWS General Reference.

+ * + *

An AWS conversion compresses the passed session policies and session tags into a + * packed binary format that has a separate limit. Your request can fail for this limit + * even if your plain text meets the other requirements. The PackedPolicySize + * response element indicates by percentage how close the policies and tags for your + * request are to the upper size limit. + *

+ *
+ * + *

Passing policies to this operation returns new + * temporary credentials. The resulting session's permissions are the intersection of the + * role's identity-based policy and the session policies. You can use the role's temporary + * credentials in subsequent AWS API calls to access resources in the account that owns + * the role. You cannot use session policies to grant more permissions than those allowed + * by the identity-based policy of the role that is being assumed. For more information, see + * Session + * Policies in the IAM User Guide.

+ */ + PolicyArns?: { arn?: string }[]; + + /** + *

An IAM policy in JSON format that you want to use as an inline session policy.

+ *

This parameter is optional. Passing policies to this operation returns new + * temporary credentials. The resulting session's permissions are the intersection of the + * role's identity-based policy and the session policies. You can use the role's temporary + * credentials in subsequent AWS API calls to access resources in the account that owns + * the role. You cannot use session policies to grant more permissions than those allowed + * by the identity-based policy of the role that is being assumed. For more information, see + * Session + * Policies in the IAM User Guide.

+ *

The plain text that you use for both inline and managed session policies can't exceed + * 2,048 characters. The JSON policy characters can be any ASCII character from the space + * character to the end of the valid character list (\u0020 through \u00FF). It can also + * include the tab (\u0009), linefeed (\u000A), and carriage return (\u000D) + * characters.

+ * + *

An AWS conversion compresses the passed session policies and session tags into a + * packed binary format that has a separate limit. Your request can fail for this limit + * even if your plain text meets the other requirements. The PackedPolicySize + * response element indicates by percentage how close the policies and tags for your + * request are to the upper size limit. + *

+ *
+ */ + Policy?: string; + + /** + *

The duration, in seconds, of the role session. The value can range from 900 seconds (15 + * minutes) up to the maximum session duration setting for the role. This setting can have a + * value from 1 hour to 12 hours. If you specify a value higher than this setting, the + * operation fails. For example, if you specify a session duration of 12 hours, but your + * administrator set the maximum session duration to 6 hours, your operation fails. To learn + * how to view the maximum value for your role, see View the + * Maximum Session Duration Setting for a Role in the + * IAM User Guide.

+ *

By default, the value is set to 3600 seconds.

+ * + *

The DurationSeconds parameter is separate from the duration of a console + * session that you might request using the returned credentials. The request to the + * federation endpoint for a console sign-in token takes a SessionDuration + * parameter that specifies the maximum length of the console session. For more + * information, see Creating a URL + * that Enables Federated Users to Access the AWS Management Console in the + * IAM User Guide.

+ *
+ */ + DurationSeconds?: number; +} + +type LowerCaseKey = { [K in keyof T as `${Uncapitalize}`]: T[K] }; +export interface FromWebTokenInit extends Omit, "roleSessionName"> { + /** + * The IAM session name used to distinguish sessions. + */ + roleSessionName?: string; + + /** + * A function that assumes a role with web identity and returns a promise fulfilled with + * credentials for the assumed role. + * + * @param params input parameter of sts:AssumeRoleWithWebIdentity API. + */ + roleAssumerWithWebIdentity?: (params: AssumeRoleWithWebIdentityParams) => Promise; +} + +export const fromWebToken = (init: FromWebTokenInit): CredentialProvider => () => { + const { + roleArn, + roleSessionName, + webIdentityToken, + providerId, + policyArns, + policy, + durationSeconds, + roleAssumerWithWebIdentity, + } = init; + + if (!roleAssumerWithWebIdentity) { + throw new ProviderError( + `Role Arn '${roleArn}' needs to be assumed with web identity,` + ` but no role assumption callback was provided.`, + false + ); + } + + return roleAssumerWithWebIdentity({ + RoleArn: roleArn, + RoleSessionName: roleSessionName ?? `aws-sdk-js-session-${Date.now()}`, + WebIdentityToken: webIdentityToken, + ProviderId: providerId, + PolicyArns: policyArns, + Policy: policy, + DurationSeconds: durationSeconds, + }); +}; diff --git a/packages/credential-provider-web-identity/src/index.ts b/packages/credential-provider-web-identity/src/index.ts index ddce9a6c4b4f..0e900c0a1334 100755 --- a/packages/credential-provider-web-identity/src/index.ts +++ b/packages/credential-provider-web-identity/src/index.ts @@ -1 +1,2 @@ export * from "./fromTokenFile"; +export * from "./fromWebToken";