Skip to content

Commit 5e0a46a

Browse files
feat(credential-provider-node): throw cannot load credential error from credentail chain (#2408)
* feat(credential-provider-node): throw no credential found error in chain * fix(credential-provider-imds): continue privder chain when IMDS times out * feat(credentials): deprecate ProviderError and use CredentialsProviderError Co-authored-by: Trivikram Kamat <[email protected]>
1 parent 6d4e6b5 commit 5e0a46a

File tree

26 files changed

+213
-141
lines changed

26 files changed

+213
-141
lines changed

Diff for: packages/credential-provider-cognito-identity/src/fromCognitoIdentity.spec.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { GetCredentialsForIdentityCommand } from "@aws-sdk/client-cognito-identity";
2-
import { ProviderError } from "@aws-sdk/property-provider";
2+
import { CredentialsProviderError } from "@aws-sdk/property-provider";
33

44
import { fromCognitoIdentity } from "./fromCognitoIdentity";
55

@@ -76,7 +76,7 @@ describe("fromCognitoIdentity", () => {
7676
identityId,
7777
customRoleArn: "myArn",
7878
})()
79-
).rejects.toMatchObject(new ProviderError("Response from Amazon Cognito contained no credentials"));
79+
).rejects.toMatchObject(new CredentialsProviderError("Response from Amazon Cognito contained no credentials"));
8080
});
8181

8282
it("should convert a GetCredentialsForIdentity response without an access key ID to a provider error", async () => {
@@ -93,7 +93,7 @@ describe("fromCognitoIdentity", () => {
9393
identityId,
9494
customRoleArn: "myArn",
9595
})()
96-
).rejects.toMatchObject(new ProviderError("Response from Amazon Cognito contained no access key ID"));
96+
).rejects.toMatchObject(new CredentialsProviderError("Response from Amazon Cognito contained no access key ID"));
9797
});
9898

9999
it("should convert a GetCredentialsForIdentity response without a secret key to a provider error", async () => {
@@ -110,6 +110,6 @@ describe("fromCognitoIdentity", () => {
110110
identityId,
111111
customRoleArn: "myArn",
112112
})()
113-
).rejects.toMatchObject(new ProviderError("Response from Amazon Cognito contained no secret key"));
113+
).rejects.toMatchObject(new CredentialsProviderError("Response from Amazon Cognito contained no secret key"));
114114
});
115115
});

Diff for: packages/credential-provider-cognito-identity/src/fromCognitoIdentity.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { GetCredentialsForIdentityCommand } from "@aws-sdk/client-cognito-identity";
2-
import { ProviderError } from "@aws-sdk/property-provider";
2+
import { CredentialsProviderError } from "@aws-sdk/property-provider";
33
import { Credentials, Provider } from "@aws-sdk/types";
44

55
import { CognitoProviderParameters } from "./CognitoProviderParameters";
@@ -56,13 +56,13 @@ export interface FromCognitoIdentityParameters extends CognitoProviderParameters
5656
}
5757

5858
function throwOnMissingAccessKeyId(): never {
59-
throw new ProviderError("Response from Amazon Cognito contained no access key ID");
59+
throw new CredentialsProviderError("Response from Amazon Cognito contained no access key ID");
6060
}
6161

6262
function throwOnMissingCredentials(): never {
63-
throw new ProviderError("Response from Amazon Cognito contained no credentials");
63+
throw new CredentialsProviderError("Response from Amazon Cognito contained no credentials");
6464
}
6565

6666
function throwOnMissingSecretKey(): never {
67-
throw new ProviderError("Response from Amazon Cognito contained no secret key");
67+
throw new CredentialsProviderError("Response from Amazon Cognito contained no secret key");
6868
}

Diff for: packages/credential-provider-cognito-identity/src/fromCognitoIdentityPool.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { GetIdCommand } from "@aws-sdk/client-cognito-identity";
2-
import { ProviderError } from "@aws-sdk/property-provider";
2+
import { CredentialsProviderError } from "@aws-sdk/property-provider";
33

44
import { fromCognitoIdentityPool } from "./fromCognitoIdentityPool";
55

@@ -127,7 +127,7 @@ describe("fromCognitoIdentityPool", () => {
127127
client: mockClient,
128128
identityPoolId,
129129
})()
130-
).rejects.toMatchObject(new ProviderError("Response from Amazon Cognito contained no identity ID"));
130+
).rejects.toMatchObject(new CredentialsProviderError("Response from Amazon Cognito contained no identity ID"));
131131
});
132132

133133
it("should allow injecting a custom cache", async () => {

Diff for: packages/credential-provider-cognito-identity/src/fromCognitoIdentityPool.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { GetIdCommand } from "@aws-sdk/client-cognito-identity";
2-
import { ProviderError } from "@aws-sdk/property-provider";
2+
import { CredentialsProviderError } from "@aws-sdk/property-provider";
33

44
import { CognitoProviderParameters } from "./CognitoProviderParameters";
55
import { CognitoIdentityCredentialProvider, fromCognitoIdentity } from "./fromCognitoIdentity";
@@ -99,5 +99,5 @@ export interface FromCognitoIdentityPoolParameters extends CognitoProviderParame
9999
}
100100

101101
function throwOnMissingId(): never {
102-
throw new ProviderError("Response from Amazon Cognito contained no identity ID");
102+
throw new CredentialsProviderError("Response from Amazon Cognito contained no identity ID");
103103
}

Diff for: packages/credential-provider-env/src/index.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ProviderError } from "@aws-sdk/property-provider";
1+
import { CredentialsProviderError } from "@aws-sdk/property-provider";
22

33
import { ENV_EXPIRATION, ENV_KEY, ENV_SECRET, ENV_SESSION, fromEnv } from "./";
44

@@ -49,7 +49,7 @@ describe("fromEnv", () => {
4949

5050
it("should reject the promise if no environmental credentials can be found", async () => {
5151
await expect(fromEnv()()).rejects.toMatchObject(
52-
new ProviderError("Unable to find environment variable credentials.")
52+
new CredentialsProviderError("Unable to find environment variable credentials.")
5353
);
5454
});
5555

@@ -59,7 +59,7 @@ describe("fromEnv", () => {
5959
throw new Error("The promise should have been rejected.");
6060
},
6161
(err) => {
62-
expect((err as ProviderError).tryNextLink).toBe(true);
62+
expect((err as CredentialsProviderError).tryNextLink).toBe(true);
6363
}
6464
);
6565
});

Diff for: packages/credential-provider-env/src/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ProviderError } from "@aws-sdk/property-provider";
1+
import { CredentialsProviderError } from "@aws-sdk/property-provider";
22
import { CredentialProvider } from "@aws-sdk/types";
33

44
export const ENV_KEY = "AWS_ACCESS_KEY_ID";
@@ -25,6 +25,6 @@ export function fromEnv(): CredentialProvider {
2525
});
2626
}
2727

28-
return Promise.reject(new ProviderError("Unable to find environment variable credentials."));
28+
return Promise.reject(new CredentialsProviderError("Unable to find environment variable credentials."));
2929
};
3030
}

Diff for: packages/credential-provider-imds/src/fromContainerMetadata.ts

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ProviderError } from "@aws-sdk/property-provider";
1+
import { CredentialsProviderError } from "@aws-sdk/property-provider";
22
import { CredentialProvider } from "@aws-sdk/types";
33
import { RequestOptions } from "http";
44
import { parse } from "url";
@@ -23,7 +23,7 @@ export const fromContainerMetadata = (init: RemoteProviderInit = {}): Credential
2323
const requestOptions = await getCmdsUri();
2424
const credsResponse = JSON.parse(await requestFromEcsImds(timeout, requestOptions));
2525
if (!isImdsCredentials(credsResponse)) {
26-
throw new ProviderError("Invalid response received from instance metadata service.");
26+
throw new CredentialsProviderError("Invalid response received from instance metadata service.");
2727
}
2828
return fromImdsCredentials(credsResponse);
2929
}, maxRetries);
@@ -65,11 +65,17 @@ const getCmdsUri = async (): Promise<RequestOptions> => {
6565
if (process.env[ENV_CMDS_FULL_URI]) {
6666
const parsed = parse(process.env[ENV_CMDS_FULL_URI]!);
6767
if (!parsed.hostname || !(parsed.hostname in GREENGRASS_HOSTS)) {
68-
throw new ProviderError(`${parsed.hostname} is not a valid container metadata service hostname`, false);
68+
throw new CredentialsProviderError(
69+
`${parsed.hostname} is not a valid container metadata service hostname`,
70+
false
71+
);
6972
}
7073

7174
if (!parsed.protocol || !(parsed.protocol in GREENGRASS_PROTOCOLS)) {
72-
throw new ProviderError(`${parsed.protocol} is not a valid container metadata service protocol`, false);
75+
throw new CredentialsProviderError(
76+
`${parsed.protocol} is not a valid container metadata service protocol`,
77+
false
78+
);
7379
}
7480

7581
return {
@@ -78,7 +84,7 @@ const getCmdsUri = async (): Promise<RequestOptions> => {
7884
};
7985
}
8086

81-
throw new ProviderError(
87+
throw new CredentialsProviderError(
8288
"The container metadata credential provider cannot be used unless" +
8389
` the ${ENV_CMDS_RELATIVE_URI} or ${ENV_CMDS_FULL_URI} environment` +
8490
" variable is set",

Diff for: packages/credential-provider-imds/src/fromInstanceMetadata.spec.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ProviderError } from "@aws-sdk/property-provider";
1+
import { CredentialsProviderError } from "@aws-sdk/property-provider";
22

33
import { fromInstanceMetadata } from "./fromInstanceMetadata";
44
import { httpRequest } from "./remoteProvider/httpRequest";
@@ -124,7 +124,7 @@ describe("fromInstanceMetadata", () => {
124124
expect((retry as jest.Mock).mock.calls[1][1]).toBe(mockMaxRetries);
125125
});
126126

127-
it("throws ProviderError if credentials returned are incorrect", async () => {
127+
it("throws CredentialsProviderError if credentials returned are incorrect", async () => {
128128
(httpRequest as jest.Mock)
129129
.mockResolvedValueOnce(mockToken)
130130
.mockResolvedValueOnce(mockProfile)
@@ -134,7 +134,7 @@ describe("fromInstanceMetadata", () => {
134134
(isImdsCredentials as unknown as jest.Mock).mockReturnValueOnce(false);
135135

136136
await expect(fromInstanceMetadata()()).rejects.toEqual(
137-
new ProviderError("Invalid response received from instance metadata service.")
137+
new CredentialsProviderError("Invalid response received from instance metadata service.")
138138
);
139139
expect(retry).toHaveBeenCalledTimes(2);
140140
expect(httpRequest).toHaveBeenCalledTimes(3);

Diff for: packages/credential-provider-imds/src/fromInstanceMetadata.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ProviderError } from "@aws-sdk/property-provider";
1+
import { CredentialsProviderError } from "@aws-sdk/property-provider";
22
import { CredentialProvider, Credentials } from "@aws-sdk/types";
33
import { RequestOptions } from "http";
44

@@ -103,7 +103,7 @@ const getCredentialsFromProfile = async (profile: string, options: RequestOption
103103
);
104104

105105
if (!isImdsCredentials(credsResponse)) {
106-
throw new ProviderError("Invalid response received from instance metadata service.");
106+
throw new CredentialsProviderError("Invalid response received from instance metadata service.");
107107
}
108108

109109
return fromImdsCredentials(credsResponse);

Diff for: packages/credential-provider-imds/src/remoteProvider/httpRequest.spec.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ describe("httpRequest", () => {
8888
.delay(timeout * 2)
8989
.reply(200, "expectedResponse");
9090

91-
await expect(httpRequest({ host, path, port, timeout })).rejects.toStrictEqual(new Error("TimeoutError"));
91+
await expect(httpRequest({ host, path, port, timeout })).rejects.toStrictEqual(
92+
new ProviderError("TimeoutError from instance metadata service")
93+
);
9294

9395
scope.done();
9496
});

Diff for: packages/credential-provider-imds/src/remoteProvider/httpRequest.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export function httpRequest(options: RequestOptions): Promise<Buffer> {
1414
});
1515

1616
req.on("timeout", () => {
17-
reject(new Error("TimeoutError"));
17+
reject(new ProviderError("TimeoutError from instance metadata service"));
1818
});
1919

2020
req.on("response", (res: IncomingMessage) => {

Diff for: packages/credential-provider-ini/src/index.ts

+8-6
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { fromEnv } from "@aws-sdk/credential-provider-env";
22
import { fromContainerMetadata, fromInstanceMetadata } from "@aws-sdk/credential-provider-imds";
33
import { AssumeRoleWithWebIdentityParams, fromTokenFile } from "@aws-sdk/credential-provider-web-identity";
4-
import { ProviderError } from "@aws-sdk/property-provider";
4+
import { CredentialsProviderError } from "@aws-sdk/property-provider";
55
import {
66
loadSharedConfigFiles,
77
ParsedIniData,
@@ -203,14 +203,14 @@ const resolveProfileData = async (
203203
} = data;
204204

205205
if (!options.roleAssumer) {
206-
throw new ProviderError(
206+
throw new CredentialsProviderError(
207207
`Profile ${profileName} requires a role to be assumed, but no` + ` role assumption callback was provided.`,
208208
false
209209
);
210210
}
211211

212212
if (source_profile && source_profile in visitedProfiles) {
213-
throw new ProviderError(
213+
throw new CredentialsProviderError(
214214
`Detected a cycle attempting to resolve credentials for profile` +
215215
` ${getMasterProfileName(options)}. Profiles visited: ` +
216216
Object.keys(visitedProfiles).join(", "),
@@ -228,7 +228,7 @@ const resolveProfileData = async (
228228
const params: AssumeRoleParams = { RoleArn, RoleSessionName, ExternalId };
229229
if (mfa_serial) {
230230
if (!options.mfaCodeProvider) {
231-
throw new ProviderError(
231+
throw new CredentialsProviderError(
232232
`Profile ${profileName} requires multi-factor authentication,` + ` but no MFA code callback was provided.`,
233233
false
234234
);
@@ -257,7 +257,9 @@ const resolveProfileData = async (
257257
// terminal resolution error if a profile has been specified by the user
258258
// (whether via a parameter, an environment variable, or another profile's
259259
// `source_profile` key).
260-
throw new ProviderError(`Profile ${profileName} could not be found or parsed in shared` + ` credentials file.`);
260+
throw new CredentialsProviderError(
261+
`Profile ${profileName} could not be found or parsed in shared` + ` credentials file.`
262+
);
261263
};
262264

263265
/**
@@ -276,7 +278,7 @@ const resolveCredentialSource = (credentialSource: string, profileName: string):
276278
if (credentialSource in sourceProvidersMap) {
277279
return sourceProvidersMap[credentialSource]();
278280
} else {
279-
throw new ProviderError(
281+
throw new CredentialsProviderError(
280282
`Unsupported credential source in profile ${profileName}. Got ${credentialSource}, ` +
281283
`expected EcsContainer or Ec2InstanceMetadata or Environment.`
282284
);

0 commit comments

Comments
 (0)