@aws-amplify/auth
Version:
Auth category of aws-amplify
205 lines (202 loc) • 9.03 kB
JavaScript
import { ConsoleLogger, createGetCredentialsForIdentityClient } from '@aws-amplify/core';
import { assertIdentityPoolIdConfig } from '@aws-amplify/core/internals/utils';
import { AuthError } from '../../../errors/AuthError.mjs';
import { assertServiceError } from '../../../errors/utils/assertServiceError.mjs';
import { getRegionFromIdentityPoolId } from '../../../foundation/parsers/regionParsers.mjs';
import { assertIdTokenInAuthTokens } from '../utils/types.mjs';
import '@aws-amplify/core/internals/aws-client-utils';
import { createCognitoIdentityPoolEndpointResolver } from '../factories/createCognitoIdentityPoolEndpointResolver.mjs';
import { cognitoIdentityIdProvider } from './IdentityIdProvider.mjs';
import { formLoginsMap } from './utils.mjs';
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
const logger = new ConsoleLogger('CognitoCredentialsProvider');
const CREDENTIALS_TTL = 50 * 60 * 1000; // 50 min, can be modified on config if required in the future
class CognitoAWSCredentialsAndIdentityIdProvider {
constructor(identityIdStore) {
this._nextCredentialsRefresh = 0;
this._identityIdStore = identityIdStore;
}
async clearCredentialsAndIdentityId() {
logger.debug('Clearing out credentials and identityId');
this._credentialsAndIdentityId = undefined;
await this._identityIdStore.clearIdentityId();
}
async clearCredentials() {
logger.debug('Clearing out in-memory credentials');
this._credentialsAndIdentityId = undefined;
}
async getCredentialsAndIdentityId(getCredentialsOptions) {
const isAuthenticated = getCredentialsOptions.authenticated;
const { tokens } = getCredentialsOptions;
const { authConfig } = getCredentialsOptions;
try {
assertIdentityPoolIdConfig(authConfig?.Cognito);
}
catch {
// No identity pool configured, skipping
return;
}
if (!isAuthenticated && !authConfig.Cognito.allowGuestAccess) {
// TODO(V6): return partial result like Native platforms
return;
}
const { forceRefresh } = getCredentialsOptions;
const tokenHasChanged = this.hasTokenChanged(tokens);
const identityId = await cognitoIdentityIdProvider({
tokens,
authConfig: authConfig.Cognito,
identityIdStore: this._identityIdStore,
});
// Clear cached credentials when forceRefresh is true OR the cache token has changed
if (forceRefresh || tokenHasChanged) {
this.clearCredentials();
}
if (!isAuthenticated) {
return this.getGuestCredentials(identityId, authConfig.Cognito);
}
else {
assertIdTokenInAuthTokens(tokens);
return this.credsForOIDCTokens(authConfig.Cognito, tokens, identityId);
}
}
async getGuestCredentials(identityId, authConfig) {
// Return existing in-memory cached credentials only if it exists, is not past it's lifetime and is unauthenticated credentials
if (this._credentialsAndIdentityId &&
!this.isPastTTL() &&
this._credentialsAndIdentityId.isAuthenticatedCreds === false) {
logger.info('returning stored credentials as they neither past TTL nor expired.');
return this._credentialsAndIdentityId;
}
// Clear to discard if any authenticated credentials are set and start with a clean slate
this.clearCredentials();
const region = getRegionFromIdentityPoolId(authConfig.identityPoolId);
const getCredentialsForIdentity = createGetCredentialsForIdentityClient({
endpointResolver: createCognitoIdentityPoolEndpointResolver({
endpointOverride: authConfig.identityPoolEndpoint,
}),
});
// use identityId to obtain guest credentials
// save credentials in-memory
// No logins params should be passed for guest creds:
// https://docs.aws.amazon.com/cognitoidentity/latest/APIReference/API_GetCredentialsForIdentity.html
let clientResult;
try {
clientResult = await getCredentialsForIdentity({ region }, {
IdentityId: identityId,
});
}
catch (e) {
assertServiceError(e);
throw new AuthError(e);
}
if (clientResult?.Credentials?.AccessKeyId &&
clientResult?.Credentials?.SecretKey) {
this._nextCredentialsRefresh = new Date().getTime() + CREDENTIALS_TTL;
const res = {
credentials: {
accessKeyId: clientResult.Credentials.AccessKeyId,
secretAccessKey: clientResult.Credentials.SecretKey,
sessionToken: clientResult.Credentials.SessionToken,
expiration: clientResult.Credentials.Expiration,
},
identityId,
};
if (clientResult.IdentityId) {
res.identityId = clientResult.IdentityId;
this._identityIdStore.storeIdentityId({
id: clientResult.IdentityId,
type: 'guest',
});
}
this._credentialsAndIdentityId = {
...res,
isAuthenticatedCreds: false,
};
return res;
}
else {
throw new AuthError({
name: 'CredentialsNotFoundException',
message: `Cognito did not respond with either Credentials, AccessKeyId or SecretKey.`,
});
}
}
async credsForOIDCTokens(authConfig, authTokens, identityId) {
if (this._credentialsAndIdentityId &&
!this.isPastTTL() &&
this._credentialsAndIdentityId.isAuthenticatedCreds === true) {
logger.debug('returning stored credentials as they neither past TTL nor expired.');
return this._credentialsAndIdentityId;
}
// Clear to discard if any unauthenticated credentials are set and start with a clean slate
this.clearCredentials();
const logins = authTokens.idToken
? formLoginsMap(authTokens.idToken.toString())
: {};
const region = getRegionFromIdentityPoolId(authConfig.identityPoolId);
const getCredentialsForIdentity = createGetCredentialsForIdentityClient({
endpointResolver: createCognitoIdentityPoolEndpointResolver({
endpointOverride: authConfig.identityPoolEndpoint,
}),
});
let clientResult;
try {
clientResult = await getCredentialsForIdentity({ region }, {
IdentityId: identityId,
Logins: logins,
});
}
catch (e) {
assertServiceError(e);
throw new AuthError(e);
}
if (clientResult?.Credentials?.AccessKeyId &&
clientResult?.Credentials?.SecretKey) {
this._nextCredentialsRefresh = new Date().getTime() + CREDENTIALS_TTL;
const res = {
credentials: {
accessKeyId: clientResult.Credentials.AccessKeyId,
secretAccessKey: clientResult.Credentials.SecretKey,
sessionToken: clientResult.Credentials.SessionToken,
expiration: clientResult.Credentials.Expiration,
},
identityId,
};
if (clientResult.IdentityId) {
res.identityId = clientResult.IdentityId;
// note: the following call removes guest identityId from the persistent store (localStorage)
this._identityIdStore.storeIdentityId({
id: clientResult.IdentityId,
type: 'primary',
});
}
// Store the credentials in-memory along with the expiration
this._credentialsAndIdentityId = {
...res,
isAuthenticatedCreds: true,
associatedIdToken: authTokens.idToken?.toString(),
};
return res;
}
else {
throw new AuthError({
name: 'CredentialsException',
message: `Cognito did not respond with either Credentials, AccessKeyId or SecretKey.`,
});
}
}
isPastTTL() {
return this._nextCredentialsRefresh === undefined
? true
: this._nextCredentialsRefresh <= Date.now();
}
hasTokenChanged(tokens) {
return (!!tokens &&
!!this._credentialsAndIdentityId?.associatedIdToken &&
tokens.idToken?.toString() !==
this._credentialsAndIdentityId.associatedIdToken);
}
}
export { CognitoAWSCredentialsAndIdentityIdProvider };
//# sourceMappingURL=credentialsProvider.mjs.map