Skip to content

Commit

Permalink
feat(credential-providers): expose node.js default credential provide…
Browse files Browse the repository at this point in the history
…r chain
  • Loading branch information
AllanZhengYP committed May 4, 2022
1 parent 22870a0 commit c9f0f74
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 17 deletions.
2 changes: 1 addition & 1 deletion UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ Default credential provider is how SDK resolve the AWS credential if you DO NOT

In Browsers and ReactNative, the chain is empty, meaning you always need supply credentials explicitly.

- **v3**: [defaultProvider](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/modules/_aws_sdk_credential_provider_node.html#defaultprovider)
- **v3**: [defaultProvider](https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/modules/_aws_sdk_credential_providers#fromnodejsproviderchain-1)
The credential sources and fallback order _does not_ change in v3. It also supports [AWS Single Sign-On credentials](https://aws.amazon.com/single-sign-on/).

### Temporary Credentials
Expand Down
20 changes: 10 additions & 10 deletions packages/credential-provider-node/src/defaultProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { Credentials, MemoizedProvider } from "@aws-sdk/types";

import { remoteProvider } from "./remoteProvider";

export type DefaultProviderInit = FromIniInit & RemoteProviderInit & FromProcessInit & FromSSOInit & FromTokenFileInit;

/**
* Creates a credential provider that will attempt to find credentials from the
* following sources (listed in order of precedence):
Expand All @@ -29,24 +31,22 @@ import { remoteProvider } from "./remoteProvider";
* @param init Configuration that is passed to each individual
* provider
*
* @see fromEnv The function used to source credentials from
* @see {@link fromEnv} The function used to source credentials from
* environment variables
* @see fromSSO The function used to source credentials from
* @see {@link fromSSO} The function used to source credentials from
* resolved SSO token cache
* @see fromTokenFile The function used to source credentials from
* @see {@link fromTokenFile} The function used to source credentials from
* token file
* @see fromIni The function used to source credentials from INI
* @see {@link fromIni} The function used to source credentials from INI
* files
* @see fromProcess The function used to sources credentials from
* @see {@link fromProcess} The function used to sources credentials from
* credential_process in INI files
* @see fromInstanceMetadata The function used to source credentials from the
* @see {@link fromInstanceMetadata} The function used to source credentials from the
* EC2 Instance Metadata Service
* @see fromContainerMetadata The function used to source credentials from the
* @see {@link fromContainerMetadata} The function used to source credentials from the
* ECS Container Metadata Service
*/
export const defaultProvider = (
init: FromIniInit & RemoteProviderInit & FromProcessInit & FromSSOInit & FromTokenFileInit = {}
): MemoizedProvider<Credentials> =>
export const defaultProvider = (init: DefaultProviderInit = {}): MemoizedProvider<Credentials> =>
memoize(
chain(
...(init.profile || process.env[ENV_PROFILE] ? [] : [fromEnv()]),
Expand Down
47 changes: 41 additions & 6 deletions packages/credential-providers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ A collection of all credential providers, with default clients.
1. [Supported Configuration](#supported-configuration)
1. [SSO login with AWS CLI](#sso-login-with-the-aws-cli)
1. [Sample Files](#sample-files-2)
1. [From Node.js default credentials provider chain](#fromnodejsproviderchain)

## `fromCognitoIdentity()`

Expand Down Expand Up @@ -119,7 +120,7 @@ const client = new FooClient({
// Optional. The master credentials used to get and refresh temporary credentials from AWS STS.
// If skipped, it uses the default credential resolved by internal STS client.
masterCredentials: fromTemporaryCredentials({
params: { RoleArn: "arn:aws:iam::1234567890:role/RoleA" }
params: { RoleArn: "arn:aws:iam::1234567890:role/RoleA" },
}),
// Required. Options passed to STS AssumeRole operation.
params: {
Expand All @@ -129,16 +130,16 @@ const client = new FooClient({
// session name with prefix of 'aws-sdk-js-'.
RoleSessionName: "aws-sdk-js-123",
// Optional. The duration, in seconds, of the role session.
DurationSeconds: 3600
DurationSeconds: 3600,
// ... For more options see https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html
},
// Optional. Custom STS client configurations overriding the default ones.
clientConfig: { region },
// Optional. A function that returns a promise fulfilled with an MFA token code for the provided
// MFA Serial code. Required if `params` has `SerialNumber` config.
mfaCodeProvider: async mfaSerial => {
return "token"
}
mfaCodeProvider: async (mfaSerial) => {
return "token";
},
}),
});
```
Expand Down Expand Up @@ -593,7 +594,7 @@ Successfully signed out of all SSO profiles.
### Sample files
This credential provider is only applicable if the profile specified in shared configuration and
credentials files contain ALL of the following entries:
credentials files contain ALL of the following entries.
#### `~/.aws/credentials`
Expand All @@ -615,6 +616,40 @@ sso_role_name = SampleRole
sso_start_url = https://d-abc123.awsapps.com/start
```
## `fromNodeJsProviderChain()`
The credential provider used as default in the Node.js clients, but with default role assumers so
you don't need to import them from STS client and supply them manually. You normally don't need
to use this explicitly in the client constructor. It is useful for utility functions requiring
credentials like S3 presigner, or RDS signer.
This credential provider will attempt to find credentials from the following sources (listed in
order of precedence):
- [Environment variables exposed via `process.env`](#fromenv)
- [SSO credentials from token cache](#fromsso)
- [Web identity token credentials](#fromtokenfile)
- [Shared credentials and config ini files](#fromini)
- [The EC2/ECS Instance Metadata Service](#fromcontainermetadata-and-frominstancemetadata)
This credential provider will invoke one provider at a time and only
continue to the next if no credentials have been located. For example, if
the process finds values defined via the `AWS_ACCESS_KEY_ID` and
`AWS_SECRET_ACCESS_KEY` environment variables, the files at
`~/.aws/credentials` and `~/.aws/config` will not be read, nor will any
messages be sent to the Instance Metadata Service
```js
import { fromNodeJsProviderChain } from "@aws-sdk/credential-providers"; // ES6 import
// const { fromNodeJsProviderChain } = require "@aws-sdk/credential-providers" // CommonJS import
const credentialProvider = fromNodeJsProviderChain({
//...any input of fromEnv(), fromSSO(), fromTokenFile(), fromIni(),
// fromProcess(), fromInstanceMetadata(), fromContainerMetadata()
// Optional. Custom STS client configurations overriding the default ones.
clientConfig: { region },
});
```
[getcredentialsforidentity_api]: https://docs.aws.amazon.com/cognitoidentity/latest/APIReference/API_GetCredentialsForIdentity.html
[getid_api]: https://docs.aws.amazon.com/cognitoidentity/latest/APIReference/API_GetId.html
[assumerole_api]: https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html
Expand Down
1 change: 1 addition & 0 deletions packages/credential-providers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@aws-sdk/credential-provider-env": "*",
"@aws-sdk/credential-provider-imds": "*",
"@aws-sdk/credential-provider-ini": "*",
"@aws-sdk/credential-provider-node": "*",
"@aws-sdk/credential-provider-process": "*",
"@aws-sdk/credential-provider-sso": "*",
"@aws-sdk/credential-provider-web-identity": "*",
Expand Down
58 changes: 58 additions & 0 deletions packages/credential-providers/src/fromNodeJsProviderChain.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const ROLE_ASSUMER = "ROLE_ASSUMER";
const ROLE_ASSUMER_WITH_WEB_IDENTITY = "ROLE_ASSUMER_WITH_WEB_IDENTITY";

jest.mock("@aws-sdk/client-sts", () => ({
getDefaultRoleAssumer: jest.fn().mockReturnValue(ROLE_ASSUMER),
getDefaultRoleAssumerWithWebIdentity: jest.fn().mockReturnValue(ROLE_ASSUMER_WITH_WEB_IDENTITY),
}));

import { getDefaultRoleAssumer, getDefaultRoleAssumerWithWebIdentity } from "@aws-sdk/client-sts";
import { defaultProvider } from "@aws-sdk/credential-provider-node";

import { fromNodeJsProviderChain } from "./fromNodeJsProviderChain";

jest.mock("@aws-sdk/credential-provider-node", () => ({
defaultProvider: jest.fn(),
}));

describe(fromNodeJsProviderChain.name, () => {
beforeEach(() => {
jest.clearAllMocks();
});

it("should inject default role assumers", () => {
const profile = "profile";
fromNodeJsProviderChain({ profile });
expect(defaultProvider).toBeCalledWith({
profile,
roleAssumer: ROLE_ASSUMER,
roleAssumerWithWebIdentity: ROLE_ASSUMER_WITH_WEB_IDENTITY,
});
expect(getDefaultRoleAssumer).toBeCalled();
expect(getDefaultRoleAssumerWithWebIdentity).toBeCalled();
});

it("should use supplied role assumers", () => {
const profile = "profile";
const roleAssumer = jest.fn();
const roleAssumerWithWebIdentity = jest.fn();
fromNodeJsProviderChain({ profile, roleAssumer, roleAssumerWithWebIdentity });
expect(defaultProvider).toBeCalledWith({
profile,
roleAssumer,
roleAssumerWithWebIdentity,
});
expect(getDefaultRoleAssumer).not.toBeCalled();
expect(getDefaultRoleAssumerWithWebIdentity).not.toBeCalled();
});

it("should use supplied sts options", () => {
const profile = "profile";
const clientConfig = {
region: "US_BAR_1",
};
fromNodeJsProviderChain({ profile, clientConfig });
expect(getDefaultRoleAssumer).toBeCalledWith(clientConfig);
expect(getDefaultRoleAssumerWithWebIdentity).toBeCalledWith(clientConfig);
});
});
40 changes: 40 additions & 0 deletions packages/credential-providers/src/fromNodeJsProviderChain.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { getDefaultRoleAssumer, getDefaultRoleAssumerWithWebIdentity, STSClientConfig } from "@aws-sdk/client-sts";
import { defaultProvider, DefaultProviderInit } from "@aws-sdk/credential-provider-node";
import { CredentialProvider } from "@aws-sdk/types";

export interface FromNodeJsProviderChainInit extends DefaultProviderInit {
clientConfig?: STSClientConfig;
}

/**
* This is the same credential provider as {@link defaultProvider|the default provider for Node.js SDK},
* but with default role assumers so you don't need to import them from
* STS client and supply them manually.
*
* You normally don't need to use this explicitly in the client constructor.
* It is useful for utility functions requiring credentials like S3 presigner,
* or RDS signer.
*
* ```js
* import { fromNodeJsProviderChain } from "@aws-sdk/credential-providers"; // ES6 import
* // const { fromNodeJsProviderChain } = require "@aws-sdk/credential-providers" // CommonJS import
*
* const credentialProvider = fromNodeJsProviderChain({
* //...any input of fromEnv(), fromSSO(), fromTokenFile(), fromIni(),
* // fromProcess(), fromInstanceMetadata(), fromContainerMetadata()
*
* // Optional. Custom STS client configurations overriding the default ones.
* clientConfig: { region },
* })
* ```
*
* @param init
* @returns
*/
export const fromNodeJsProviderChain = (init: FromNodeJsProviderChainInit = {}): CredentialProvider =>
defaultProvider({
...init,
roleAssumer: init.roleAssumer ?? getDefaultRoleAssumer(init.clientConfig),
roleAssumerWithWebIdentity:
init.roleAssumerWithWebIdentity ?? getDefaultRoleAssumerWithWebIdentity(init.clientConfig),
});
1 change: 1 addition & 0 deletions packages/credential-providers/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export * from "./fromContainerMetadata";
export * from "./fromEnv";
export * from "./fromIni";
export * from "./fromInstanceMetadata";
export * from "./fromNodeJsProviderChain";
export * from "./fromProcess";
export * from "./fromSSO";
export * from "./fromTemporaryCredentials";
Expand Down

0 comments on commit c9f0f74

Please sign in to comment.