Skip to content

Commit

Permalink
Merge branch 'next/release' into sign-in-authenticated-validation
Browse files Browse the repository at this point in the history
  • Loading branch information
israx authored Sep 14, 2023
2 parents b9f12e9 + c9b7174 commit fe68ddf
Show file tree
Hide file tree
Showing 8 changed files with 36 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { formLoginsMap } from './credentialsProvider';
import { AuthError } from '../../../errors/AuthError';
import { IdentityIdStore } from './types';
import { getRegionFromIdentityPoolId } from '../utils/clients/CognitoIdentityProvider/utils';
import { Identity } from '@aws-amplify/core';

const logger = new Logger('CognitoIdentityIdProvider');

Expand All @@ -18,9 +19,9 @@ const logger = new Logger('CognitoIdentityIdProvider');
*
* @param tokens - The AuthTokens received after SignIn
* @returns string
* @throws internal: {@link AuthError }
* @throws configuration excpetions: {@link InvalidIdentityPoolIdException }
* - Auth errors that may arise from misconfiguration.
*
* @throws service excpetions: {@link GetIdException }
*/
export async function cognitoIdentityIdProvider({
tokens,
Expand All @@ -32,10 +33,13 @@ export async function cognitoIdentityIdProvider({
identityIdStore: IdentityIdStore;
}): Promise<string> {
identityIdStore.setAuthConfig({ Cognito: authConfig });
let identityId = await identityIdStore.loadIdentityId();

// will return null only if there is no identityId cached or if there is an error retrieving it
let identityId: Identity | null = await identityIdStore.loadIdentityId();

// Tokens are available so return primary identityId
if (tokens) {
// Tokens are available so return primary identityId
// If there is existing primary identityId in-memory return that
if (identityId && identityId.type === 'primary') {
return identityId.id;
} else {
Expand All @@ -46,10 +50,8 @@ export async function cognitoIdentityIdProvider({
const generatedIdentityId = await generateIdentityId(logins, authConfig);

if (identityId && identityId.id === generatedIdentityId) {
// if guestIdentity is found and used by GetCredentialsForIdentity
// it will be linked to the logins provided, and disqualified as an unauth identity
logger.debug(
`The guest identity ${identityId.id} has become the primary identity`
`The guest identity ${identityId.id} has become the primary identity.`
);
}
identityId = {
Expand All @@ -58,7 +60,7 @@ export async function cognitoIdentityIdProvider({
};
}
} else {
// Tokens are avaliable so return guest identityId
// If there is existing guest identityId cached return that
if (identityId && identityId.type === 'guest') {
return identityId.id;
} else {
Expand All @@ -69,9 +71,8 @@ export async function cognitoIdentityIdProvider({
}
}

// Store in-memory or local storage
// Store in-memory or local storage depending on guest or primary identityId
identityIdStore.storeIdentityId(identityId);
logger.debug(`The identity being returned ${identityId.id}`);
return identityId.id;
}

Expand All @@ -80,7 +81,6 @@ async function generateIdentityId(
authConfig: CognitoIdentityPoolConfig
): Promise<string> {
const identityPoolId = authConfig?.identityPoolId;

const region = getRegionFromIdentityPoolId(identityPoolId);

// IdentityId is absent so get it using IdentityPoolId with Cognito's GetId API
Expand All @@ -100,8 +100,8 @@ async function generateIdentityId(
).IdentityId;
if (!idResult) {
throw new AuthError({
name: 'IdentityIdResponseException',
message: 'Did not receive an identityId from Cognito identity pool',
name: 'GetIdResponseException',
message: 'Received undefined response from getId operation',
recoverySuggestion:
'Make sure to pass a valid identityPoolId in the configuration.',
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
} from '@aws-amplify/core';
import { assertIdentityPooIdConfig } from '@aws-amplify/core/internals/utils';
import { IdentityIdStorageKeys, IdentityIdStore } from './types';
import { AuthError } from '../../../errors/AuthError';
import { getAuthStorageKeys } from '../tokenProvider/TokenStore';
import { AuthKeys } from '../tokenProvider/types';

Expand All @@ -33,18 +32,9 @@ export class DefaultIdentityIdStore implements IdentityIdStore {
this.keyValueStorage = keyValueStorage;
}

async loadIdentityId(): Promise<Identity | undefined> {
async loadIdentityId(): Promise<Identity | null> {
assertIdentityPooIdConfig(this.authConfig?.Cognito);
if (this.keyValueStorage === undefined) {
throw new AuthError({
message: 'No KeyValueStorage available',
name: 'KeyValueStorageNotFound',
recoverySuggestion:
'Make sure to set the keyValueStorage before using this method',
});
}
// TODO(v6): migration logic should be here
// Reading V5 tokens old format
try {
if (!!this._primaryIdentityId) {
return {
Expand All @@ -61,30 +51,16 @@ export class DefaultIdentityIdStore implements IdentityIdStore {
type: 'guest',
};
}
return null;
}
} catch (err) {
// TODO(v6): validate partial results with mobile implementation
throw new Error(`Error loading identityId from storage: ${err}`);
return null;
}
}

async storeIdentityId(identity: Identity): Promise<void> {
assertIdentityPooIdConfig(this.authConfig?.Cognito);
if (identity === undefined) {
throw new AuthError({
message: 'Invalid Identity parameter',
name: 'InvalidAuthIdentity',
recoverySuggestion: 'Make sure a valid Identity object is passed',
});
}
if (this.keyValueStorage === undefined) {
throw new AuthError({
message: 'No KeyValueStorage available',
name: 'KeyValueStorageNotFound',
recoverySuggestion:
'Make sure to set the keyValueStorage before using this method',
});
}

if (identity.type === 'guest') {
this.keyValueStorage.setItem(this._authKeys.identityId, identity.id);
Expand All @@ -99,9 +75,7 @@ export class DefaultIdentityIdStore implements IdentityIdStore {

async clearIdentityId(): Promise<void> {
this._primaryIdentityId = undefined;
await Promise.all([
this.keyValueStorage.removeItem(this._authKeys.identityId),
]);
await this.keyValueStorage.removeItem(this._authKeys.identityId);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import { assertIdTokenInAuthTokens } from '../utils/types';

const logger = new Logger('CognitoCredentialsProvider');
const CREDENTIALS_TTL = 50 * 60 * 1000; // 50 min, can be modified on config if required in the future

export class CognitoAWSCredentialsAndIdentityIdProvider
implements AWSCredentialsAndIdentityIdProvider
{
Expand Down Expand Up @@ -75,21 +74,13 @@ export class CognitoAWSCredentialsAndIdentityIdProvider
identityIdStore: this._identityIdStore,
});

if (!identityId) {
throw new AuthError({
name: 'IdentityIdConfigException',
message: 'No Cognito Identity Id provided',
recoverySuggestion: 'Make sure to pass a valid identityId.',
});
}

// 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 {
// Tokens will always be present if getCredentialsOptions.authenticated is true as dictated by the type
assertIdTokenInAuthTokens(tokens);
return this.credsForOIDCTokens(authConfig.Cognito, tokens, identityId);
}
Expand All @@ -99,6 +90,7 @@ export class CognitoAWSCredentialsAndIdentityIdProvider
identityId: string,
authConfig: CognitoIdentityPoolConfig
): Promise<AWSCredentialsAndIdentityId> {
// 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() &&
Expand All @@ -113,12 +105,12 @@ export class CognitoAWSCredentialsAndIdentityIdProvider
// Clear to discard if any authenticated credentials are set and start with a clean slate
this.clearCredentials();

const region = getRegionFromIdentityPoolId(authConfig.identityPoolId);

// 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
const region = getRegionFromIdentityPoolId(authConfig.identityPoolId);

const clientResult = await getCredentialsForIdentity(
{ region },
{
Expand Down Expand Up @@ -157,7 +149,7 @@ export class CognitoAWSCredentialsAndIdentityIdProvider
return res;
} else {
throw new AuthError({
name: 'CredentialsException',
name: 'CredentialsNotFoundException',
message: `Cognito did not respond with either Credentials, AccessKeyId or SecretKey.`,
});
}
Expand All @@ -166,15 +158,15 @@ export class CognitoAWSCredentialsAndIdentityIdProvider
private async credsForOIDCTokens(
authConfig: CognitoIdentityPoolConfig,
authTokens: AuthTokens,
identityId?: string
identityId: string
): Promise<AWSCredentialsAndIdentityId> {
if (
this._credentialsAndIdentityId &&
!this.isPastTTL() &&
this._credentialsAndIdentityId.isAuthenticatedCreds === true
) {
logger.debug(
'returning stored credentials as they neither past TTL nor expired'
'returning stored credentials as they neither past TTL nor expired.'
);
return this._credentialsAndIdentityId;
}
Expand Down Expand Up @@ -256,7 +248,7 @@ export function formLoginsMap(idToken: string) {
if (!issuer) {
throw new AuthError({
name: 'InvalidIdTokenException',
message: 'Invalid Idtoken',
message: 'Invalid Idtoken.',
});
}
let domainName: string = issuer.replace(/(^\w+:|^)\/\//, '');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ import { LocalStorage } from '@aws-amplify/core';
* Cognito specific implmentation of the CredentialsProvider interface
* that manages setting and getting of AWS Credentials.
*
* @throws internal: {@link AuthError }
* @throws configuration expections: {@link InvalidIdentityPoolIdException }
* - Auth errors that may arise from misconfiguration.
* @throws service expections: {@link GetCredentialsForIdentityException}, {@link GetIdException}
*
*/
export const cognitoCredentialsProvider =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export const IdentityIdStorageKeys = {

export interface IdentityIdStore {
setAuthConfig(authConfigParam: AuthConfig): void;
loadIdentityId(): Promise<Identity | undefined>;
loadIdentityId(): Promise<Identity | null>;
storeIdentityId(identity: Identity): Promise<void>;
clearIdentityId(): Promise<void>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function getRegion(userPoolId?: string): string {
export function getRegionFromIdentityPoolId(identityPoolId?: string): string {
if (!identityPoolId || !identityPoolId.includes(':')) {
throw new AuthError({
name: 'InvalidIdentityPoolId',
name: 'InvalidIdentityPoolIdException',
message: 'Invalid identity pool id provided.',
recoverySuggestion:
'Make sure a valid identityPoolId is given in the config.',
Expand Down
5 changes: 4 additions & 1 deletion packages/core/src/clients/types/aws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { MetadataBearer } from '@aws-sdk/types';
import { Endpoint } from './core';
import { HttpResponse } from './http';

export type { Credentials, MetadataBearer } from '@aws-sdk/types';
export type {
AwsCredentialIdentity as Credentials,
MetadataBearer,
} from '@aws-sdk/types';

export type SourceData = string | ArrayBuffer | ArrayBufferView;

Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/singleton/Auth/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ export function assertIdentityPooIdConfig(
): asserts cognitoConfig is CognitoIdentityPoolConfig {
const validConfig = !!cognitoConfig?.identityPoolId;
return asserts(validConfig, {
name: 'AuthIdentityPoolIdException',
message: 'Auth IdentityPoolId not configured',
name: 'InvalidIdentityPoolIdException',
message: 'Invalid identity pool id provided.',
recoverySuggestion:
'Make sure to call Amplify.configure in your app with a valid IdentityPoolId',
'Make sure a valid identityPoolId is given in the config.',
});
}

Expand Down

0 comments on commit fe68ddf

Please sign in to comment.