diff --git a/.circleci/config.yml b/.circleci/config.yml index 01ee0d62f46..fc92016f5fc 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -443,6 +443,13 @@ jobs: working_directory: ~/amplify-js-samples-staging/samples steps: - prepare_test_env + - integ_test_js: + test_name: 'React Credentials Different Region' + framework: react + category: auth + sample_name: credentials-auth + spec: credentials-auth + browser: << parameters.browser >> - integ_test_js: test_name: 'React Custom Authenticator' framework: react diff --git a/packages/auth/__tests__/auth-configure-test.ts b/packages/auth/__tests__/auth-configure-test.ts index 8eee6a2db7e..3a6e7f61cd3 100644 --- a/packages/auth/__tests__/auth-configure-test.ts +++ b/packages/auth/__tests__/auth-configure-test.ts @@ -1,4 +1,5 @@ import { AuthClass as Auth } from '../src/Auth'; +import { Credentials } from '@aws-amplify/core'; describe('configure test', () => { test('throw error when storage is empty', () => { @@ -18,4 +19,29 @@ describe('configure test', () => { expect(e).not.toBeNull(); } }); + + test('configure Credentials correctly when using different region', () => { + const opts = { + userPoolId: 'us-east-1_awdasd', + userPoolWebClientId: 'awsUserPoolsWebClientId', + region: 'us-east-1', + identityPoolId: 'awsCognitoIdentityPoolId', + identityPoolRegion: 'us-east-2', + }; + + const spyOn = jest.spyOn(Credentials, 'configure'); + + const auth = new Auth(null); + expect.assertions(1); + + auth.configure(opts); + expect(spyOn).toBeCalledWith( + expect.objectContaining({ + region: 'us-east-1', + identityPoolRegion: 'us-east-2', + identityPoolId: 'awsCognitoIdentityPoolId', + userPoolId: 'us-east-1_awdasd', + }) + ); + }); }); diff --git a/packages/auth/src/Auth.ts b/packages/auth/src/Auth.ts index ec62617978b..3a3654c76e8 100644 --- a/packages/auth/src/Auth.ts +++ b/packages/auth/src/Auth.ts @@ -196,11 +196,12 @@ export class AuthClass { this.Credentials.configure({ mandatorySignIn, - region: identityPoolRegion || region, + region, userPoolId, identityPoolId, refreshHandlers, storage: this._storage, + identityPoolRegion }); // initialize cognitoauth client if hosted ui options provided diff --git a/packages/core/__tests__/Credentials-test.ts b/packages/core/__tests__/Credentials-test.ts index bc3181f1caf..6e6d86c2d16 100644 --- a/packages/core/__tests__/Credentials-test.ts +++ b/packages/core/__tests__/Credentials-test.ts @@ -1,6 +1,14 @@ import { CredentialsClass as Credentials } from '../src/Credentials'; import { Amplify } from '../src/Amplify'; import { Hub } from '../src/Hub'; +import { + CognitoIdentityClient, + GetCredentialsForIdentityCommand, + GetIdCommand, +} from '@aws-sdk/client-cognito-identity'; +jest.mock('@aws-sdk/client-cognito-identity'); +import { fromCognitoIdentity } from '@aws-sdk/credential-provider-cognito-identity'; +jest.mock('@aws-sdk/credential-provider-cognito-identity'); const session = {}; const user = { @@ -93,6 +101,133 @@ describe('Credentials test', () => { }); }); + describe('different regions', () => { + const userPoolId = 'us-west-2:aaaaaaaaa'; + const identityPoolId = 'us-east-1:bbbbbbbb'; + const identityPoolRegion = 'us-east-1'; + const region = 'us-west-2'; + + beforeAll(() => { + CognitoIdentityClient.mockImplementation(params => { + return { + send: params => { + if (params instanceof GetIdCommand) { + return { IdentityId: '123' }; + } + if (params instanceof GetCredentialsForIdentityCommand) { + return { + Credentials: { + AccessKeyId: 'accessKey', + Expiration: 0, + SecretKey: 'secretKey', + SessionToken: 'sessionToken', + }, + IdentityId: '123', + }; + } + }, + }; + }); + + fromCognitoIdentity.mockImplementation(params => { + return async () => { + return {}; + }; + }); + }); + + test('should use identityPoolRegion param for credentials for federation', async () => { + expect.assertions(2); + + const credentials = new Credentials(null); + + credentials.configure({ + userPoolId, + identityPoolId, + identityPoolRegion, + region, + }); + + await credentials._setCredentialsFromFederation({ + provider: 'google', + token: 'token', + identity_id: '123', + }); + + expect(CognitoIdentityClient).toHaveBeenCalledWith( + expect.objectContaining({ region: identityPoolRegion }) + ); + + expect(fromCognitoIdentity).toBeCalledWith( + expect.objectContaining({ + identityId: '123', + logins: { + 'accounts.google.com': 'token', + }, + }) + ); + }); + + test('should use identityPoolRegion param for credentials from session', async () => { + expect.assertions(2); + + const credentials = new Credentials(null); + + credentials.configure({ + userPoolId, + identityPoolId, + identityPoolRegion, + region, + }); + + const session = { + getIdToken: () => { + return { + getJwtToken: () => { + return 'token'; + }, + }; + }, + }; + + await credentials._setCredentialsFromSession(session); + + expect(CognitoIdentityClient).toHaveBeenCalledWith( + expect.objectContaining({ region: identityPoolRegion }) + ); + + expect(GetIdCommand).toBeCalledWith({ + IdentityPoolId: identityPoolId, + Logins: { + [`cognito-idp.${region}.amazonaws.com/${userPoolId}`]: 'token', + }, + }); + }); + + test('should use identityPoolRegion param for credentials for guest', async () => { + expect.assertions(2); + + const credentials = new Credentials(null); + + credentials.configure({ + userPoolId, + identityPoolId, + identityPoolRegion, + region, + }); + + await credentials._setCredentialsForGuest(); + + expect(CognitoIdentityClient).toHaveBeenCalledWith( + expect.objectContaining({ region: identityPoolRegion }) + ); + + expect(GetIdCommand).toBeCalledWith({ + IdentityPoolId: identityPoolId, + }); + }); + }); + describe('getCredSource test', () => { test('happy case', () => { const credentials = new Credentials(null); diff --git a/packages/core/src/Credentials.ts b/packages/core/src/Credentials.ts index 154079c5554..c4b5dc018f7 100644 --- a/packages/core/src/Credentials.ts +++ b/packages/core/src/Credentials.ts @@ -265,7 +265,7 @@ export class CredentialsClass { parseAWSExports(this._config || {}).Auth ); } - const { identityPoolId, region, mandatorySignIn } = this._config; + const { identityPoolId, region, mandatorySignIn, identityPoolRegion } = this._config; if (mandatorySignIn) { return Promise.reject( @@ -282,7 +282,7 @@ export class CredentialsClass { ); } - if (!region) { + if (!identityPoolRegion && !region) { logger.debug('region is not configured for getting the credentials'); return Promise.reject( 'region is not configured for getting the credentials' @@ -292,7 +292,7 @@ export class CredentialsClass { const identityId = (this._identityId = await this._getGuestIdentityId()); const cognitoClient = new CognitoIdentityClient({ - region, + region: identityPoolRegion || region, customUserAgent: getAmplifyUserAgent(), }); @@ -396,12 +396,12 @@ export class CredentialsClass { const logins = {}; logins[domain] = token; - const { identityPoolId, region } = this._config; + const { identityPoolId, region, identityPoolRegion } = this._config; if (!identityPoolId) { logger.debug('No Cognito Federated Identity pool provided'); return Promise.reject('No Cognito Federated Identity pool provided'); } - if (!region) { + if (!identityPoolRegion && !region) { logger.debug('region is not configured for getting the credentials'); return Promise.reject( 'region is not configured for getting the credentials' @@ -409,7 +409,7 @@ export class CredentialsClass { } const cognitoClient = new CognitoIdentityClient({ - region, + region: identityPoolRegion || region, customUserAgent: getAmplifyUserAgent(), }); @@ -435,12 +435,12 @@ export class CredentialsClass { private _setCredentialsFromSession(session): Promise { logger.debug('set credentials from session'); const idToken = session.getIdToken().getJwtToken(); - const { region, userPoolId, identityPoolId } = this._config; + const { region, userPoolId, identityPoolId, identityPoolRegion } = this._config; if (!identityPoolId) { logger.debug('No Cognito Federated Identity pool provided'); return Promise.reject('No Cognito Federated Identity pool provided'); } - if (!region) { + if (!identityPoolRegion && !region) { logger.debug('region is not configured for getting the credentials'); return Promise.reject( 'region is not configured for getting the credentials' @@ -451,7 +451,7 @@ export class CredentialsClass { logins[key] = idToken; const cognitoClient = new CognitoIdentityClient({ - region, + region: identityPoolRegion || region, customUserAgent: getAmplifyUserAgent(), });