diff --git a/packages/amazon-cognito-identity-js/internals/index.d.ts b/packages/amazon-cognito-identity-js/internals/index.d.ts index 5bb20d17a63..713869332ae 100644 --- a/packages/amazon-cognito-identity-js/internals/index.d.ts +++ b/packages/amazon-cognito-identity-js/internals/index.d.ts @@ -22,29 +22,6 @@ import { export const addAuthCategoryToCognitoUserAgent: () => void; export const addFrameworkToCognitoUserAgent: (content: string) => void; -export class InternalCognitoUserPool { - constructor( - data: ICognitoUserPoolData, - wrapRefreshSessionCallback?: (target: NodeCallback.Any) => NodeCallback.Any - ); - - public getUserPoolId(): string; - public getUserPoolName(): string; - public getClientId(): string; - - public signUp( - username: string, - password: string, - userAttributes: CognitoUserAttribute[], - validationData: CognitoUserAttribute[], - callback: NodeCallback, - clientMetadata?: ClientMetadata, - userAgentValue?: string - ): void; - - public getCurrentUser(): CognitoUser | null; -} - export class InternalCognitoUser { constructor(data: ICognitoUserData); @@ -234,6 +211,7 @@ export class InternalCognitoUser { ): void; public deleteUser( callback: NodeCallback, + clientMetaData?: ClientMetadata, userAgentValue?: string ): void; public enableMFA( @@ -292,3 +270,26 @@ export class InternalCognitoUser { userAgentValue?: string ): void; } + +export class InternalCognitoUserPool { + constructor( + data: ICognitoUserPoolData, + wrapRefreshSessionCallback?: (target: NodeCallback.Any) => NodeCallback.Any + ); + + public getUserPoolId(): string; + public getUserPoolName(): string; + public getClientId(): string; + + public signUp( + username: string, + password: string, + userAttributes: CognitoUserAttribute[], + validationData: CognitoUserAttribute[], + callback: NodeCallback, + clientMetadata?: ClientMetadata, + userAgentValue?: string + ): void; + + public getCurrentUser(): CognitoUser | null; +} diff --git a/packages/amazon-cognito-identity-js/src/internals/InternalCognitoUser.js b/packages/amazon-cognito-identity-js/src/internals/InternalCognitoUser.js index 19f0e87b79e..f9cda9dd4e7 100644 --- a/packages/amazon-cognito-identity-js/src/internals/InternalCognitoUser.js +++ b/packages/amazon-cognito-identity-js/src/internals/InternalCognitoUser.js @@ -1378,7 +1378,7 @@ export class InternalCognitoUser { const userData = this.getUserDataFromCache(); if (!userData) { - this.fetchUserData() + this.fetchUserData(userAgentValue) .then(data => { callback(null, data); }) @@ -1387,7 +1387,7 @@ export class InternalCognitoUser { } if (this.isFetchUserDataAndTokenRequired(params)) { - this.fetchUserData() + this.fetchUserData(userAgentValue) .then(data => { return this.refreshSessionIfPossible(params, userAgentValue).then( () => data @@ -1436,10 +1436,10 @@ export class InternalCognitoUser { * @param {string} userAgentValue Optional string containing custom user agent value */ fetchUserData(userAgentValue) { - return this.createGetUserRequest().then(data => { + return this.createGetUserRequest(userAgentValue).then(data => { this.cacheUserData(data); return data; - }, userAgentValue); + }); } /** diff --git a/packages/amazon-cognito-identity-js/src/internals/InternalCognitoUserPool.js b/packages/amazon-cognito-identity-js/src/internals/InternalCognitoUserPool.js index af98946441e..e83b8ce273e 100644 --- a/packages/amazon-cognito-identity-js/src/internals/InternalCognitoUserPool.js +++ b/packages/amazon-cognito-identity-js/src/internals/InternalCognitoUserPool.js @@ -5,6 +5,7 @@ import Client from '../Client'; import CognitoUser from '../CognitoUser'; +import { InternalCognitoUser } from '../internals'; import StorageHelper from '../StorageHelper'; const USER_POOL_ID_MAX_LENGTH = 55; @@ -130,7 +131,7 @@ export class InternalCognitoUserPool { }; const returnData = { - user: new CognitoUser(cognitoUser), + user: new InternalCognitoUser(cognitoUser), userConfirmed: data.UserConfirmed, userSub: data.UserSub, codeDeliveryDetails: data.CodeDeliveryDetails, @@ -156,7 +157,7 @@ export class InternalCognitoUserPool { Storage: this.storage, }; - return new CognitoUser(cognitoUser); + return new InternalCognitoUser(cognitoUser); } return null; diff --git a/packages/analytics/package.json b/packages/analytics/package.json index 8c28eb8c95a..b7daac12c3f 100644 --- a/packages/analytics/package.json +++ b/packages/analytics/package.json @@ -66,13 +66,13 @@ "name": "Analytics (Pinpoint)", "path": "./lib-esm/index.js", "import": "{ Amplify, Analytics, AWSPinpointProvider }", - "limit": "31.5 kB" + "limit": "31.57 kB" }, { "name": "Analytics (Kinesis)", "path": "./lib-esm/index.js", "import": "{ Amplify, Analytics, AWSKinesisProvider }", - "limit": "60.5 kB" + "limit": "60.82 kB" } ], "jest": { diff --git a/packages/api-graphql/package.json b/packages/api-graphql/package.json index 80740d239da..606819a941a 100644 --- a/packages/api-graphql/package.json +++ b/packages/api-graphql/package.json @@ -65,7 +65,7 @@ "name": "API (GraphQL client)", "path": "./lib-esm/index.js", "import": "{ Amplify, GraphQLAPI }", - "limit": "89.52 kB" + "limit": "90.35 kB" } ], "jest": { diff --git a/packages/api/package.json b/packages/api/package.json index 2e29e1a7df6..c2ca564f347 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -67,7 +67,7 @@ "name": "API (top-level class)", "path": "./lib-esm/index.js", "import": "{ Amplify, API }", - "limit": "90.28 kB" + "limit": "91.12 kB" } ], "jest": { diff --git a/packages/auth/__tests__/auth-attribute-test.ts b/packages/auth/__tests__/auth-attribute-test.ts index 10963bcfd42..e91dad66e7f 100644 --- a/packages/auth/__tests__/auth-attribute-test.ts +++ b/packages/auth/__tests__/auth-attribute-test.ts @@ -8,6 +8,10 @@ import { CognitoAccessToken, CognitoUserAttribute, } from 'amazon-cognito-identity-js'; +import { + InternalCognitoUserPool, + InternalCognitoUser, +} from 'amazon-cognito-identity-js/internals'; import { AuthOptions } from '../src/types'; import { InternalAuthClass } from '../src/internals/InternalAuth'; @@ -23,7 +27,7 @@ const authOptions: AuthOptions = { describe('User-Attribute-validation', () => { it('Check-non-verified-attributes', async () => { const spyonAuthUserAttributes = jest - .spyOn(InternalAuthClass.prototype, 'userAttributes') + .spyOn(InternalAuthClass.prototype as any, '_userAttributes') .mockImplementation((user: CognitoUser) => { const emailAttribute = new CognitoUserAttribute({ Name: 'email', @@ -75,10 +79,10 @@ describe('User-Attribute-validation', () => { const auth = new Auth(authOptions); const spyUserPoolCurrentUser = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return new CognitoUser({ - Pool: new CognitoUserPool({ + Pool: new InternalCognitoUserPool({ UserPoolId: authOptions.userPoolId, ClientId: authOptions.userPoolWebClientId, }), @@ -87,7 +91,7 @@ describe('User-Attribute-validation', () => { }); const spyUserGetSession = jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementation((callback: any) => { const session = new CognitoUserSession({ AccessToken: new CognitoAccessToken({ AccessToken: 'accesstoken' }), @@ -103,7 +107,7 @@ describe('User-Attribute-validation', () => { }); const spyGetUserData = jest - .spyOn(CognitoUser.prototype, 'getUserData') + .spyOn(InternalCognitoUser.prototype, 'getUserData') .mockImplementation(callback => { const emailAttribute = { Name: 'email', diff --git a/packages/auth/__tests__/auth-creds-test.ts b/packages/auth/__tests__/auth-creds-test.ts index b6208d4f7f8..7de9e72a6bd 100644 --- a/packages/auth/__tests__/auth-creds-test.ts +++ b/packages/auth/__tests__/auth-creds-test.ts @@ -3,11 +3,15 @@ import { Credentials } from '@aws-amplify/core'; import { AuthOptions } from '../src/types'; import { CognitoUser, - CognitoUserPool, CognitoUserSession, CognitoAccessToken, CognitoIdToken, + CognitoUserPool, } from 'amazon-cognito-identity-js'; +import { + InternalCognitoUser, + InternalCognitoUserPool, +} from 'amazon-cognito-identity-js/internals'; const authOptions: AuthOptions = { userPoolId: 'us-west-2_0xxxxxxxx', userPoolWebClientId: 'awsUserPoolsWebClientId', @@ -21,7 +25,7 @@ describe('credentials syncing tests', () => { const auth = new Auth(authOptions); jest - .spyOn(CognitoUser.prototype, 'authenticateUser') + .spyOn(InternalCognitoUser.prototype, 'authenticateUser') .mockImplementation((authenticationDetails, callback) => { const session = new CognitoUserSession({ AccessToken: new CognitoAccessToken({ AccessToken: 'accesstoken' }), @@ -32,7 +36,7 @@ describe('credentials syncing tests', () => { }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return new CognitoUser({ Pool: new CognitoUserPool({ @@ -49,7 +53,7 @@ describe('credentials syncing tests', () => { }); jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementation((callback: any) => { callback(null, session); }); diff --git a/packages/auth/__tests__/auth-federation-unit-tests.ts b/packages/auth/__tests__/auth-federation-unit-tests.ts index 388a769cd3b..13b742a63fb 100644 --- a/packages/auth/__tests__/auth-federation-unit-tests.ts +++ b/packages/auth/__tests__/auth-federation-unit-tests.ts @@ -54,15 +54,15 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUserSession', () => { return CognitoUserSession; }); -jest.mock('amazon-cognito-identity-js/lib/CognitoUserPool', () => { - const CognitoUserPool = () => {}; +jest.mock('amazon-cognito-identity-js/internals', () => { + const InternalCognitoUserPool = () => {}; - CognitoUserPool.prototype.CognitoUserPool = options => { - CognitoUserPool.prototype.options = options; - return CognitoUserPool; + InternalCognitoUserPool.prototype.InternalCognitoUserPool = options => { + InternalCognitoUserPool.prototype.options = options; + return InternalCognitoUserPool; }; - CognitoUserPool.prototype.getCurrentUser = () => { + InternalCognitoUserPool.prototype.getCurrentUser = () => { return { username: 'username', getSession: callback => { @@ -78,7 +78,7 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUserPool', () => { }; }; - CognitoUserPool.prototype.signUp = ( + InternalCognitoUserPool.prototype.signUp = ( username, password, signUpAttributeList, @@ -89,49 +89,48 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUserPool', () => { callback(null, 'signUpResult'); }; - return CognitoUserPool; -}); - -jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { - const CognitoUser = () => {}; + const InternalCognitoUser = () => {}; - CognitoUser.prototype.CognitoUser = options => { - CognitoUser.prototype.options = options; - return CognitoUser; + InternalCognitoUser.prototype.InternalCognitoUser = options => { + InternalCognitoUser.prototype.options = options; + return InternalCognitoUser; }; - CognitoUser.prototype.getSession = callback => { + InternalCognitoUser.prototype.getSession = callback => { callback(null, 'session'); }; - CognitoUser.prototype.getUserAttributes = callback => { + InternalCognitoUser.prototype.getUserAttributes = callback => { callback(null, 'attributes'); }; - CognitoUser.prototype.getAttributeVerificationCode = (attr, callback) => { + InternalCognitoUser.prototype.getAttributeVerificationCode = ( + attr, + callback + ) => { callback.onSuccess('success'); }; - CognitoUser.prototype.verifyAttribute = (attr, code, callback) => { + InternalCognitoUser.prototype.verifyAttribute = (attr, code, callback) => { callback.onSuccess('success'); }; - CognitoUser.prototype.authenticateUser = ( + InternalCognitoUser.prototype.authenticateUser = ( authenticationDetails, callback ) => { callback.onSuccess('session'); }; - CognitoUser.prototype.sendMFACode = (code, callback) => { + InternalCognitoUser.prototype.sendMFACode = (code, callback) => { callback.onSuccess('session'); }; - CognitoUser.prototype.resendConfirmationCode = callback => { + InternalCognitoUser.prototype.resendConfirmationCode = callback => { callback(null, 'result'); }; - CognitoUser.prototype.changePassword = ( + InternalCognitoUser.prototype.changePassword = ( oldPassword, newPassword, callback @@ -139,21 +138,25 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { callback(null, 'SUCCESS'); }; - CognitoUser.prototype.forgotPassword = callback => { + InternalCognitoUser.prototype.forgotPassword = callback => { callback.onSuccess(); }; - CognitoUser.prototype.confirmPassword = (code, password, callback) => { + InternalCognitoUser.prototype.confirmPassword = ( + code, + password, + callback + ) => { callback.onSuccess(); }; - CognitoUser.prototype.signOut = () => {}; + InternalCognitoUser.prototype.signOut = () => {}; - CognitoUser.prototype.globalSignOut = callback => { + InternalCognitoUser.prototype.globalSignOut = callback => { callback.onSuccess(); }; - CognitoUser.prototype.confirmRegistration = ( + InternalCognitoUser.prototype.confirmRegistration = ( confirmationCode, forceAliasCreation, callback @@ -161,7 +164,7 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { callback(null, 'Success'); }; - CognitoUser.prototype.completeNewPasswordChallenge = ( + InternalCognitoUser.prototype.completeNewPasswordChallenge = ( password, requiredAttributes, callback @@ -169,36 +172,46 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { callback.onSuccess('session'); }; - CognitoUser.prototype.updateAttributes = (attributeList, callback) => { + InternalCognitoUser.prototype.updateAttributes = ( + attributeList, + callback + ) => { callback(null, 'SUCCESS'); }; - CognitoUser.prototype.setAuthenticationFlowType = type => {}; + InternalCognitoUser.prototype.setAuthenticationFlowType = type => {}; - CognitoUser.prototype.initiateAuth = (authenticationDetails, callback) => { + InternalCognitoUser.prototype.initiateAuth = ( + authenticationDetails, + callback + ) => { callback.customChallenge('challengeParam'); }; - CognitoUser.prototype.sendCustomChallengeAnswer = ( + InternalCognitoUser.prototype.sendCustomChallengeAnswer = ( challengeAnswer, callback ) => { callback.onSuccess('session'); }; - CognitoUser.prototype.refreshSession = (refreshToken, callback) => { + InternalCognitoUser.prototype.refreshSession = (refreshToken, callback) => { callback(null, 'session'); }; - CognitoUser.prototype.getUsername = () => { + InternalCognitoUser.prototype.getUsername = () => { return 'username'; }; - CognitoUser.prototype.getUserData = callback => { + InternalCognitoUser.prototype.getUserData = callback => { callback(null, 'data'); }; - return CognitoUser; + return { + ...jest.requireActual('amazon-cognito-identity-js/internals'), + InternalCognitoUser, + InternalCognitoUserPool, + }; }); function mockGAPI({ reloadAuthResponse }: { reloadAuthResponse: Function }) { diff --git a/packages/auth/__tests__/auth-refresh-token-test.ts b/packages/auth/__tests__/auth-refresh-token-test.ts index 3aeedd9a437..8ecc5861f9c 100644 --- a/packages/auth/__tests__/auth-refresh-token-test.ts +++ b/packages/auth/__tests__/auth-refresh-token-test.ts @@ -1,14 +1,17 @@ import { AuthClass as Auth } from '../src/Auth'; import { - CognitoUserPool, CognitoUser, CognitoUserSession, CognitoIdToken, CognitoAccessToken, - CognitoUserAttribute, } from 'amazon-cognito-identity-js'; +import { + InternalCognitoUser, + InternalCognitoUserPool, +} from 'amazon-cognito-identity-js/internals'; + import { AuthOptions } from '../src/types'; const clientMetadata = { @@ -32,7 +35,7 @@ describe('Refresh token', () => { expect.assertions(1); const getSessionSpy = jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementation( // @ts-ignore ( @@ -51,10 +54,10 @@ describe('Refresh token', () => { ); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return new CognitoUser({ - Pool: new CognitoUserPool({ + Pool: new InternalCognitoUserPool({ ClientId: authOptions.userPoolWebClientId, UserPoolId: authOptions.userPoolId, }), @@ -71,7 +74,7 @@ describe('Refresh token', () => { expect.assertions(2); const getSessionSpy = jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementation( // @ts-ignore ( @@ -90,10 +93,10 @@ describe('Refresh token', () => { ); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return new CognitoUser({ - Pool: new CognitoUserPool({ + Pool: new InternalCognitoUserPool({ ClientId: authOptions.userPoolWebClientId, UserPoolId: authOptions.userPoolId, }), @@ -111,7 +114,7 @@ describe('Refresh token', () => { expect.assertions(2); - jest.spyOn(CognitoUser.prototype, 'getSession').mockImplementation( + jest.spyOn(InternalCognitoUser.prototype, 'getSession').mockImplementation( // @ts-ignore ( callback: (error: Error, session: CognitoUserSession) => void, @@ -129,16 +132,16 @@ describe('Refresh token', () => { ); jest - .spyOn(CognitoUser.prototype, 'globalSignOut') + .spyOn(InternalCognitoUser.prototype, 'globalSignOut') .mockImplementation(({ onSuccess, onFailure }) => { onSuccess(''); }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return new CognitoUser({ - Pool: new CognitoUserPool({ + Pool: new InternalCognitoUserPool({ ClientId: authOptions.userPoolWebClientId, UserPoolId: authOptions.userPoolId, }), @@ -157,7 +160,7 @@ describe('Refresh token', () => { expect.assertions(1); - jest.spyOn(CognitoUser.prototype, 'getSession').mockImplementation( + jest.spyOn(InternalCognitoUser.prototype, 'getSession').mockImplementation( // @ts-ignore ( callback: (error: Error, session: CognitoUserSession) => void, @@ -175,7 +178,7 @@ describe('Refresh token', () => { ); jest - .spyOn(CognitoUser.prototype, 'getUserData') + .spyOn(InternalCognitoUser.prototype, 'getUserData') .mockImplementation((callback, params) => { expect(params.clientMetadata).toEqual(clientMetadata); @@ -189,10 +192,10 @@ describe('Refresh token', () => { }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return new CognitoUser({ - Pool: new CognitoUserPool({ + Pool: new InternalCognitoUserPool({ ClientId: authOptions.userPoolWebClientId, UserPoolId: authOptions.userPoolId, }), @@ -209,7 +212,7 @@ describe('Refresh token', () => { expect.assertions(2); - jest.spyOn(CognitoUser.prototype, 'getSession').mockImplementation( + jest.spyOn(InternalCognitoUser.prototype, 'getSession').mockImplementation( // @ts-ignore ( callback: (error: Error, session: CognitoUserSession) => void, @@ -227,7 +230,7 @@ describe('Refresh token', () => { ); jest - .spyOn(CognitoUser.prototype, 'getUserData') + .spyOn(InternalCognitoUser.prototype, 'getUserData') .mockImplementation((callback, params) => { expect(params.clientMetadata).toEqual(clientMetadata); @@ -241,10 +244,10 @@ describe('Refresh token', () => { }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return new CognitoUser({ - Pool: new CognitoUserPool({ + Pool: new InternalCognitoUserPool({ ClientId: authOptions.userPoolWebClientId, UserPoolId: authOptions.userPoolId, }), @@ -263,7 +266,7 @@ describe('Refresh token', () => { expect.assertions(3); - jest.spyOn(CognitoUser.prototype, 'getSession').mockImplementation( + jest.spyOn(InternalCognitoUser.prototype, 'getSession').mockImplementation( // @ts-ignore ( callback: (error: Error, session: CognitoUserSession) => void, @@ -281,7 +284,7 @@ describe('Refresh token', () => { ); jest - .spyOn(CognitoUser.prototype, 'getUserData') + .spyOn(InternalCognitoUser.prototype, 'getUserData') .mockImplementation((callback, params) => { expect(params.clientMetadata).toEqual(clientMetadata); @@ -295,10 +298,10 @@ describe('Refresh token', () => { }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return new CognitoUser({ - Pool: new CognitoUserPool({ + Pool: new InternalCognitoUserPool({ ClientId: authOptions.userPoolWebClientId, UserPoolId: authOptions.userPoolId, }), @@ -307,7 +310,7 @@ describe('Refresh token', () => { }); jest - .spyOn(CognitoUser.prototype, 'setUserMfaPreference') + .spyOn(InternalCognitoUser.prototype, 'setUserMfaPreference') .mockImplementation( (smsMfaSettings, softwareTokenMfaSettings, callback) => { callback(); diff --git a/packages/auth/__tests__/auth-unit-test.ts b/packages/auth/__tests__/auth-unit-test.ts index eaf6b022370..26993310391 100644 --- a/packages/auth/__tests__/auth-unit-test.ts +++ b/packages/auth/__tests__/auth-unit-test.ts @@ -2,14 +2,16 @@ import OAuth from '../src/OAuth/OAuth'; import * as oauthStorage from '../src/OAuth/oauthStorage'; import { CookieStorage, - CognitoUserPool, - CognitoUser, CognitoUserSession, CognitoIdToken, CognitoAccessToken, NodeCallback, ISignUpResult, } from 'amazon-cognito-identity-js'; +import { + InternalCognitoUser, + InternalCognitoUserPool, +} from 'amazon-cognito-identity-js/internals'; const MAX_DEVICES: number = 60; @@ -69,86 +71,50 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUserSession', () => { return CognitoUserSession; }); -jest.mock('amazon-cognito-identity-js/lib/CognitoUserPool', () => { - const CognitoUserPool = () => {}; - - CognitoUserPool.prototype.CognitoUserPool = options => { - CognitoUserPool.prototype.options = options; - return CognitoUserPool; - }; - - CognitoUserPool.prototype.getCurrentUser = () => { - return { - username: 'username', - attributes: { email: 'test@test.com' }, - getSession: callback => { - // throw 3; - callback(null, { - getAccessToken: () => { - return { - decodePayload: () => 'payload', - getJwtToken: () => 'jwt', - }; - }, - }); - }, - }; - }; - - CognitoUserPool.prototype.signUp = ( - username, - password, - signUpAttributeList, - validationData, - callback, - clientMetadata - ) => { - callback(null, 'signUpResult'); - }; - - return CognitoUserPool; -}); - -jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { - const CognitoUser = function() { +jest.mock('amazon-cognito-identity-js/internals', () => { + // prettier-ignore + const InternalCognitoUser = function() { // mock private member this.signInUserSession = null; }; - CognitoUser.prototype.CognitoUser = options => { - CognitoUser.prototype.options = options; - return CognitoUser; + InternalCognitoUser.prototype.InternalCognitoUser = options => { + InternalCognitoUser.prototype.options = options; + return InternalCognitoUser; }; - CognitoUser.prototype.getSession = callback => { + InternalCognitoUser.prototype.getSession = callback => { // throw 3; callback(null, 'session'); }; - CognitoUser.prototype.getUserAttributes = callback => { + InternalCognitoUser.prototype.getUserAttributes = callback => { callback(null, 'attributes'); }; - CognitoUser.prototype.getAttributeVerificationCode = (attr, callback) => { + InternalCognitoUser.prototype.getAttributeVerificationCode = ( + attr, + callback + ) => { callback.onSuccess('success'); }; - CognitoUser.prototype.verifyAttribute = (attr, code, callback) => { + InternalCognitoUser.prototype.verifyAttribute = (attr, code, callback) => { callback.onSuccess('success'); }; - CognitoUser.prototype.authenticateUser = ( + InternalCognitoUser.prototype.authenticateUser = ( authenticationDetails, callback ) => { callback.onSuccess('session'); }; - CognitoUser.prototype.sendMFACode = (code, callback) => { + InternalCognitoUser.prototype.sendMFACode = (code, callback) => { callback.onSuccess('session'); }; - CognitoUser.prototype.resendConfirmationCode = callback => { + InternalCognitoUser.prototype.resendConfirmationCode = callback => { callback(null, { CodeDeliveryDetails: { AttributeName: 'email', @@ -158,7 +124,7 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { }); }; - CognitoUser.prototype.changePassword = ( + InternalCognitoUser.prototype.changePassword = ( oldPassword, newPassword, callback @@ -166,25 +132,29 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { callback(null, 'SUCCESS'); }; - CognitoUser.prototype.forgotPassword = callback => { + InternalCognitoUser.prototype.forgotPassword = callback => { callback.onSuccess(); }; - CognitoUser.prototype.confirmPassword = (code, password, callback) => { + InternalCognitoUser.prototype.confirmPassword = ( + code, + password, + callback + ) => { callback.onSuccess(); }; - CognitoUser.prototype.signOut = callback => { + InternalCognitoUser.prototype.signOut = callback => { if (callback && typeof callback === 'function') { callback(); } }; - CognitoUser.prototype.globalSignOut = callback => { + InternalCognitoUser.prototype.globalSignOut = callback => { callback.onSuccess(); }; - CognitoUser.prototype.confirmRegistration = ( + InternalCognitoUser.prototype.confirmRegistration = ( confirmationCode, forceAliasCreation, callback @@ -192,7 +162,7 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { callback(null, 'Success'); }; - CognitoUser.prototype.completeNewPasswordChallenge = ( + InternalCognitoUser.prototype.completeNewPasswordChallenge = ( password, requiredAttributes, callback @@ -200,42 +170,51 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { callback.onSuccess('session'); }; - CognitoUser.prototype.updateAttributes = (attributeList, callback) => { + InternalCognitoUser.prototype.updateAttributes = ( + attributeList, + callback + ) => { callback(null, 'SUCCESS'); }; - CognitoUser.prototype.deleteAttributes = (attributeList, callback) => { + InternalCognitoUser.prototype.deleteAttributes = ( + attributeList, + callback + ) => { callback(null, 'SUCCESS'); }; - CognitoUser.prototype.deleteUser = (callback, {}) => { + InternalCognitoUser.prototype.deleteUser = (callback, {}) => { callback(null, 'SUCCESS'); }; - CognitoUser.prototype.setAuthenticationFlowType = type => {}; + InternalCognitoUser.prototype.setAuthenticationFlowType = type => {}; - CognitoUser.prototype.initiateAuth = (authenticationDetails, callback) => { + InternalCognitoUser.prototype.initiateAuth = ( + authenticationDetails, + callback + ) => { callback.customChallenge('challengeParam'); }; - CognitoUser.prototype.sendCustomChallengeAnswer = ( + InternalCognitoUser.prototype.sendCustomChallengeAnswer = ( challengeAnswer, callback ) => { callback.onSuccess('session'); }; - CognitoUser.prototype.refreshSession = (refreshToken, callback) => { + InternalCognitoUser.prototype.refreshSession = (refreshToken, callback) => { callback(null, 'session'); }; - CognitoUser.prototype.getUsername = () => { + InternalCognitoUser.prototype.getUsername = () => { return 'username'; }; - CognitoUser.prototype.getUserData = callback => { + InternalCognitoUser.prototype.getUserData = callback => { callback(null, 'data'); }; - CognitoUser.prototype.setUserMfaPreference = ( + InternalCognitoUser.prototype.setUserMfaPreference = ( smsMfaSettings, softwareTokenMfaSettings, callback @@ -243,23 +222,69 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { callback(null, 'success'); }; - CognitoUser.prototype.getCachedDeviceKeyAndPassword = () => { + InternalCognitoUser.prototype.getCachedDeviceKeyAndPassword = () => { return 'success'; }; - CognitoUser.prototype.setDeviceStatusRemembered = callback => { + InternalCognitoUser.prototype.setDeviceStatusRemembered = callback => { callback.onSuccess('success'); }; - CognitoUser.prototype.forgetDevice = callback => { + InternalCognitoUser.prototype.forgetDevice = callback => { callback.onSuccess('success'); }; - CognitoUser.prototype.listDevices = (limit, paginationToken, callback) => { + InternalCognitoUser.prototype.listDevices = ( + limit, + paginationToken, + callback + ) => { callback.onSuccess('success'); }; - CognitoUser.prototype.getSignInUserSession = function() { + // prettier-ignore + InternalCognitoUser.prototype.getSignInUserSession = function() { return this.signInUserSession; }; - return CognitoUser; + const InternalCognitoUserPool = () => {}; + + InternalCognitoUserPool.prototype.InternalCognitoUserPool = options => { + InternalCognitoUserPool.prototype.options = options; + return InternalCognitoUserPool; + }; + + InternalCognitoUserPool.prototype.getCurrentUser = () => { + return { + username: 'username', + attributes: { email: 'test@test.com' }, + getSession: callback => { + // throw 3; + callback(null, { + getAccessToken: () => { + return { + decodePayload: () => 'payload', + getJwtToken: () => 'jwt', + }; + }, + }); + }, + }; + }; + + InternalCognitoUserPool.prototype.signUp = ( + username, + password, + signUpAttributeList, + validationData, + callback, + clientMetadata, + customUserAgentDetails? + ) => { + callback(null, 'signUpResult'); + }; + + return { + ...jest.requireActual('amazon-cognito-identity-js/internals'), + InternalCognitoUser, + InternalCognitoUserPool, + }; }); const createMockLocalStorage = () => @@ -281,11 +306,12 @@ const createMockLocalStorage = () => import { AuthOptions, SignUpParams, AwsCognitoOAuthOpts } from '../src/types'; import { AuthClass as Auth } from '../src/Auth'; -import { Credentials, StorageHelper, Hub } from '@aws-amplify/core'; +import { InternalAuthClass } from '../src/internals/InternalAuth'; +import { AuthAction, Credentials, StorageHelper, Hub } from '@aws-amplify/core'; import { AuthError, NoUserPoolError } from '../src/Errors'; import { AuthErrorTypes } from '../src/types/Auth'; import { mockDeviceArray, transformedMockData } from './mockData'; -import { InternalAuthClass } from '../src/internals/InternalAuth'; +import { getAuthUserAgentDetails, getAuthUserAgentValue } from '../src/utils'; const authOptions: AuthOptions = { userPoolId: 'awsUserPoolsId', @@ -342,7 +368,7 @@ const authOptionsWithNoUserPoolId: AuthOptions = { mandatorySignIn: false, }; -const userPool = new CognitoUserPool({ +const userPool = new InternalCognitoUserPool({ UserPoolId: authOptions.userPoolId, ClientId: authOptions.userPoolWebClientId, }); @@ -378,7 +404,7 @@ const USER_ADMIN_SCOPE = 'aws.cognito.signin.user.admin'; describe('auth unit test', () => { describe('signUp', () => { test('happy case with object attr', async () => { - const spyon = jest.spyOn(CognitoUserPool.prototype, 'signUp'); + const spyon = jest.spyOn(InternalCognitoUserPool.prototype, 'signUp'); const auth = new Auth(authOptions); const attrs = { @@ -396,7 +422,7 @@ describe('auth unit test', () => { }); test('happy case clientMetadata default', async () => { - const spyon = jest.spyOn(CognitoUserPool.prototype, 'signUp'); + const spyon = jest.spyOn(InternalCognitoUserPool.prototype, 'signUp'); const auth = new Auth(authOptionsWithClientMetadata); const attrs = { @@ -410,7 +436,7 @@ describe('auth unit test', () => { }; await auth.signUp(attrs); - expect(await CognitoUserPool.prototype.signUp).toBeCalledWith( + expect(await InternalCognitoUserPool.prototype.signUp).toBeCalledWith( attrs.username, attrs.password, [ @@ -420,13 +446,14 @@ describe('auth unit test', () => { ], null, jasmine.any(Function), - { foo: 'bar' } + { foo: 'bar' }, + getAuthUserAgentValue(AuthAction.SignUp) ); spyon.mockClear(); }); test('happy case clientMetadata parameter', async () => { - const spyon = jest.spyOn(CognitoUserPool.prototype, 'signUp'); + const spyon = jest.spyOn(InternalCognitoUserPool.prototype, 'signUp'); const auth = new Auth(authOptionsWithClientMetadata); const attrs = { @@ -443,7 +470,7 @@ describe('auth unit test', () => { }; await auth.signUp(attrs); - expect(await CognitoUserPool.prototype.signUp).toBeCalledWith( + expect(await InternalCognitoUserPool.prototype.signUp).toBeCalledWith( attrs.username, attrs.password, [ @@ -453,7 +480,8 @@ describe('auth unit test', () => { ], null, jasmine.any(Function), - { custom: 'value' } + { custom: 'value' }, + getAuthUserAgentValue(AuthAction.SignUp) ); spyon.mockClear(); }); @@ -476,7 +504,7 @@ describe('auth unit test', () => { test('callback error', async () => { const spyon = jest - .spyOn(CognitoUserPool.prototype, 'signUp') + .spyOn(InternalCognitoUserPool.prototype, 'signUp') .mockImplementationOnce( ( username, @@ -572,7 +600,7 @@ describe('auth unit test', () => { describe('autoSignInAfterSignUp', () => { test('happy case auto confirm', async () => { const spyon = jest - .spyOn(CognitoUserPool.prototype, 'signUp') + .spyOn(InternalCognitoUserPool.prototype, 'signUp') .mockImplementationOnce( ( username, @@ -585,7 +613,10 @@ describe('auth unit test', () => { callback(null, signUpResult); } ); - const signInSpyon = jest.spyOn(CognitoUser.prototype, 'authenticateUser'); + const signInSpyon = jest.spyOn( + InternalCognitoUser.prototype, + 'authenticateUser' + ); const auth = new Auth(authOptions); const attrs = { username: 'username', @@ -604,12 +635,15 @@ describe('auth unit test', () => { }); test('happy case confirmation code', async () => { - const spyon = jest.spyOn(CognitoUserPool.prototype, 'signUp'); + const spyon = jest.spyOn(InternalCognitoUserPool.prototype, 'signUp'); const confirmSpyon = jest.spyOn( - CognitoUser.prototype, + InternalCognitoUser.prototype, 'confirmRegistration' ); - const signInSpyon = jest.spyOn(CognitoUser.prototype, 'authenticateUser'); + const signInSpyon = jest.spyOn( + InternalCognitoUser.prototype, + 'authenticateUser' + ); const auth = new Auth(authOptions); const attrs = { username: 'username', @@ -631,8 +665,11 @@ describe('auth unit test', () => { test('happy case confirmation link', async () => { jest.useFakeTimers(); - const spyon = jest.spyOn(CognitoUserPool.prototype, 'signUp'); - const signInSpyon = jest.spyOn(CognitoUser.prototype, 'authenticateUser'); + const spyon = jest.spyOn(InternalCognitoUserPool.prototype, 'signUp'); + const signInSpyon = jest.spyOn( + InternalCognitoUser.prototype, + 'authenticateUser' + ); const auth = new Auth(authOptionConfirmationLink); const attrs = { username: 'username', @@ -652,15 +689,18 @@ describe('auth unit test', () => { }); test('fail confirmation code', async () => { - const spyon = jest.spyOn(CognitoUserPool.prototype, 'signUp'); + const spyon = jest.spyOn(InternalCognitoUserPool.prototype, 'signUp'); const confirmSpyon = jest - .spyOn(CognitoUser.prototype, 'confirmRegistration') + .spyOn(InternalCognitoUser.prototype, 'confirmRegistration') .mockImplementationOnce( (confirmationCode, forceAliasCreation, callback) => { callback('err', null); } ); - const signInSpyon = jest.spyOn(CognitoUser.prototype, 'authenticateUser'); + const signInSpyon = jest.spyOn( + InternalCognitoUser.prototype, + 'authenticateUser' + ); const auth = new Auth(authOptions); const attrs = { username: 'username', @@ -687,7 +727,10 @@ describe('auth unit test', () => { describe('confirmSignUp', () => { test('happy case', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'confirmRegistration'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'confirmRegistration' + ); const auth = new Auth(authOptions); expect.assertions(1); @@ -697,7 +740,10 @@ describe('auth unit test', () => { }); test('with options', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'confirmRegistration'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'confirmRegistration' + ); const auth = new Auth(authOptions); expect.assertions(1); @@ -711,25 +757,34 @@ describe('auth unit test', () => { }); test('happy case clientMetadata default', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'confirmRegistration'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'confirmRegistration' + ); const auth = new Auth(authOptionsWithClientMetadata); const code = 'code'; await auth.confirmSignUp('username', code); - expect(await CognitoUser.prototype.confirmRegistration).toBeCalledWith( + expect( + await InternalCognitoUser.prototype.confirmRegistration + ).toBeCalledWith( code, jasmine.any(Boolean), jasmine.any(Function), { foo: 'bar', - } + }, + getAuthUserAgentValue(AuthAction.ConfirmSignUp) ); spyon.mockClear(); }); test('happy case clientMetadata parameter', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'confirmRegistration'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'confirmRegistration' + ); const auth = new Auth(authOptionsWithClientMetadata); const code = 'code'; @@ -737,20 +792,23 @@ describe('auth unit test', () => { clientMetadata: { custom: 'value' }, }); - expect(await CognitoUser.prototype.confirmRegistration).toBeCalledWith( + expect( + await InternalCognitoUser.prototype.confirmRegistration + ).toBeCalledWith( code, jasmine.any(Boolean), jasmine.any(Function), { custom: 'value', - } + }, + getAuthUserAgentValue(AuthAction.ConfirmSignUp) ); spyon.mockClear(); }); test('callback err', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'confirmRegistration') + .spyOn(InternalCognitoUser.prototype, 'confirmRegistration') .mockImplementationOnce( (confirmationCode, forceAliasCreation, callback) => { callback('err', null); @@ -813,7 +871,10 @@ describe('auth unit test', () => { describe('resendSignUp', () => { test('happy case', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'resendConfirmationCode'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'resendConfirmationCode' + ); const auth = new Auth(authOptions); expect.assertions(1); @@ -829,34 +890,46 @@ describe('auth unit test', () => { }); test('happy case clientMetadata default', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'resendConfirmationCode'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'resendConfirmationCode' + ); const auth = new Auth(authOptionsWithClientMetadata); await auth.resendSignUp('username'); - expect(await CognitoUser.prototype.resendConfirmationCode).toBeCalledWith( + expect( + await InternalCognitoUser.prototype.resendConfirmationCode + ).toBeCalledWith( jasmine.any(Function), - { foo: 'bar' } + { foo: 'bar' }, + getAuthUserAgentValue(AuthAction.ResendSignUp) ); spyon.mockClear(); }); test('happy case clientMetadata parameter', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'resendConfirmationCode'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'resendConfirmationCode' + ); const auth = new Auth(authOptionsWithClientMetadata); await auth.resendSignUp('username', { custom: 'value' }); - expect(await CognitoUser.prototype.resendConfirmationCode).toBeCalledWith( + expect( + await InternalCognitoUser.prototype.resendConfirmationCode + ).toBeCalledWith( jasmine.any(Function), - { custom: 'value' } + { custom: 'value' }, + getAuthUserAgentValue(AuthAction.ResendSignUp) ); spyon.mockClear(); }); test('callback err', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'resendConfirmationCode') + .spyOn(InternalCognitoUser.prototype, 'resendConfirmationCode') .mockImplementationOnce(callback => { callback(new Error('err'), null); }); @@ -952,19 +1025,19 @@ describe('auth unit test', () => { describe('signIn', () => { test('happy case with password', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'authenticateUser') + .spyOn(InternalCognitoUser.prototype, 'authenticateUser') .mockImplementationOnce((authenticationDetails, callback) => { callback.onSuccess(session); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon2 = jest - .spyOn(auth, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return Promise.resolve(user); }); @@ -978,12 +1051,17 @@ describe('auth unit test', () => { }); test('happy case clientMetadata default', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'authenticateUser'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'authenticateUser' + ); const auth = new Auth(authOptionsWithClientMetadata); await auth.signIn('username', 'password'); - expect(await CognitoUser.prototype.authenticateUser).toBeCalledWith( + expect( + await InternalCognitoUser.prototype.authenticateUser + ).toBeCalledWith( { username: 'username', password: 'password', @@ -991,18 +1069,24 @@ describe('auth unit test', () => { clientMetadata: { foo: 'bar' }, authParameters: {}, }, - authCallbacks + authCallbacks, + getAuthUserAgentValue(AuthAction.SignIn) ); spyon.mockClear(); }); test('happy case clientMetadata parameter', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'authenticateUser'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'authenticateUser' + ); const auth = new Auth(authOptionsWithClientMetadata); await auth.signIn('username', 'password', { custom: 'value' }); - expect(await CognitoUser.prototype.authenticateUser).toBeCalledWith( + expect( + await InternalCognitoUser.prototype.authenticateUser + ).toBeCalledWith( { username: 'username', password: 'password', @@ -1010,13 +1094,14 @@ describe('auth unit test', () => { clientMetadata: { custom: 'value' }, authParameters: {}, }, - authCallbacks + authCallbacks, + getAuthUserAgentValue(AuthAction.SignIn) ); spyon.mockClear(); }); test('happy case validationData parameter', async () => { - const spyon = jest.spyOn(CognitoUserPool.prototype, 'signUp'); + const spyon = jest.spyOn(InternalCognitoUserPool.prototype, 'signUp'); const auth = new Auth(authOptionsWithClientMetadata); const attrs: SignUpParams = { @@ -1050,26 +1135,27 @@ describe('auth unit test', () => { { Name: 'test', Value: '123' }, ], jasmine.any(Function), - { custom: 'value' } + { custom: 'value' }, + getAuthUserAgentValue(AuthAction.SignUp) ); spyon.mockClear(); }); test('throw error if failed to call currentUserPoolUser after signing in', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'authenticateUser') + .spyOn(InternalCognitoUser.prototype, 'authenticateUser') .mockImplementationOnce((authenticationDetails, callback) => { callback.onSuccess(session); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon2 = jest - .spyOn(auth, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return Promise.reject('User is disabled.'); }); @@ -1087,7 +1173,7 @@ describe('auth unit test', () => { test('happy case using cookie storage', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'authenticateUser') + .spyOn(InternalCognitoUser.prototype, 'authenticateUser') .mockImplementationOnce((_authenticationDetails, callback) => { callback.onSuccess(session); }); @@ -1096,14 +1182,14 @@ describe('auth unit test', () => { ...authOptions, cookieStorage: { domain: '.example.com' }, }); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, Storage: new CookieStorage({ domain: '.yourdomain.com' }), }); const spyon2 = jest - .spyOn(auth, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return Promise.resolve(user); }); @@ -1117,7 +1203,7 @@ describe('auth unit test', () => { test('onFailure', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'authenticateUser') + .spyOn(InternalCognitoUser.prototype, 'authenticateUser') .mockImplementationOnce((authenticationDetails, callback) => { callback.onFailure('err'); }); @@ -1136,12 +1222,12 @@ describe('auth unit test', () => { test('mfaRequired', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'authenticateUser') + .spyOn(InternalCognitoUser.prototype, 'authenticateUser') .mockImplementationOnce((authenticationDetails, callback) => { callback.mfaRequired('SELECT_MFA_TYPE', 'challengeParam'); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1160,12 +1246,12 @@ describe('auth unit test', () => { test('mfaSetup', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'authenticateUser') + .spyOn(InternalCognitoUser.prototype, 'authenticateUser') .mockImplementationOnce((authenticationDetails, callback) => { callback.mfaSetup('MFA_SETUP', 'challengeParam'); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1184,12 +1270,12 @@ describe('auth unit test', () => { test('totpRequired', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'authenticateUser') + .spyOn(InternalCognitoUser.prototype, 'authenticateUser') .mockImplementationOnce((authenticationDetails, callback) => { callback.totpRequired('SOFTWARE_TOKEN_MFA', 'challengeParam'); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1208,12 +1294,12 @@ describe('auth unit test', () => { test('selectMFAType', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'authenticateUser') + .spyOn(InternalCognitoUser.prototype, 'authenticateUser') .mockImplementationOnce((authenticationDetails, callback) => { callback.selectMFAType('SELECT_MFA_TYPE', 'challengeParam'); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1232,12 +1318,12 @@ describe('auth unit test', () => { test('newPasswordRequired', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'authenticateUser') + .spyOn(InternalCognitoUser.prototype, 'authenticateUser') .mockImplementationOnce((authenticationDetails, callback) => { callback.newPasswordRequired('userAttributes', 'requiredAttributes'); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1259,15 +1345,18 @@ describe('auth unit test', () => { test('customChallenge', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'authenticateUser') + .spyOn(InternalCognitoUser.prototype, 'authenticateUser') .mockImplementationOnce((authenticationDetails, callback) => { callback.customChallenge('challengeParam'); }); const spyon2 = jest - .spyOn(CognitoUser.prototype as any, 'setAuthenticationFlowType') + .spyOn( + InternalCognitoUser.prototype as any, + 'setAuthenticationFlowType' + ) .mockImplementationOnce(type => {}); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1285,7 +1374,10 @@ describe('auth unit test', () => { }); test('no userPool', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'authenticateUser'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'authenticateUser' + ); // @ts-ignore const auth = new Auth(authOptionsWithNoUserPoolId); @@ -1301,7 +1393,10 @@ describe('auth unit test', () => { }); test('no username', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'authenticateUser'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'authenticateUser' + ); const auth = new Auth(authOptions); expect.assertions(1); @@ -1318,12 +1413,12 @@ describe('auth unit test', () => { describe('confirmSignIn', () => { test('happy case', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'sendMFACode') + .spyOn(InternalCognitoUser.prototype, 'sendMFACode') .mockImplementationOnce((code, callback) => { callback.onSuccess(session); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1336,13 +1431,13 @@ describe('auth unit test', () => { test('happy case attributes are appended', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'sendMFACode') + .spyOn(InternalCognitoUser.prototype, 'sendMFACode') .mockImplementationOnce((code, callback) => { callback.onSuccess(session); }); const hubSpy = jest.spyOn(Hub, 'dispatch'); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1363,9 +1458,9 @@ describe('auth unit test', () => { }); test('happy case clientMetadata default', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'sendMFACode'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'sendMFACode'); const auth = new Auth(authOptionsWithClientMetadata); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1373,22 +1468,23 @@ describe('auth unit test', () => { await auth.confirmSignIn(user, code); - expect(await CognitoUser.prototype.sendMFACode).toBeCalledWith( + expect(await InternalCognitoUser.prototype.sendMFACode).toBeCalledWith( code, { onSuccess: jasmine.any(Function), onFailure: jasmine.any(Function), }, undefined, - { foo: 'bar' } + { foo: 'bar' }, + getAuthUserAgentValue(AuthAction.ConfirmSignIn) ); spyon.mockClear(); }); test('happy case clientMetadata parameter', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'sendMFACode'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'sendMFACode'); const auth = new Auth(authOptionsWithClientMetadata); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1396,14 +1492,15 @@ describe('auth unit test', () => { await auth.confirmSignIn(user, code, 'SMS_MFA', { custom: 'value' }); - expect(await CognitoUser.prototype.sendMFACode).toBeCalledWith( + expect(await InternalCognitoUser.prototype.sendMFACode).toBeCalledWith( code, { onSuccess: jasmine.any(Function), onFailure: jasmine.any(Function), }, 'SMS_MFA', - { custom: 'value' } + { custom: 'value' }, + getAuthUserAgentValue(AuthAction.ConfirmSignIn) ); spyon.mockClear(); }); @@ -1411,18 +1508,18 @@ describe('auth unit test', () => { test('currentUserPoolUser fails but hub event still dispatches', async () => { const auth = new Auth(authOptions); const spyon = jest - .spyOn(CognitoUser.prototype, 'sendMFACode') + .spyOn(InternalCognitoUser.prototype, 'sendMFACode') .mockImplementationOnce((code, callback) => { callback.onSuccess(session); }); const spyon2 = jest - .spyOn(auth, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return Promise.reject('Could not get current user.'); }); const hubSpy = jest.spyOn(Hub, 'dispatch'); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1443,12 +1540,12 @@ describe('auth unit test', () => { test('onFailure', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'sendMFACode') + .spyOn(InternalCognitoUser.prototype, 'sendMFACode') .mockImplementationOnce((code, callback) => { callback.onFailure('err'); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1463,10 +1560,10 @@ describe('auth unit test', () => { }); test('no code', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'sendMFACode'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'sendMFACode'); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1485,13 +1582,13 @@ describe('auth unit test', () => { describe('completeNewPassword', () => { test('happy case', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'completeNewPasswordChallenge') + .spyOn(InternalCognitoUser.prototype, 'completeNewPasswordChallenge') .mockImplementationOnce((password, requiredAttributes, callback) => { callback.onSuccess(session); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1506,11 +1603,11 @@ describe('auth unit test', () => { test('happy case clientMetadata default', async () => { const spyon = jest.spyOn( - CognitoUser.prototype, + InternalCognitoUser.prototype, 'completeNewPasswordChallenge' ); const auth = new Auth(authOptionsWithClientMetadata); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1518,7 +1615,7 @@ describe('auth unit test', () => { await auth.completeNewPassword(user, 'password', {}); expect( - await CognitoUser.prototype.completeNewPasswordChallenge + await InternalCognitoUser.prototype.completeNewPasswordChallenge ).toBeCalledWith( 'password', {}, @@ -1529,18 +1626,19 @@ describe('auth unit test', () => { mfaSetup: jasmine.any(Function), totpRequired: jasmine.any(Function), }, - { foo: 'bar' } + { foo: 'bar' }, + getAuthUserAgentValue(AuthAction.CompleteNewPassword) ); spyon.mockClear(); }); test('happy case clientMetadata default', async () => { const spyon = jest.spyOn( - CognitoUser.prototype, + InternalCognitoUser.prototype, 'completeNewPasswordChallenge' ); const auth = new Auth(authOptionsWithClientMetadata); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1548,7 +1646,7 @@ describe('auth unit test', () => { await auth.completeNewPassword(user, 'password', {}, { custom: 'value' }); expect( - await CognitoUser.prototype.completeNewPasswordChallenge + await InternalCognitoUser.prototype.completeNewPasswordChallenge ).toBeCalledWith( 'password', {}, @@ -1559,20 +1657,21 @@ describe('auth unit test', () => { mfaSetup: jasmine.any(Function), totpRequired: jasmine.any(Function), }, - { custom: 'value' } + { custom: 'value' }, + getAuthUserAgentValue(AuthAction.CompleteNewPassword) ); spyon.mockClear(); }); test('on Failure', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'completeNewPasswordChallenge') + .spyOn(InternalCognitoUser.prototype, 'completeNewPasswordChallenge') .mockImplementationOnce((password, requiredAttributes, callback) => { callback.onFailure('err'); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1589,13 +1688,13 @@ describe('auth unit test', () => { test('mfaRequired', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'completeNewPasswordChallenge') + .spyOn(InternalCognitoUser.prototype, 'completeNewPasswordChallenge') .mockImplementationOnce((password, requiredAttributes, callback) => { callback.mfaRequired('SMS_MFA', 'challengeParam'); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1608,13 +1707,13 @@ describe('auth unit test', () => { test('mfaSetup', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'completeNewPasswordChallenge') + .spyOn(InternalCognitoUser.prototype, 'completeNewPasswordChallenge') .mockImplementationOnce((password, requiredAttributes, callback) => { callback.mfaSetup('MFA_SETUP', 'challengeParam'); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1627,7 +1726,7 @@ describe('auth unit test', () => { test('no password', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1647,17 +1746,20 @@ describe('auth unit test', () => { describe('userAttributes', () => { test('happy case', async () => { const spyon = jest - .spyOn(InternalAuthClass.prototype, 'userSession') + .spyOn(InternalAuthClass.prototype as any, '_userSession') .mockImplementationOnce(user => { return new Promise((res: any, rej) => { res('session'); }); }); - const spyon2 = jest.spyOn(CognitoUser.prototype, 'getUserAttributes'); + const spyon2 = jest.spyOn( + InternalCognitoUser.prototype, + 'getUserAttributes' + ); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1671,7 +1773,7 @@ describe('auth unit test', () => { test('get userattributes failed', async () => { const spyon = jest - .spyOn(InternalAuthClass.prototype, 'userSession') + .spyOn(InternalAuthClass.prototype as any, '_userSession') .mockImplementationOnce(user => { return new Promise((res: any, rej) => { res('session'); @@ -1679,13 +1781,13 @@ describe('auth unit test', () => { }); const spyon2 = jest - .spyOn(CognitoUser.prototype, 'getUserAttributes') + .spyOn(InternalCognitoUser.prototype, 'getUserAttributes') .mockImplementationOnce(callback => { callback(new Error('err')); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1709,19 +1811,19 @@ describe('auth unit test', () => { }); test('happy case', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon = jest - .spyOn(auth, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return Promise.resolve(user); }); const spyon2 = jest - .spyOn(InternalAuthClass.prototype, 'userSession') + .spyOn(InternalAuthClass.prototype as any, '_userSession') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(session); @@ -1737,19 +1839,19 @@ describe('auth unit test', () => { test('no current session', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon = jest - .spyOn(auth, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return Promise.resolve(user); }); const spyon2 = jest - .spyOn(auth, 'userSession') + .spyOn(InternalAuthClass.prototype as any, '_userSession') .mockImplementationOnce(() => { return Promise.reject('cannot get the session'); }); @@ -1769,7 +1871,7 @@ describe('auth unit test', () => { const auth = new Auth(authOptions); const spyon = jest - .spyOn(auth, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return Promise.reject('no current user'); }); @@ -1804,13 +1906,13 @@ describe('auth unit test', () => { describe('currentAuthenticatedUser', () => { test('happy case with source userpool', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon = jest - .spyOn(InternalAuthClass.prototype, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(user); @@ -1841,7 +1943,7 @@ describe('auth unit test', () => { }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1859,13 +1961,13 @@ describe('auth unit test', () => { describe('userSession test', () => { test('happy case', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementationOnce((callback: any) => { callback(null, session); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1879,17 +1981,21 @@ describe('auth unit test', () => { test('debouncer happy case', async () => { const concurrency = 10; const spyon = jest - .spyOn(CognitoUser.prototype, 'getSession') - .mockImplementationOnce(function(callback: any) { + .spyOn(InternalCognitoUser.prototype, 'getSession') + + .mockImplementationOnce( + // prettier-ignore + function(callback: any) { this.signInUserSession = session; callback(null, session); - }); + } + ); expect.assertions(2 * concurrency + 1); const auth = new Auth(authOptions); const promiseArr = Array.from({ length: concurrency }, async () => { - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1907,13 +2013,13 @@ describe('auth unit test', () => { test('callback error', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon = jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementationOnce((callback: any) => { callback('err', null); }); @@ -1932,14 +2038,14 @@ describe('auth unit test', () => { const auth = new Auth(authOptions); const spyon = jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementationOnce((callback: any) => { callback('err', null); }); expect.assertions(2); try { const promiseArr = Array.from({ length: 10 }, async () => { - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -1968,12 +2074,12 @@ describe('auth unit test', () => { test('refresh token revoked case', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementationOnce(() => user); const getSessionSpy = jest .spyOn(user, 'getSession') @@ -2004,16 +2110,16 @@ describe('auth unit test', () => { const auth = new Auth(authOptions); const credentialsClearSpy = jest.spyOn(Credentials, 'clear'); const hubSpy = jest.spyOn(Hub, 'dispatch'); - let user: CognitoUser | null = null; + let user: InternalCognitoUser | null = null; const getSessionSpy = jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementationOnce((callback: any) => { callback(new Error('Refresh Token has been revoked'), null); }); const userSignoutSpy = jest.fn(); expect.assertions(5); const promiseArr = Array.from({ length: 10 }, async () => { - user = new CognitoUser({ + user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -2088,7 +2194,7 @@ describe('auth unit test', () => { const auth = new Auth(authOptions); const spyon2 = jest - .spyOn(auth, 'currentSession') + .spyOn(InternalAuthClass.prototype as any, '_currentSession') .mockImplementationOnce(() => { return Promise.resolve('session' as any); }); @@ -2122,7 +2228,7 @@ describe('auth unit test', () => { const auth = new Auth(authOptions); const spyon2 = jest - .spyOn(auth, 'currentSession') + .spyOn(InternalAuthClass.prototype as any, '_currentSession') .mockImplementationOnce(() => { return Promise.reject('err' as any); }); @@ -2156,7 +2262,7 @@ describe('auth unit test', () => { const auth = new Auth(authOptions); const spyon2 = jest - .spyOn(auth, 'currentSession') + .spyOn(InternalAuthClass.prototype as any, '_currentSession') .mockImplementationOnce(() => { return Promise.resolve('session') as any; }); @@ -2191,12 +2297,12 @@ describe('auth unit test', () => { describe('verifyUserAttribute test', () => { test('happy case', async () => { const spyon = jest.spyOn( - CognitoUser.prototype, + InternalCognitoUser.prototype, 'getAttributeVerificationCode' ); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -2210,13 +2316,13 @@ describe('auth unit test', () => { test('onFailure', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'getAttributeVerificationCode') + .spyOn(InternalCognitoUser.prototype, 'getAttributeVerificationCode') .mockImplementationOnce((attr, callback) => { callback.onFailure('err' as any); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -2234,10 +2340,13 @@ describe('auth unit test', () => { describe('verifyUserAttributeSubmit', () => { test('happy case', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'verifyAttribute'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'verifyAttribute' + ); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -2252,13 +2361,13 @@ describe('auth unit test', () => { test('onFailure', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'verifyAttribute') + .spyOn(InternalCognitoUser.prototype, 'verifyAttribute') .mockImplementationOnce((attr, code, callback) => { callback.onFailure('err' as any); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -2275,7 +2384,7 @@ describe('auth unit test', () => { test('code empty', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -2294,13 +2403,13 @@ describe('auth unit test', () => { describe('verifyCurrentUserAttribute test', () => { test('happy case', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon = jest - .spyOn(InternalAuthClass.prototype, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(user); @@ -2308,7 +2417,7 @@ describe('auth unit test', () => { }); const spyon2 = jest - .spyOn(InternalAuthClass.prototype, 'verifyUserAttribute') + .spyOn(InternalAuthClass.prototype as any, '_verifyUserAttribute') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(); @@ -2318,8 +2427,17 @@ describe('auth unit test', () => { await auth.verifyCurrentUserAttribute('attr'); expect.assertions(2); - expect(spyon).toBeCalled(); - expect(spyon2).toBeCalledWith(user, 'attr', undefined); + console.log('??', spyon.mock.calls); + expect(spyon).toBeCalledWith( + undefined, + getAuthUserAgentDetails(AuthAction.VerifyCurrentUserAttribute) + ); + expect(spyon2).toBeCalledWith( + user, + 'attr', + undefined, + getAuthUserAgentDetails(AuthAction.VerifyCurrentUserAttribute) + ); spyon.mockClear(); spyon2.mockClear(); @@ -2329,13 +2447,13 @@ describe('auth unit test', () => { describe('verifyCurrentUserAttributeSubmit test', () => { test('happy case', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon = jest - .spyOn(InternalAuthClass.prototype, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(user); @@ -2343,7 +2461,7 @@ describe('auth unit test', () => { }); const spyon2 = jest - .spyOn(InternalAuthClass.prototype, 'verifyUserAttributeSubmit') + .spyOn(InternalAuthClass.prototype as any, '_verifyUserAttributeSubmit') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(); @@ -2353,8 +2471,16 @@ describe('auth unit test', () => { await auth.verifyCurrentUserAttributeSubmit('attr', 'code'); expect.assertions(2); - expect(spyon).toBeCalled(); - expect(spyon2).toBeCalledWith(user, 'attr', 'code'); + expect(spyon).toBeCalledWith( + undefined, + getAuthUserAgentDetails(AuthAction.VerifyCurrentUserAttributeSubmit) + ); + expect(spyon2).toBeCalledWith( + user, + 'attr', + 'code', + getAuthUserAgentDetails(AuthAction.VerifyCurrentUserAttributeSubmit) + ); spyon.mockClear(); spyon2.mockClear(); @@ -2377,7 +2503,7 @@ describe('auth unit test', () => { test('happy case', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -2388,7 +2514,7 @@ describe('auth unit test', () => { return Promise.resolve(); }); const spyon2 = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementationOnce(() => { return user; }); @@ -2405,7 +2531,7 @@ describe('auth unit test', () => { test('happy case for source userpool', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -2422,11 +2548,11 @@ describe('auth unit test', () => { }); }); const spyon = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementationOnce(() => { return user; }); - const spyon2 = jest.spyOn(CognitoUser.prototype, 'signOut'); + const spyon2 = jest.spyOn(InternalCognitoUser.prototype, 'signOut'); // @ts-ignore await auth.signOut(); @@ -2441,7 +2567,7 @@ describe('auth unit test', () => { test('happy case for globalSignOut', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -2452,11 +2578,11 @@ describe('auth unit test', () => { return Promise.resolve(); }); const spyon = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementationOnce(() => { return user; }); - const spyon2 = jest.spyOn(CognitoUser.prototype, 'globalSignOut'); + const spyon2 = jest.spyOn(InternalCognitoUser.prototype, 'globalSignOut'); await auth.signOut({ global: true }); @@ -2479,7 +2605,7 @@ describe('auth unit test', () => { const auth = new Auth(authOptions); const spyon = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementationOnce(() => { return null; }); @@ -2498,7 +2624,7 @@ describe('auth unit test', () => { describe('changePassword', () => { test('happy case', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -2506,7 +2632,7 @@ describe('auth unit test', () => { const newPassword = 'newPassword1.'; const spyon = jest - .spyOn(InternalAuthClass.prototype, 'userSession') + .spyOn(InternalAuthClass.prototype as any, '_userSession') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(session); @@ -2522,9 +2648,9 @@ describe('auth unit test', () => { }); test('happy case clientMetadata default', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'changePassword'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'changePassword'); const auth = new Auth(authOptionsWithClientMetadata); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -2533,21 +2659,22 @@ describe('auth unit test', () => { await auth.changePassword(user, oldPassword, newPassword); - expect(await CognitoUser.prototype.changePassword).toBeCalledWith( + expect(await InternalCognitoUser.prototype.changePassword).toBeCalledWith( oldPassword, newPassword, jasmine.any(Function), { foo: 'bar', - } + }, + getAuthUserAgentValue(AuthAction.ChangePassword) ); spyon.mockClear(); }); test('happy case clientMetadata parameter', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'changePassword'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'changePassword'); const auth = new Auth(authOptionsWithClientMetadata); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -2558,13 +2685,14 @@ describe('auth unit test', () => { custom: 'value', }); - expect(await CognitoUser.prototype.changePassword).toBeCalledWith( + expect(await InternalCognitoUser.prototype.changePassword).toBeCalledWith( oldPassword, newPassword, jasmine.any(Function), { custom: 'value', - } + }, + getAuthUserAgentValue(AuthAction.ChangePassword) ); spyon.mockClear(); }); @@ -2572,7 +2700,7 @@ describe('auth unit test', () => { describe('forgotPassword', () => { test('happy case', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'forgotPassword'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'forgotPassword'); const auth = new Auth(authOptions); @@ -2583,42 +2711,44 @@ describe('auth unit test', () => { }); test('happy case clientMetadata default', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'forgotPassword'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'forgotPassword'); const auth = new Auth(authOptionsWithClientMetadata); await auth.forgotPassword('username'); - expect(await CognitoUser.prototype.forgotPassword).toBeCalledWith( + expect(await InternalCognitoUser.prototype.forgotPassword).toBeCalledWith( { inputVerificationCode: jasmine.any(Function), onFailure: jasmine.any(Function), onSuccess: jasmine.any(Function), }, - { foo: 'bar' } + { foo: 'bar' }, + getAuthUserAgentValue(AuthAction.ForgotPassword) ); spyon.mockClear(); }); test('happy case clientMetadata parameter', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'forgotPassword'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'forgotPassword'); const auth = new Auth(authOptionsWithClientMetadata); await auth.forgotPassword('username', { custom: 'value' }); - expect(await CognitoUser.prototype.forgotPassword).toBeCalledWith( + expect(await InternalCognitoUser.prototype.forgotPassword).toBeCalledWith( { inputVerificationCode: jasmine.any(Function), onFailure: jasmine.any(Function), onSuccess: jasmine.any(Function), }, - { custom: 'value' } + { custom: 'value' }, + getAuthUserAgentValue(AuthAction.ForgotPassword) ); spyon.mockClear(); }); test('onFailure', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'forgotPassword') + .spyOn(InternalCognitoUser.prototype, 'forgotPassword') .mockImplementationOnce(callback => { callback.onFailure(new Error('err')); }); @@ -2637,7 +2767,7 @@ describe('auth unit test', () => { test('inputVerificationCode', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'forgotPassword') + .spyOn(InternalCognitoUser.prototype, 'forgotPassword') .mockImplementationOnce(callback => { callback.inputVerificationCode('data'); }); @@ -2651,7 +2781,7 @@ describe('auth unit test', () => { }); test('no user pool id', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'forgotPassword'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'forgotPassword'); const auth = new Auth(authOptionsWithNoUserPoolId); const errorMessage = new NoUserPoolError( @@ -2670,7 +2800,7 @@ describe('auth unit test', () => { }); test('no username', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'forgotPassword'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'forgotPassword'); const auth = new Auth(authOptions); @@ -2686,7 +2816,10 @@ describe('auth unit test', () => { describe('forgotPasswordSubmit', () => { test('happy case', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'confirmPassword'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'confirmPassword' + ); const auth = new Auth(authOptions); @@ -2699,7 +2832,10 @@ describe('auth unit test', () => { }); test('happy case', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'confirmPassword'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'confirmPassword' + ); const auth = new Auth(authOptions); @@ -2710,7 +2846,7 @@ describe('auth unit test', () => { }); test('happy case clientMetadata default', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'forgotPassword'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'forgotPassword'); const auth = new Auth(authOptionsWithClientMetadata); const username = 'username'; const code = 'code'; @@ -2718,20 +2854,23 @@ describe('auth unit test', () => { await auth.forgotPasswordSubmit(username, code, password); - expect(await CognitoUser.prototype.confirmPassword).toBeCalledWith( + expect( + await InternalCognitoUser.prototype.confirmPassword + ).toBeCalledWith( code, password, { onFailure: jasmine.any(Function), onSuccess: jasmine.any(Function), }, - { foo: 'bar' } + { foo: 'bar' }, + getAuthUserAgentValue(AuthAction.ForgotPasswordSubmit) ); spyon.mockClear(); }); test('happy case clientMetadata parameter', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'forgotPassword'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'forgotPassword'); const auth = new Auth(authOptionsWithClientMetadata); const username = 'username'; const code = 'code'; @@ -2741,21 +2880,24 @@ describe('auth unit test', () => { custom: 'value', }); - expect(await CognitoUser.prototype.confirmPassword).toBeCalledWith( + expect( + await InternalCognitoUser.prototype.confirmPassword + ).toBeCalledWith( code, password, { onFailure: jasmine.any(Function), onSuccess: jasmine.any(Function), }, - { custom: 'value' } + { custom: 'value' }, + getAuthUserAgentValue(AuthAction.ForgotPasswordSubmit) ); spyon.mockClear(); }); test('confirmPassword failed', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'confirmPassword') + .spyOn(InternalCognitoUser.prototype, 'confirmPassword') .mockImplementationOnce((code, password, callback) => { callback.onFailure(new Error('err')); }); @@ -2830,13 +2972,13 @@ describe('auth unit test', () => { describe('currentUserInfo test', () => { test('happy case with aws or userpool source', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon = jest - .spyOn(InternalAuthClass.prototype, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(user); @@ -2844,7 +2986,7 @@ describe('auth unit test', () => { }); const spyon2 = jest - .spyOn(InternalAuthClass.prototype, 'userAttributes') + .spyOn(InternalAuthClass.prototype as any, '_userAttributes') .mockImplementationOnce(() => { auth['credentials'] = { IdentityPoolId: 'identityPoolId', @@ -2897,13 +3039,13 @@ describe('auth unit test', () => { test('return empty object if error happens', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon = jest - .spyOn(InternalAuthClass.prototype, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return new Promise((res, rej) => { res({ @@ -2913,7 +3055,7 @@ describe('auth unit test', () => { }); const spyon2 = jest - .spyOn(InternalAuthClass.prototype, 'userAttributes') + .spyOn(InternalAuthClass.prototype as any, '_userAttributes') .mockImplementationOnce(() => { return new Promise((res, rej) => { rej('err'); @@ -2946,14 +3088,14 @@ describe('auth unit test', () => { test('no current userpool user', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); auth['credentials_source'] = 'aws'; const spyon = jest - .spyOn(InternalAuthClass.prototype, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(null); @@ -2975,7 +3117,7 @@ describe('auth unit test', () => { test('federated user', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -2998,7 +3140,7 @@ describe('auth unit test', () => { test('happy case', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -3010,7 +3152,7 @@ describe('auth unit test', () => { }; const spyon = jest - .spyOn(InternalAuthClass.prototype, 'userSession') + .spyOn(InternalAuthClass.prototype as any, '_userSession') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(session); @@ -3024,37 +3166,49 @@ describe('auth unit test', () => { }); test('happy case clientMetadata default', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'updateAttributes'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'updateAttributes' + ); const auth = new Auth(authOptionsWithClientMetadata); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); await auth.updateUserAttributes(user, {}); - expect(await CognitoUser.prototype.updateAttributes).toBeCalledWith( + expect( + await InternalCognitoUser.prototype.updateAttributes + ).toBeCalledWith( [], jasmine.any(Function), - { foo: 'bar' } + { foo: 'bar' }, + getAuthUserAgentValue(AuthAction.UpdateUserAttributes) ); spyon.mockClear(); }); test('happy case clientMetadata parameter', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'updateAttributes'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'updateAttributes' + ); const auth = new Auth(authOptionsWithClientMetadata); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); await auth.updateUserAttributes(user, {}, { custom: 'value' }); - expect(await CognitoUser.prototype.updateAttributes).toBeCalledWith( + expect( + await InternalCognitoUser.prototype.updateAttributes + ).toBeCalledWith( [], jasmine.any(Function), - { custom: 'value' } + { custom: 'value' }, + getAuthUserAgentValue(AuthAction.UpdateUserAttributes) ); spyon.mockClear(); }); @@ -3062,14 +3216,14 @@ describe('auth unit test', () => { test('error hub event', async done => { expect.assertions(3); const spyon = jest - .spyOn(CognitoUser.prototype, 'updateAttributes') + .spyOn(InternalCognitoUser.prototype, 'updateAttributes') .mockImplementationOnce((attrs, callback: any) => { callback(new Error('Error'), null, null); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -3112,13 +3266,13 @@ describe('auth unit test', () => { ], }; const spyon = jest - .spyOn(CognitoUser.prototype, 'updateAttributes') + .spyOn(InternalCognitoUser.prototype, 'updateAttributes') .mockImplementationOnce((attrs, callback: any) => { callback(null, 'SUCCESS', codeDeliverDetailsResult); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -3162,7 +3316,7 @@ describe('auth unit test', () => { test('happy case', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -3170,7 +3324,7 @@ describe('auth unit test', () => { const attributeNames = ['email', 'phone_number']; const spyon = jest - .spyOn(InternalAuthClass.prototype, 'userSession') + .spyOn(InternalAuthClass.prototype as any, '_userSession') .mockImplementationOnce(() => { return new Promise(res => { res(session); @@ -3186,18 +3340,24 @@ describe('auth unit test', () => { }); test('happy case to call with expected attributes', async () => { - const spyon = jest.spyOn(CognitoUser.prototype, 'deleteAttributes'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'deleteAttributes' + ); const auth = new Auth(authOptionsWithClientMetadata); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); await auth.deleteUserAttributes(user, ['email', 'phone_number']); - expect(await CognitoUser.prototype.deleteAttributes).toBeCalledWith( + expect( + await InternalCognitoUser.prototype.deleteAttributes + ).toBeCalledWith( ['email', 'phone_number'], - jasmine.any(Function) + jasmine.any(Function), + getAuthUserAgentValue(AuthAction.DeleteUserAttributes) ); spyon.mockClear(); }); @@ -3210,23 +3370,23 @@ describe('auth unit test', () => { beforeEach(() => { jest.clearAllMocks(); auth = new Auth(authOptions); - user = new CognitoUser({ + user = new InternalCognitoUser({ Username: 'raz', Pool: userPool, }); - userPool = new CognitoUserPool({ + userPool = new InternalCognitoUserPool({ UserPoolId: authOptions.userPoolId, ClientId: authOptions.userPoolWebClientId, }); }); test('Happy path should delete a user', async () => { const spy1 = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return user; }); const spy2 = jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementation((callback: any) => { return callback(null, session); }); @@ -3250,7 +3410,7 @@ describe('auth unit test', () => { test('no user should throw error', async () => { const spy1 = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return null; }); @@ -3265,12 +3425,12 @@ describe('auth unit test', () => { test('no session should throw error', async () => { const spy1 = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return user; }); const spy2 = jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementation((callback: any) => { return callback(new Error('no session'), null); }); @@ -3284,7 +3444,7 @@ describe('auth unit test', () => { test('getSession call fail should signout user', async () => { jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return user; }); @@ -3314,17 +3474,17 @@ describe('auth unit test', () => { test('cognito deleteUser call fails...', async () => { const spy1 = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return user; }); const spy2 = jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementation((callback: any) => { return callback(null, session); }); const spy3 = jest - .spyOn(CognitoUser.prototype, 'deleteUser') + .spyOn(InternalCognitoUser.prototype, 'deleteUser') .mockImplementationOnce((callback: any) => { return callback(new Error('Cognito deleteUser error'), null); }); @@ -3417,7 +3577,7 @@ describe('auth unit test', () => { return Promise.resolve('cred' as any); }); const spyon2 = jest - .spyOn(InternalAuthClass.prototype, 'currentAuthenticatedUser') + .spyOn(InternalAuthClass.prototype as any, '_currentAuthenticatedUser') .mockImplementation(() => { if (!user) return Promise.reject('error'); else return Promise.resolve(user); @@ -3491,7 +3651,7 @@ describe('auth unit test', () => { return Promise.resolve('cred' as any); }); const spyon2 = jest - .spyOn(InternalAuthClass.prototype, 'currentAuthenticatedUser') + .spyOn(InternalAuthClass.prototype as any, '_currentAuthenticatedUser') .mockImplementation(() => { if (!user) return Promise.reject('error'); else return Promise.resolve(user); @@ -3522,15 +3682,7 @@ describe('auth unit test', () => { throw new Error('no user logged in'); }); - jest - .spyOn(StorageHelper.prototype, 'getStorage') - .mockImplementation(() => { - return { - setItem() { - return null; - }, - }; - }); + jest.spyOn(StorageHelper.prototype, 'getStorage'); }); test('User Pools Code Flow', async () => { @@ -3589,7 +3741,7 @@ describe('auth unit test', () => { (oauthStorage.getState as jest.Mock).mockReturnValueOnce(state); await (auth as any)._handleAuthResponse(url); - expect(handleAuthResponseSpy).toHaveBeenCalledWith(url); + expect(handleAuthResponseSpy).toHaveBeenCalledWith(url, undefined); expect(replaceStateSpy).toHaveBeenCalledWith( {}, null, @@ -3654,7 +3806,7 @@ describe('auth unit test', () => { await (auth as any)._handleAuthResponse(url); - expect(handleAuthResponseSpy).toHaveBeenCalledWith(url); + expect(handleAuthResponseSpy).toHaveBeenCalledWith(url, undefined); expect(replaceStateSpy).toHaveBeenCalledWith( {}, null, @@ -3718,7 +3870,7 @@ describe('auth unit test', () => { }?code=${code}`; await (auth as any)._handleAuthResponse(url); - expect(handleAuthResponseSpy).toHaveBeenCalledWith(url); + expect(handleAuthResponseSpy).toHaveBeenCalledWith(url, undefined); expect(replaceStateSpy).toHaveBeenCalledWith( {}, null, @@ -3730,7 +3882,7 @@ describe('auth unit test', () => { describe('verifiedContact test', () => { test('happy case with unverified', async () => { const spyon = jest - .spyOn(InternalAuthClass.prototype, 'userAttributes') + .spyOn(InternalAuthClass.prototype as any, '_userAttributes') .mockImplementationOnce(() => { return new Promise((res: any, rej) => { res([ @@ -3747,7 +3899,7 @@ describe('auth unit test', () => { }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -3762,7 +3914,7 @@ describe('auth unit test', () => { test('happy case with verified', async () => { const spyon = jest - .spyOn(InternalAuthClass.prototype, 'userAttributes') + .spyOn(InternalAuthClass.prototype as any, '_userAttributes') .mockImplementationOnce(() => { return new Promise((res: any, rej) => { res([ @@ -3787,7 +3939,7 @@ describe('auth unit test', () => { }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -3802,7 +3954,7 @@ describe('auth unit test', () => { test('happy case with verified as strings', async () => { const spyon = jest - .spyOn(InternalAuthClass.prototype, 'userAttributes') + .spyOn(InternalAuthClass.prototype as any, '_userAttributes') .mockImplementationOnce(() => { return new Promise((res: any, rej) => { res([ @@ -3827,7 +3979,7 @@ describe('auth unit test', () => { }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -3848,24 +4000,24 @@ describe('auth unit test', () => { test('happy case', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return user; }); const spyon2 = jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementation((callback: any) => { return callback(null, session); }); const spyon3 = jest - .spyOn(CognitoUser.prototype, 'getUserData') + .spyOn(InternalCognitoUser.prototype, 'getUserData') .mockImplementationOnce((callback: any) => { const data = { PreferredMfaSetting: 'SMS', @@ -3899,13 +4051,13 @@ describe('auth unit test', () => { test('no current user', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return null; }); @@ -3921,7 +4073,7 @@ describe('auth unit test', () => { test('No userPool in config', async () => { const auth = new Auth(authOptionsWithNoUserPoolId); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -3938,23 +4090,23 @@ describe('auth unit test', () => { test('get session error', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return user; }); const spyon2 = jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementation((callback: any) => { return callback('err', null); }); - const spyon3 = jest.spyOn(CognitoUser.prototype, 'getUserData'); + const spyon3 = jest.spyOn(InternalCognitoUser.prototype, 'getUserData'); expect.assertions(2); try { @@ -3967,12 +4119,12 @@ describe('auth unit test', () => { test('get session error - refresh token revoked should signout user', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return user; }); @@ -4006,27 +4158,30 @@ describe('auth unit test', () => { .mockImplementation(createMockLocalStorage); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return user; }); jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementation((callback: any) => { return callback(null, session); }); jest - .spyOn(CognitoUser.prototype, 'getUserData') + .spyOn(InternalCognitoUser.prototype, 'getUserData') .mockImplementationOnce((callback: any) => { callback(new Error('User is disabled.'), null); }); - const userSignoutSpy = jest.spyOn(CognitoUser.prototype, 'signOut'); + const userSignoutSpy = jest.spyOn( + InternalCognitoUser.prototype, + 'signOut' + ); jest .spyOn(CognitoUserSession.prototype, 'getAccessToken') @@ -4065,27 +4220,30 @@ describe('auth unit test', () => { }); const auth = new Auth(authOptionsWithHostedUIConfig); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return user; }); jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementation((callback: any) => { return callback(null, session); }); jest - .spyOn(CognitoUser.prototype, 'getUserData') + .spyOn(InternalCognitoUser.prototype, 'getUserData') .mockImplementationOnce((callback: any) => { callback(new Error('User is disabled.'), null); }); - const userSignoutSpy = jest.spyOn(CognitoUser.prototype, 'signOut'); + const userSignoutSpy = jest.spyOn( + InternalCognitoUser.prototype, + 'signOut' + ); jest .spyOn(CognitoUserSession.prototype, 'getAccessToken') @@ -4106,23 +4264,23 @@ describe('auth unit test', () => { test('bypass the error if the user is not deleted or disabled', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return user; }); const spyon2 = jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementation((callback: any) => { return callback(null, session); }); const spyon3 = jest - .spyOn(CognitoUser.prototype, 'getUserData') + .spyOn(InternalCognitoUser.prototype, 'getUserData') .mockImplementationOnce((callback: any) => { callback( { @@ -4151,24 +4309,24 @@ describe('auth unit test', () => { test('directly return the user if no permission(scope) to get the user data', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const spyon = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => { return user; }); const spyon2 = jest - .spyOn(CognitoUser.prototype, 'getSession') + .spyOn(InternalCognitoUser.prototype, 'getSession') .mockImplementation((callback: any) => { return callback(null, session); }); const spyon3 = jest - .spyOn(CognitoUser.prototype, 'getUserData') + .spyOn(InternalCognitoUser.prototype, 'getUserData') .mockImplementationOnce((callback: any) => { const data = { PreferredMfaSetting: 'SMS', @@ -4198,17 +4356,17 @@ describe('auth unit test', () => { describe('sendCustomChallengeAnswer', () => { test('happy case', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'sendCustomChallengeAnswer') + .spyOn(InternalCognitoUser.prototype, 'sendCustomChallengeAnswer') .mockImplementationOnce((challengeResponses, callback) => { callback.onSuccess(session); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const userAfterCustomChallengeAnswer = Object.assign( - new CognitoUser({ + new InternalCognitoUser({ Username: 'username', Pool: userPool, }), @@ -4219,7 +4377,7 @@ describe('auth unit test', () => { ); const spyon2 = jest - .spyOn(auth, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return Promise.resolve(user); }); @@ -4240,15 +4398,15 @@ describe('auth unit test', () => { const auth = new Auth(authOptionsWithClientMetadata); const spyon = jest.spyOn( - CognitoUser.prototype, + InternalCognitoUser.prototype, 'sendCustomChallengeAnswer' ); const spyon2 = jest - .spyOn(auth, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return Promise.resolve(user); }); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -4256,8 +4414,13 @@ describe('auth unit test', () => { await auth.sendCustomChallengeAnswer(user, 'answer'); expect( - await CognitoUser.prototype.sendCustomChallengeAnswer - ).toBeCalledWith('answer', authCallbacks, { foo: 'bar' }); + await InternalCognitoUser.prototype.sendCustomChallengeAnswer + ).toBeCalledWith( + 'answer', + authCallbacks, + { foo: 'bar' }, + getAuthUserAgentValue(AuthAction.SendCustomChallengeAnswer) + ); spyon.mockClear(); }); @@ -4265,15 +4428,15 @@ describe('auth unit test', () => { const auth = new Auth(authOptionsWithClientMetadata); const spyon = jest.spyOn( - CognitoUser.prototype, + InternalCognitoUser.prototype, 'sendCustomChallengeAnswer' ); const spyon2 = jest - .spyOn(auth, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype as any, '_currentUserPoolUser') .mockImplementationOnce(() => { return Promise.resolve(user); }); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -4281,24 +4444,29 @@ describe('auth unit test', () => { await auth.sendCustomChallengeAnswer(user, 'answer', { custom: 'value' }); expect( - await CognitoUser.prototype.sendCustomChallengeAnswer - ).toBeCalledWith('answer', authCallbacks, { custom: 'value' }); + await InternalCognitoUser.prototype.sendCustomChallengeAnswer + ).toBeCalledWith( + 'answer', + authCallbacks, + { custom: 'value' }, + getAuthUserAgentValue(AuthAction.SendCustomChallengeAnswer) + ); spyon.mockClear(); }); test('customChallenge', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'sendCustomChallengeAnswer') + .spyOn(InternalCognitoUser.prototype, 'sendCustomChallengeAnswer') .mockImplementationOnce((challengeResponses, callback) => { callback.customChallenge('challengeParam'); }); const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); const userAfterCustomChallengeAnswer = Object.assign( - new CognitoUser({ + new InternalCognitoUser({ Username: 'username', Pool: userPool, }), @@ -4321,14 +4489,14 @@ describe('auth unit test', () => { test('onFailure', async () => { const spyon = jest - .spyOn(CognitoUser.prototype, 'sendCustomChallengeAnswer') + .spyOn(InternalCognitoUser.prototype, 'sendCustomChallengeAnswer') .mockImplementationOnce((challengeResponses, callback) => { callback.onFailure('err'); }); const auth = new Auth(authOptions); const userAfterCustomChallengeAnswer = Object.assign( - new CognitoUser({ + new InternalCognitoUser({ Username: 'username', Pool: userPool, }), @@ -4353,13 +4521,13 @@ describe('auth unit test', () => { test('no userPool', async () => { const spyon = jest.spyOn( - CognitoUser.prototype, + InternalCognitoUser.prototype, 'sendCustomChallengeAnswer' ); const auth = new Auth(authOptionsWithNoUserPoolId); const userAfterCustomChallengeAnswer = Object.assign( - new CognitoUser({ + new InternalCognitoUser({ Username: 'username', Pool: userPool, }), @@ -4398,6 +4566,7 @@ describe('auth unit test', () => { describe('Device Tracking', () => { test('remember device happy path', async () => { const auth = new Auth(authOptions); + const spyon = jest .spyOn(CognitoUserSession.prototype, 'getAccessToken') .mockImplementationOnce(() => { @@ -4411,7 +4580,7 @@ describe('auth unit test', () => { }); const spyOnCognito = jest - .spyOn(CognitoUser.prototype, 'setDeviceStatusRemembered') + .spyOn(InternalCognitoUser.prototype, 'setDeviceStatusRemembered') .mockImplementationOnce( (obj: { onSuccess: (success: string) => void; @@ -4445,7 +4614,7 @@ describe('auth unit test', () => { }); const spyOnCognito = jest - .spyOn(CognitoUser.prototype, 'forgetDevice') + .spyOn(InternalCognitoUser.prototype, 'forgetDevice') .mockImplementationOnce( (obj: { onSuccess: (success: string) => void; @@ -4480,7 +4649,7 @@ describe('auth unit test', () => { }); const spyOnCognito = jest - .spyOn(CognitoUser.prototype, 'listDevices') + .spyOn(InternalCognitoUser.prototype, 'listDevices') .mockImplementationOnce( ( MAX_DEVICES, @@ -4519,7 +4688,7 @@ describe('auth unit test', () => { }); const spyOnCognito = jest - .spyOn(CognitoUser.prototype, 'listDevices') + .spyOn(InternalCognitoUser.prototype, 'listDevices') .mockImplementationOnce( ( MAX_DEVICES, @@ -4560,7 +4729,7 @@ describe('auth unit test', () => { test('happy path', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -4579,7 +4748,7 @@ describe('auth unit test', () => { test('should allow bypassCache', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -4593,19 +4762,25 @@ describe('auth unit test', () => { }); const res = await auth.getPreferredMFA(user, { bypassCache: true }); expect(res).toEqual('SMS'); - expect(getUserDataSpy).toHaveBeenCalledWith(expect.any(Function), { - bypassCache: true, - }); + expect(getUserDataSpy).toHaveBeenCalledWith( + expect.any(Function), + { + bypassCache: true, + }, + getAuthUserAgentValue(AuthAction.GetPreferredMFA) + ); }); test('get user data error because user is deleted, disabled or token has been revoked', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + console.log('mock class definition', InternalCognitoUser); + console.log('auth class def', Auth); + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementation(() => user); const getUserDataSpy = jest .spyOn(user, 'getUserData') @@ -4621,9 +4796,13 @@ describe('auth unit test', () => { await expect( auth.getPreferredMFA(user, { bypassCache: true }) ).rejects.toThrow('Access Token has been revoked'); - expect(getUserDataSpy).toHaveBeenCalledWith(expect.any(Function), { - bypassCache: true, - }); + expect(getUserDataSpy).toHaveBeenCalledWith( + expect.any(Function), + { + bypassCache: true, + }, + getAuthUserAgentValue(AuthAction.GetPreferredMFA) + ); expect(userSignoutSpy).toHaveBeenCalledTimes(1); expect(credentialsClearSpy).toHaveBeenCalledTimes(1); expect(hubSpy).toHaveBeenCalledWith( @@ -4649,7 +4828,7 @@ describe('auth unit test', () => { it('happy path', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); @@ -4666,11 +4845,16 @@ describe('auth unit test', () => { expect(setUserMfaPreferenceSpy).toHaveBeenCalledWith( null, { Enabled: true, PreferredMfa: true }, - expect.any(Function) + expect.any(Function), + getAuthUserAgentValue(AuthAction.SetPreferredMFA) + ); + expect(getUserDataSpy).toHaveBeenCalledWith( + expect.any(Function), + { + bypassCache: true, + }, + getAuthUserAgentValue(AuthAction.SetPreferredMFA) ); - expect(getUserDataSpy).toHaveBeenCalledWith(expect.any(Function), { - bypassCache: true, - }); // once at the beginning, once after calling setUserMfaPreference expect(getUserDataSpy).toHaveBeenCalledTimes(2); expect(res).toStrictEqual('success'); @@ -4678,12 +4862,12 @@ describe('auth unit test', () => { test('get user data error because user is deleted, disabled or token has been revoked', async () => { const auth = new Auth(authOptions); - const user = new CognitoUser({ + const user = new InternalCognitoUser({ Username: 'username', Pool: userPool, }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementationOnce(() => user); const getUserDataSpy = jest .spyOn(user, 'getUserData') @@ -4698,9 +4882,13 @@ describe('auth unit test', () => { await expect( auth.setPreferredMFA(user, 'SOFTWARE_TOKEN_MFA') ).rejects.toThrow('Access Token has been revoked'); - expect(getUserDataSpy).toHaveBeenCalledWith(expect.any(Function), { - bypassCache: true, - }); + expect(getUserDataSpy).toHaveBeenCalledWith( + expect.any(Function), + { + bypassCache: true, + }, + getAuthUserAgentValue(AuthAction.SetPreferredMFA) + ); expect(userSignoutSpy).toHaveBeenCalledTimes(1); expect(credentialsClearSpy).toHaveBeenCalledTimes(1); expect(hubSpy).toHaveBeenCalledWith( diff --git a/packages/auth/__tests__/hosted-ui.test.ts b/packages/auth/__tests__/hosted-ui.test.ts index cee6df74044..eed9ea69ee7 100644 --- a/packages/auth/__tests__/hosted-ui.test.ts +++ b/packages/auth/__tests__/hosted-ui.test.ts @@ -1,20 +1,21 @@ import { CognitoUser, - CognitoUserPool, CognitoUserSession, CognitoAccessToken, CognitoIdToken, } from 'amazon-cognito-identity-js'; -jest.mock('amazon-cognito-identity-js/lib/CognitoUserPool', () => { - const CognitoUserPool = () => {}; +import { InternalCognitoUserPool } from 'amazon-cognito-identity-js/internals'; - CognitoUserPool.prototype.CognitoUserPool = options => { - CognitoUserPool.prototype.options = options; - return CognitoUserPool; +jest.mock('amazon-cognito-identity-js/internals', () => { + const InternalCognitoUserPool = () => {}; + + InternalCognitoUserPool.prototype.InternalCognitoUserPool = options => { + InternalCognitoUserPool.prototype.options = options; + return InternalCognitoUserPool; }; - CognitoUserPool.prototype.getCurrentUser = () => { + InternalCognitoUserPool.prototype.getCurrentUser = () => { return { username: 'username', getSession: callback => { @@ -30,7 +31,7 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUserPool', () => { }; }; - CognitoUserPool.prototype.signUp = ( + InternalCognitoUserPool.prototype.signUp = ( username, password, signUpAttributeList, @@ -41,7 +42,10 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUserPool', () => { callback(null, 'signUpResult'); }; - return CognitoUserPool; + return { + ...jest.requireActual('amazon-cognito-identity-js/internals'), + InternalCognitoUserPool, + }; }); jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { @@ -180,7 +184,7 @@ const authOptionsWithOAuth: AuthOptions = { }, }; -const userPool = new CognitoUserPool({ +const userPool = new InternalCognitoUserPool({ UserPoolId: authOptionsWithOAuth.userPoolId, ClientId: authOptionsWithOAuth.userPoolWebClientId, }); @@ -209,7 +213,7 @@ describe('Hosted UI tests', () => { Pool: userPool, }); const spyonGetCurrentUser = jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementationOnce(() => { return user; }); @@ -286,7 +290,7 @@ describe('Hosted UI tests', () => { }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementationOnce(() => { return user; }); @@ -340,7 +344,7 @@ describe('Hosted UI tests', () => { }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementationOnce(() => { return user; }); @@ -394,7 +398,7 @@ describe('Hosted UI tests', () => { }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementationOnce(() => { return user; }); @@ -440,7 +444,7 @@ describe('Hosted UI tests', () => { }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementationOnce(() => { return user; }); @@ -500,7 +504,7 @@ describe('Hosted UI tests', () => { }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementationOnce(() => { return user; }); @@ -547,7 +551,7 @@ describe('Hosted UI tests', () => { }); jest - .spyOn(CognitoUserPool.prototype, 'getCurrentUser') + .spyOn(InternalCognitoUserPool.prototype, 'getCurrentUser') .mockImplementationOnce(() => { return user; }); diff --git a/packages/auth/__tests__/totp-unit-test.ts b/packages/auth/__tests__/totp-unit-test.ts index 892de6a4587..20f8e119035 100644 --- a/packages/auth/__tests__/totp-unit-test.ts +++ b/packages/auth/__tests__/totp-unit-test.ts @@ -34,19 +34,19 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoIdToken', () => { return CognitoIdToken; }); -jest.mock('amazon-cognito-identity-js/lib/CognitoUserPool', () => { - const CognitoUserPool = () => {}; +jest.mock('amazon-cognito-identity-js/internals', () => { + const InternalCognitoUserPool = () => {}; - CognitoUserPool.prototype.CognitoUserPool = options => { - CognitoUserPool.prototype.options = options; - return CognitoUserPool; + InternalCognitoUserPool.prototype.InternalCognitoUserPool = options => { + InternalCognitoUserPool.prototype.options = options; + return InternalCognitoUserPool; }; - CognitoUserPool.prototype.getCurrentUser = () => { + InternalCognitoUserPool.prototype.getCurrentUser = () => { return 'currentUser'; }; - CognitoUserPool.prototype.signUp = ( + InternalCognitoUserPool.prototype.signUp = ( username, password, signUpAttributeList, @@ -56,50 +56,49 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUserPool', () => { callback(null, 'signUpResult'); }; - return CognitoUserPool; -}); - -jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { - const CognitoUser = () => {}; + const InternalCognitoUser = () => {}; - CognitoUser.prototype.CognitoUser = options => { - CognitoUser.prototype.options = options; - return CognitoUser; + InternalCognitoUser.prototype.InternalCognitoUser = options => { + InternalCognitoUser.prototype.options = options; + return InternalCognitoUser; }; - CognitoUser.prototype.getSession = callback => { + InternalCognitoUser.prototype.getSession = callback => { // throw 3; callback(null, 'session'); }; - CognitoUser.prototype.getUserAttributes = callback => { + InternalCognitoUser.prototype.getUserAttributes = callback => { callback(null, 'attributes'); }; - CognitoUser.prototype.getAttributeVerificationCode = (attr, callback) => { + InternalCognitoUser.prototype.getAttributeVerificationCode = ( + attr, + callback + ) => { callback.onSuccess('success'); }; - CognitoUser.prototype.verifyAttribute = (attr, code, callback) => { + InternalCognitoUser.prototype.verifyAttribute = (attr, code, callback) => { callback.onSuccess('success'); }; - CognitoUser.prototype.authenticateUser = ( + InternalCognitoUser.prototype.authenticateUser = ( authenticationDetails, callback ) => { callback.onSuccess('session'); }; - CognitoUser.prototype.sendMFACode = (code, callback) => { + InternalCognitoUser.prototype.sendMFACode = (code, callback) => { callback.onSuccess('session'); }; - CognitoUser.prototype.resendConfirmationCode = callback => { + InternalCognitoUser.prototype.resendConfirmationCode = callback => { callback(null, 'result'); }; - CognitoUser.prototype.changePassword = ( + InternalCognitoUser.prototype.changePassword = ( oldPassword, newPassword, callback @@ -107,17 +106,21 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { callback(null, 'SUCCESS'); }; - CognitoUser.prototype.forgotPassword = callback => { + InternalCognitoUser.prototype.forgotPassword = callback => { callback.onSuccess(); }; - CognitoUser.prototype.confirmPassword = (code, password, callback) => { + InternalCognitoUser.prototype.confirmPassword = ( + code, + password, + callback + ) => { callback.onSuccess(); }; - CognitoUser.prototype.signOut = () => {}; + InternalCognitoUser.prototype.signOut = () => {}; - CognitoUser.prototype.confirmRegistration = ( + InternalCognitoUser.prototype.confirmRegistration = ( confirmationCode, forceAliasCreation, callback @@ -125,7 +128,7 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { callback(null, 'Success'); }; - CognitoUser.prototype.completeNewPasswordChallenge = ( + InternalCognitoUser.prototype.completeNewPasswordChallenge = ( password, requiredAttributes, callback @@ -133,27 +136,30 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { callback.onSuccess('session'); }; - CognitoUser.prototype.updateAttributes = (attributeList, callback) => { + InternalCognitoUser.prototype.updateAttributes = ( + attributeList, + callback + ) => { callback(null, 'SUCCESS'); }; - CognitoUser.prototype.getMFAOptions = callback => { + InternalCognitoUser.prototype.getMFAOptions = callback => { callback(null, 'mfaOptions'); }; - CognitoUser.prototype.disableMFA = callback => { + InternalCognitoUser.prototype.disableMFA = callback => { callback(null, 'Success'); }; - CognitoUser.prototype.enableMFA = callback => { + InternalCognitoUser.prototype.enableMFA = callback => { callback(null, 'Success'); }; - CognitoUser.prototype.associateSoftwareToken = callback => { + InternalCognitoUser.prototype.associateSoftwareToken = callback => { callback.associateSecretCode('secretCode'); }; - CognitoUser.prototype.verifySoftwareToken = ( + InternalCognitoUser.prototype.verifySoftwareToken = ( challengeAnswer, device, callback @@ -161,7 +167,7 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { callback.onSuccess('Success'); }; - CognitoUser.prototype.setUserMfaPreference = ( + InternalCognitoUser.prototype.setUserMfaPreference = ( smsMfaSettings, totpMfaSettings, callback @@ -169,31 +175,38 @@ jest.mock('amazon-cognito-identity-js/lib/CognitoUser', () => { callback(null, 'Success'); }; - CognitoUser.prototype.getUserData = callback => { + InternalCognitoUser.prototype.getUserData = callback => { callback(null, { PreferredMfaSetting: 'SMS_MFA', }); }; - CognitoUser.prototype.getUsername = () => { + InternalCognitoUser.prototype.getUsername = () => { return 'testUsername'; }; - CognitoUser.prototype.getSignInUserSession = () => { + InternalCognitoUser.prototype.getSignInUserSession = () => { return session; }; - return CognitoUser; + return { + ...jest.requireActual('amazon-cognito-identity-js/internals'), + InternalCognitoUser, + InternalCognitoUserPool, + }; }); import { AuthClass as Auth } from '../src/Auth'; import { - CognitoUserPool, CognitoUser, CognitoUserSession, CognitoIdToken, CognitoAccessToken, } from 'amazon-cognito-identity-js'; +import { + InternalCognitoUser, + InternalCognitoUserPool, +} from 'amazon-cognito-identity-js/internals'; import { Hub } from '@aws-amplify/core'; import { InternalAuthClass } from '../src/internals/InternalAuth'; @@ -215,7 +228,7 @@ const authOptionsWithNoUserPoolId = { }, }; -const userPool = new CognitoUserPool({ +const userPool = new InternalCognitoUserPool({ UserPoolId: authOptions.Auth.userPoolId, ClientId: authOptions.Auth.userPoolWebClientId, }); @@ -238,7 +251,7 @@ describe('auth unit test', () => { test('happy case', async () => { const auth = new Auth(authOptions); - const spyon = jest.spyOn(CognitoUser.prototype, 'getMFAOptions'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'getMFAOptions'); expect(await auth.getMFAOptions(user)).toBe('mfaOptions'); expect(spyon).toBeCalled(); @@ -249,7 +262,7 @@ describe('auth unit test', () => { const auth = new Auth(authOptions); const spyon = jest - .spyOn(CognitoUser.prototype, 'getMFAOptions') + .spyOn(InternalCognitoUser.prototype, 'getMFAOptions') .mockImplementationOnce(callback => { callback(new Error('err'), null); }); @@ -266,7 +279,7 @@ describe('auth unit test', () => { describe('disableMFA test', () => { test('happy case', async () => { const auth = new Auth(authOptions); - const spyon = jest.spyOn(CognitoUser.prototype, 'disableMFA'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'disableMFA'); expect(await auth.disableSMS(user)).toBe('Success'); expect(spyon).toBeCalled(); @@ -277,7 +290,7 @@ describe('auth unit test', () => { const auth = new Auth(authOptions); const spyon = jest - .spyOn(CognitoUser.prototype, 'disableMFA') + .spyOn(InternalCognitoUser.prototype, 'disableMFA') .mockImplementationOnce(callback => { callback(new Error('err'), null); }); @@ -295,7 +308,7 @@ describe('auth unit test', () => { test('happy case', async () => { const auth = new Auth(authOptions); - const spyon = jest.spyOn(CognitoUser.prototype, 'enableMFA'); + const spyon = jest.spyOn(InternalCognitoUser.prototype, 'enableMFA'); expect(await auth.enableSMS(user)).toBe('Success'); expect(spyon).toBeCalled(); @@ -306,7 +319,7 @@ describe('auth unit test', () => { const auth = new Auth(authOptions); const spyon = jest - .spyOn(CognitoUser.prototype, 'enableMFA') + .spyOn(InternalCognitoUser.prototype, 'enableMFA') .mockImplementationOnce(callback => { callback(new Error('err'), null); }); @@ -324,7 +337,10 @@ describe('auth unit test', () => { test('happy case', async () => { const auth = new Auth(authOptions); - const spyon = jest.spyOn(CognitoUser.prototype, 'associateSoftwareToken'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'associateSoftwareToken' + ); expect(await auth.setupTOTP(user)).toBe('secretCode'); expect(spyon).toBeCalled(); @@ -335,7 +351,7 @@ describe('auth unit test', () => { const auth = new Auth(authOptions); const spyon = jest - .spyOn(CognitoUser.prototype, 'associateSoftwareToken') + .spyOn(InternalCognitoUser.prototype, 'associateSoftwareToken') .mockImplementationOnce(callback => { callback.onFailure('err'); }); @@ -360,8 +376,11 @@ describe('auth unit test', () => { }); happyCaseUser.getSignInUserSession = () => null; - const spyon = jest.spyOn(CognitoUser.prototype, 'verifySoftwareToken'); - const spyon2 = jest.spyOn(CognitoUser.prototype, 'getUsername'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'verifySoftwareToken' + ); + const spyon2 = jest.spyOn(InternalCognitoUser.prototype, 'getUsername'); const hubSpy = jest.spyOn(Hub, 'dispatch'); expect(await auth.verifyTotpToken(happyCaseUser, 'challengeAnswer')).toBe( @@ -384,8 +403,11 @@ describe('auth unit test', () => { Username: 'username', Pool: userPool, }); - const spyon = jest.spyOn(CognitoUser.prototype, 'verifySoftwareToken'); - const spyon2 = jest.spyOn(CognitoUser.prototype, 'getUsername'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'verifySoftwareToken' + ); + const spyon2 = jest.spyOn(InternalCognitoUser.prototype, 'getUsername'); const hubSpy = jest.spyOn(Hub, 'dispatch'); expect(await auth.verifyTotpToken(happyCaseUser, 'challengeAnswer')).toBe( @@ -410,7 +432,7 @@ describe('auth unit test', () => { const auth = new Auth(authOptions); const spyon = jest - .spyOn(CognitoUser.prototype, 'verifySoftwareToken') + .spyOn(InternalCognitoUser.prototype, 'verifySoftwareToken') .mockImplementationOnce((challengeAnswer, device, callback) => { callback.onFailure(new Error('err')); }); @@ -428,7 +450,10 @@ describe('auth unit test', () => { test('happy case', async () => { const auth = new Auth(authOptions); - const spyon = jest.spyOn(CognitoUser.prototype, 'setUserMfaPreference'); + const spyon = jest.spyOn( + InternalCognitoUser.prototype, + 'setUserMfaPreference' + ); const spyon2 = jest .spyOn(InternalAuthClass.prototype, 'getPreferredMFA') .mockImplementationOnce(() => { @@ -447,7 +472,7 @@ describe('auth unit test', () => { const auth = new Auth(authOptions); const spyon = jest - .spyOn(CognitoUser.prototype, 'setUserMfaPreference') + .spyOn(InternalCognitoUser.prototype, 'setUserMfaPreference') .mockImplementationOnce((smsMfaSettings, totpMfaSettings, callback) => { const err = { message: 'User has not verified software token mfa', @@ -493,7 +518,7 @@ describe('auth unit test', () => { const auth = new Auth(authOptions); const spyon = jest - .spyOn(CognitoUser.prototype, 'getUserData') + .spyOn(InternalCognitoUser.prototype, 'getUserData') .mockImplementationOnce(callback => { callback(new Error('err'), null); }); diff --git a/packages/auth/package.json b/packages/auth/package.json index 66a3f2e3bfa..c3d3d77a71f 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -61,7 +61,7 @@ "name": "Auth (top-level class)", "path": "./lib-esm/index.js", "import": "{ Amplify, Auth }", - "limit": "56.38 kB" + "limit": "57.19 kB" } ], "jest": { diff --git a/packages/auth/src/OAuth/OAuth.ts b/packages/auth/src/OAuth/OAuth.ts index 5bf25f6bdd0..a8458c25646 100644 --- a/packages/auth/src/OAuth/OAuth.ts +++ b/packages/auth/src/OAuth/OAuth.ts @@ -117,7 +117,7 @@ export default class OAuth { this._urlOpener(URL, redirectSignIn); } - private async _handleCodeFlow(currentUrl: string) { + private async _handleCodeFlow(currentUrl: string, userAgentValue?: string) { /* Convert URL into an object with parameters as keys { redirect_uri: 'http://localhost:3000/', response_type: 'code', ...} */ const { code } = (parse(currentUrl).query || '') @@ -169,17 +169,12 @@ export default class OAuth { .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`) .join('&'); - const customUserAgentDetails: CustomUserAgentDetails = { - category: Category.Auth, - action: AuthAction.FederatedSignIn, - }; - const { access_token, refresh_token, id_token, error } = await ( (await fetch(oAuthTokenEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded', - [USER_AGENT_HEADER]: getAmplifyUserAgent(customUserAgentDetails), + [USER_AGENT_HEADER]: userAgentValue, }, body, })) as any @@ -217,7 +212,10 @@ export default class OAuth { }; } - public async handleAuthResponse(currentUrl?: string) { + public async handleAuthResponse( + currentUrl?: string, + userAgentValue?: string + ) { try { const urlParams = currentUrl ? ({ @@ -244,7 +242,10 @@ export default class OAuth { `Starting ${this._config.responseType} flow with ${currentUrl}` ); if (this._config.responseType === 'code') { - return { ...(await this._handleCodeFlow(currentUrl)), state }; + return { + ...(await this._handleCodeFlow(currentUrl, userAgentValue)), + state, + }; } else { return { ...(await this._handleImplicitFlow(currentUrl)), state }; } diff --git a/packages/auth/src/internals/InternalAuth.ts b/packages/auth/src/internals/InternalAuth.ts index b25bfc6a90e..3584e5b78a4 100644 --- a/packages/auth/src/internals/InternalAuth.ts +++ b/packages/auth/src/internals/InternalAuth.ts @@ -25,9 +25,11 @@ import { import { Amplify, + AuthAction, ConsoleLogger as Logger, Credentials, CustomUserAgentDetails, + getAmplifyUserAgent, Hub, StorageHelper, ICredentials, @@ -40,7 +42,6 @@ import { } from '@aws-amplify/core'; import { CookieStorage, - CognitoUserPool, AuthenticationDetails, ICognitoUserPoolData, ICognitoUserData, @@ -60,6 +61,8 @@ import { import { addAuthCategoryToCognitoUserAgent, addFrameworkToCognitoUserAgent, + InternalCognitoUser, + InternalCognitoUserPool, } from 'amazon-cognito-identity-js/internals'; import { parse } from 'url'; @@ -72,6 +75,7 @@ import { CognitoHostedUIIdentityProvider, IAuthDevice, } from '../types/Auth'; +import { getAuthUserAgentDetails, getAuthUserAgentValue } from '../utils'; const logger = new Logger('AuthClass'); const USER_ADMIN_SCOPE = 'aws.cognito.signin.user.admin'; @@ -101,7 +105,7 @@ const MAX_AUTOSIGNIN_POLLING_MS = 3 * 60 * 1000; */ export class InternalAuthClass { private _config: AuthOptions; - private userPool: CognitoUserPool = null; + private userPool: InternalCognitoUserPool = null; private user: any = null; private _oAuthHandler: OAuth; private _storage; @@ -205,7 +209,7 @@ export class InternalAuthClass { }; userPoolData.Storage = this._storage; - this.userPool = new CognitoUserPool( + this.userPool = new InternalCognitoUserPool( userPoolData, this.wrapRefreshSessionCallback ); @@ -408,6 +412,10 @@ export class InternalAuthClass { logger.debug('signUp validation data:', validationData); return new Promise((resolve, reject) => { + const userAgentDetails = getAuthUserAgentDetails( + AuthAction.SignUp, + customUserAgentDetails + ); this.userPool.signUp( username, password, @@ -433,13 +441,15 @@ export class InternalAuthClass { password, autoSignInValidationData, autoSignInClientMetaData, - data + data, + userAgentDetails ); } resolve(data); } }, - clientMetadata + clientMetadata, + getAmplifyUserAgent(userAgentDetails) ); }); } @@ -449,7 +459,8 @@ export class InternalAuthClass { password: string, validationData: {}, clientMetadata: any, - data: any + data: any, + customUserAgentDetails: CustomUserAgentDetails ) { this.autoSignInInitiated = true; const authDetails = new AuthenticationDetails({ @@ -459,24 +470,34 @@ export class InternalAuthClass { ClientMetadata: clientMetadata, }); if (data.userConfirmed) { - this.signInAfterUserConfirmed(authDetails); + this.signInAfterUserConfirmed(authDetails, customUserAgentDetails); } else if (this._config.signUpVerificationMethod === 'link') { - this.handleLinkAutoSignIn(authDetails); + this.handleLinkAutoSignIn(authDetails, customUserAgentDetails); } else { - this.handleCodeAutoSignIn(authDetails); + this.handleCodeAutoSignIn(authDetails, customUserAgentDetails); } } - private handleCodeAutoSignIn(authDetails: AuthenticationDetails) { + private handleCodeAutoSignIn( + authDetails: AuthenticationDetails, + customUserAgentDetails: CustomUserAgentDetails + ) { const listenEvent = ({ payload }) => { if (payload.event === 'confirmSignUp') { - this.signInAfterUserConfirmed(authDetails, listenEvent); + this.signInAfterUserConfirmed( + authDetails, + customUserAgentDetails, + listenEvent + ); } }; Hub.listen('auth', listenEvent); } - private handleLinkAutoSignIn(authDetails: AuthenticationDetails) { + private handleLinkAutoSignIn( + authDetails: AuthenticationDetails, + customUserAgentDetails: CustomUserAgentDetails + ) { this._storage.setItem('amplify-polling-started', 'true'); const start = Date.now(); const autoSignInPollingIntervalId = setInterval(() => { @@ -491,7 +512,8 @@ export class InternalAuthClass { } else { this.signInAfterUserConfirmed( authDetails, - null, + customUserAgentDetails, + undefined, autoSignInPollingIntervalId ); } @@ -500,6 +522,7 @@ export class InternalAuthClass { private async signInAfterUserConfirmed( authDetails: AuthenticationDetails, + customUserAgentDetails: CustomUserAgentDetails, listenEvent?: HubCallback, autoSignInPollingIntervalId?: ReturnType ) { @@ -527,8 +550,10 @@ export class InternalAuthClass { error => { logger.error(error); this._storage.removeItem('amplify-auto-sign-in'); - } - ) + }, + customUserAgentDetails + ), + getAmplifyUserAgent(customUserAgentDetails) ); } catch (error) { logger.error(error); @@ -596,7 +621,8 @@ export class InternalAuthClass { resolve(data); } }, - clientMetadata + clientMetadata, + getAuthUserAgentValue(AuthAction.ConfirmSignUp, customUserAgentDetails) ); }); } @@ -627,13 +653,17 @@ export class InternalAuthClass { const user = this.createCognitoUser(username); return new Promise((resolve, reject) => { - user.resendConfirmationCode((err, data) => { - if (err) { - reject(err); - } else { - resolve(data); - } - }, clientMetadata); + user.resendConfirmationCode( + (err, data) => { + if (err) { + reject(err); + } else { + resolve(data); + } + }, + clientMetadata, + getAuthUserAgentValue(AuthAction.ResendSignUp, customUserAgentDetails) + ); }); } @@ -683,24 +713,29 @@ export class InternalAuthClass { ValidationData: validationData, ClientMetadata: clientMetadata, }); + const userAgentDetails = getAuthUserAgentDetails( + AuthAction.SignIn, + customUserAgentDetails + ); if (password) { - return this.signInWithPassword(authDetails); + return this.signInWithPassword(authDetails, userAgentDetails); } else { - return this.signInWithoutPassword(authDetails); + return this.signInWithoutPassword(authDetails, userAgentDetails); } } /** * Return an object with the authentication callbacks - * @param {CognitoUser} user - the cognito user object + * @param {InternalCognitoUser} user - the cognito user object * @param {} resolve - function called when resolving the current step * @param {} reject - function called when rejecting the current step * @return - an object with the callback methods for user authentication */ private authCallbacks( - user: CognitoUser, - resolve: (value?: CognitoUser | any) => void, - reject: (value?: any) => void + user: InternalCognitoUser, + resolve: (value?: InternalCognitoUser | any) => void, + reject: (value?: any) => void, + customUserAgentDetails: CustomUserAgentDetails ): IAuthenticationCallback { const that = this; return { @@ -718,7 +753,10 @@ export class InternalAuthClass { try { // In order to get user attributes and MFA methods // We need to trigger currentUserPoolUser again - const currentUser = await this.currentUserPoolUser(); + const currentUser = await this._currentUserPoolUser( + undefined, + customUserAgentDetails + ); that.user = currentUser; dispatchAuthEvent( 'signIn', @@ -787,11 +825,13 @@ export class InternalAuthClass { * Sign in with a password * @private * @param {AuthenticationDetails} authDetails - the user sign in data + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details * @return - A promise resolves the CognitoUser object if success or mfa required */ private signInWithPassword( - authDetails: AuthenticationDetails - ): Promise { + authDetails: AuthenticationDetails, + customUserAgentDetails: CustomUserAgentDetails + ): Promise { if (this.pendingSignIn) { throw new Error('Pending sign-in attempt already in progress'); } @@ -810,8 +850,10 @@ export class InternalAuthClass { error => { this.pendingSignIn = null; reject(error); - } - ) + }, + customUserAgentDetails + ), + getAmplifyUserAgent(customUserAgentDetails) ); }); @@ -822,16 +864,22 @@ export class InternalAuthClass { * Sign in without a password * @private * @param {AuthenticationDetails} authDetails - the user sign in data - * @return - A promise resolves the CognitoUser object if success or mfa required + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves the InternalCognitoUser object if success or mfa required */ private signInWithoutPassword( - authDetails: AuthenticationDetails - ): Promise { + authDetails: AuthenticationDetails, + customUserAgentDetails: CustomUserAgentDetails + ): Promise { const user = this.createCognitoUser(authDetails.getUsername()); user.setAuthenticationFlowType('CUSTOM_AUTH'); return new Promise((resolve, reject) => { - user.initiateAuth(authDetails, this.authCallbacks(user, resolve, reject)); + user.initiateAuth( + authDetails, + this.authCallbacks(user, resolve, reject, customUserAgentDetails), + getAmplifyUserAgent(customUserAgentDetails) + ); }); } @@ -848,8 +896,10 @@ export class InternalAuthClass { user: CognitoUser | any, customUserAgentDetails?: CustomUserAgentDetails ): Promise { + const internalUser: InternalCognitoUser | any = user; + return new Promise((res, rej) => { - user.getMFAOptions((err, mfaOptions) => { + internalUser.getMFAOptions((err, mfaOptions) => { if (err) { logger.debug('get MFA Options failed', err); rej(err); @@ -858,7 +908,7 @@ export class InternalAuthClass { logger.debug('get MFA options success', mfaOptions); res(mfaOptions); return; - }); + }, getAuthUserAgentValue(AuthAction.GetMFAOptions, customUserAgentDetails)); }); } @@ -873,18 +923,23 @@ export class InternalAuthClass { params?: GetPreferredMFAOpts, customUserAgentDetails?: CustomUserAgentDetails ): Promise { + const internalUser: InternalCognitoUser | any = user; const that = this; return new Promise((res, rej) => { const clientMetadata = this._config.clientMetadata; // TODO: verify behavior if this is override during signIn const bypassCache = params ? params.bypassCache : false; - user.getUserData( + const userAgentValue = getAuthUserAgentValue( + AuthAction.GetPreferredMFA, + customUserAgentDetails + ); + internalUser.getUserData( async (err, data) => { if (err) { logger.debug('getting preferred mfa failed', err); if (this.isSessionInvalid(err)) { try { - await this.cleanUpInvalidSession(user); + await this.cleanUpInvalidSession(user, userAgentValue); } catch (cleanUpError) { rej( new Error( @@ -907,7 +962,8 @@ export class InternalAuthClass { return; } }, - { bypassCache, clientMetadata } + { bypassCache, clientMetadata }, + userAgentValue ); }); } @@ -942,29 +998,37 @@ export class InternalAuthClass { return ret; } - private _getUserData(user, params) { + private _getUserData( + user: InternalCognitoUser, + params, + userAgentValue: string + ) { return new Promise((res, rej) => { - user.getUserData(async (err, data) => { - if (err) { - logger.debug('getting user data failed', err); - if (this.isSessionInvalid(err)) { - try { - await this.cleanUpInvalidSession(user); - } catch (cleanUpError) { - rej( - new Error( - `Session is invalid due to: ${err.message} and failed to clean up invalid session: ${cleanUpError.message}` - ) - ); - return; + user.getUserData( + async (err, data) => { + if (err) { + logger.debug('getting user data failed', err); + if (this.isSessionInvalid(err)) { + try { + await this.cleanUpInvalidSession(user, userAgentValue); + } catch (cleanUpError) { + rej( + new Error( + `Session is invalid due to: ${err.message} and failed to clean up invalid session: ${cleanUpError.message}` + ) + ); + return; + } } + rej(err); + return; + } else { + res(data); } - rej(err); - return; - } else { - res(data); - } - }, params); + }, + params, + userAgentValue + ); }); } @@ -980,12 +1044,21 @@ export class InternalAuthClass { mfaMethod: 'TOTP' | 'SMS' | 'NOMFA' | 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA', customUserAgentDetails?: CustomUserAgentDetails ): Promise { + const internalUser: InternalCognitoUser | any = user; + const userAgentValue = getAuthUserAgentValue( + AuthAction.SetPreferredMFA, + customUserAgentDetails + ); const clientMetadata = this._config.clientMetadata; // TODO: verify behavior if this is override during signIn - const userData = await this._getUserData(user, { - bypassCache: true, - clientMetadata, - }); + const userData = await this._getUserData( + user, + { + bypassCache: true, + clientMetadata, + }, + userAgentValue + ); let smsMfaSettings = null; let totpMfaSettings = null; @@ -1048,7 +1121,7 @@ export class InternalAuthClass { const that = this; return new Promise((res, rej) => { - user.setUserMfaPreference( + internalUser.setUserMfaPreference( smsMfaSettings, totpMfaSettings, (err, result) => { @@ -1059,13 +1132,13 @@ export class InternalAuthClass { logger.debug('Set user mfa success', result); logger.debug('Caching the latest user data into local'); // cache the latest result into user data - user.getUserData( + internalUser.getUserData( async (err, data) => { if (err) { logger.debug('getting user data failed', err); if (this.isSessionInvalid(err)) { try { - await this.cleanUpInvalidSession(user); + await this.cleanUpInvalidSession(user, userAgentValue); } catch (cleanUpError) { rej( new Error( @@ -1083,9 +1156,11 @@ export class InternalAuthClass { { bypassCache: true, clientMetadata, - } + }, + userAgentValue ); - } + }, + userAgentValue ); }); } @@ -1101,8 +1176,10 @@ export class InternalAuthClass { user: CognitoUser, customUserAgentDetails?: CustomUserAgentDetails ): Promise { + const internalUser = user as InternalCognitoUser; + return new Promise((res, rej) => { - user.disableMFA((err, data) => { + internalUser.disableMFA((err, data) => { if (err) { logger.debug('disable mfa failed', err); rej(err); @@ -1111,7 +1188,7 @@ export class InternalAuthClass { logger.debug('disable mfa succeed', data); res(data); return; - }); + }, getAuthUserAgentValue(AuthAction.DisableSMS, customUserAgentDetails)); }); } @@ -1126,8 +1203,10 @@ export class InternalAuthClass { user: CognitoUser, customUserAgentDetails?: CustomUserAgentDetails ): Promise { + const internalUser = user as InternalCognitoUser; + return new Promise((res, rej) => { - user.enableMFA((err, data) => { + internalUser.enableMFA((err, data) => { if (err) { logger.debug('enable mfa failed', err); rej(err); @@ -1136,7 +1215,7 @@ export class InternalAuthClass { logger.debug('enable mfa succeed', data); res(data); return; - }); + }, getAuthUserAgentValue(AuthAction.EnableSMS, customUserAgentDetails)); }); } @@ -1150,19 +1229,24 @@ export class InternalAuthClass { user: CognitoUser | any, customUserAgentDetails?: CustomUserAgentDetails ): Promise { + const internalUser: InternalCognitoUser | any = user; + return new Promise((res, rej) => { - user.associateSoftwareToken({ - onFailure: err => { - logger.debug('associateSoftwareToken failed', err); - rej(err); - return; - }, - associateSecretCode: secretCode => { - logger.debug('associateSoftwareToken success', secretCode); - res(secretCode); - return; + internalUser.associateSoftwareToken( + { + onFailure: err => { + logger.debug('associateSoftwareToken failed', err); + rej(err); + return; + }, + associateSecretCode: secretCode => { + logger.debug('associateSoftwareToken success', secretCode); + res(secretCode); + return; + }, }, - }); + getAuthUserAgentValue(AuthAction.SetupTOTP, customUserAgentDetails) + ); }); } @@ -1179,38 +1263,50 @@ export class InternalAuthClass { customUserAgentDetails?: CustomUserAgentDetails ): Promise { logger.debug('verification totp token', user, challengeAnswer); + const internalUser: InternalCognitoUser | any = user; let signInUserSession; - if (user && typeof user.getSignInUserSession === 'function') { - signInUserSession = (user as CognitoUser).getSignInUserSession(); + if ( + internalUser && + typeof internalUser.getSignInUserSession === 'function' + ) { + signInUserSession = (user as InternalCognitoUser).getSignInUserSession(); } const isLoggedIn = signInUserSession?.isValid(); return new Promise((res, rej) => { - user.verifySoftwareToken(challengeAnswer, 'My TOTP device', { - onFailure: err => { - logger.debug('verifyTotpToken failed', err); - rej(err); - return; - }, - onSuccess: data => { - if (!isLoggedIn) { + internalUser.verifySoftwareToken( + challengeAnswer, + 'My TOTP device', + { + onFailure: err => { + logger.debug('verifyTotpToken failed', err); + rej(err); + return; + }, + onSuccess: data => { + if (!isLoggedIn) { + dispatchAuthEvent( + 'signIn', + internalUser, + `A user ${internalUser.getUsername()} has been signed in` + ); + } dispatchAuthEvent( - 'signIn', - user, - `A user ${user.getUsername()} has been signed in` + 'verify', + internalUser, + `A user ${internalUser.getUsername()} has been verified` ); - } - dispatchAuthEvent( - 'verify', - user, - `A user ${user.getUsername()} has been verified` - ); - logger.debug('verifyTotpToken success', data); - res(data); - return; + logger.debug('verifyTotpToken success', data); + res(data); + return; + }, }, - }); + getAuthUserAgentValue( + AuthAction.VerifyTotpToken, + customUserAgentDetails + ) + ); }); } @@ -1229,13 +1325,19 @@ export class InternalAuthClass { clientMetadata: ClientMetaData = this._config.clientMetadata, customUserAgentDetails?: CustomUserAgentDetails ): Promise { + const internalUser: InternalCognitoUser | any = user; + if (!code) { return this.rejectAuthError(AuthErrorTypes.EmptyCode); } const that = this; + const userAgentDetails = getAuthUserAgentDetails( + AuthAction.ConfirmSignIn, + customUserAgentDetails + ); return new Promise((resolve, reject) => { - user.sendMFACode( + internalUser.sendMFACode( code, { onSuccess: async session => { @@ -1247,19 +1349,24 @@ export class InternalAuthClass { } catch (e) { logger.debug('cannot get cognito credentials', e); } finally { - that.user = user; + that.user = internalUser; try { - const currentUser = await this.currentUserPoolUser(); - user.attributes = currentUser.attributes; + const currentUser = await this._currentUserPoolUser( + undefined, + userAgentDetails + ); + Object.assign(internalUser, { + attributes: currentUser.attributes, + }); } catch (e) { logger.debug('cannot get updated Cognito User', e); } dispatchAuthEvent( 'signIn', - user, - `A user ${user.getUsername()} has been signed in` + internalUser, + `A user ${internalUser.getUsername()} has been signed in` ); - resolve(user); + resolve(internalUser); } }, onFailure: err => { @@ -1268,7 +1375,8 @@ export class InternalAuthClass { }, }, mfaType, - clientMetadata + clientMetadata, + getAmplifyUserAgent(userAgentDetails) ); }); } @@ -1280,13 +1388,15 @@ export class InternalAuthClass { clientMetadata: ClientMetaData = this._config.clientMetadata, customUserAgentDetails?: CustomUserAgentDetails ): Promise { + const internalUser: InternalCognitoUser | any = user; + if (!password) { return this.rejectAuthError(AuthErrorTypes.EmptyPassword); } const that = this; return new Promise((resolve, reject) => { - user.completeNewPasswordChallenge( + internalUser.completeNewPasswordChallenge( password, requiredAttributes, { @@ -1299,13 +1409,13 @@ export class InternalAuthClass { } catch (e) { logger.debug('cannot get cognito credentials', e); } finally { - that.user = user; + that.user = internalUser; dispatchAuthEvent( 'signIn', - user, - `A user ${user.getUsername()} has been signed in` + internalUser, + `A user ${internalUser.getUsername()} has been signed in` ); - resolve(user); + resolve(internalUser); } }, onFailure: err => { @@ -1319,24 +1429,28 @@ export class InternalAuthClass { }, mfaRequired: (challengeName, challengeParam) => { logger.debug('signIn MFA required'); - user['challengeName'] = challengeName; - user['challengeParam'] = challengeParam; - resolve(user); + internalUser['challengeName'] = challengeName; + internalUser['challengeParam'] = challengeParam; + resolve(internalUser); }, mfaSetup: (challengeName, challengeParam) => { logger.debug('signIn mfa setup', challengeName); - user['challengeName'] = challengeName; - user['challengeParam'] = challengeParam; - resolve(user); + internalUser['challengeName'] = challengeName; + internalUser['challengeParam'] = challengeParam; + resolve(internalUser); }, totpRequired: (challengeName, challengeParam) => { logger.debug('signIn mfa setup', challengeName); - user['challengeName'] = challengeName; - user['challengeParam'] = challengeParam; - resolve(user); + internalUser['challengeName'] = challengeName; + internalUser['challengeParam'] = challengeParam; + resolve(internalUser); }, }, - clientMetadata + clientMetadata, + getAuthUserAgentValue( + AuthAction.CompleteNewPassword, + customUserAgentDetails + ) ); }); } @@ -1347,6 +1461,7 @@ export class InternalAuthClass { * @param {String} challengeResponses - The confirmation code * @param {ClientMetaData} clientMetadata - optional client metadata defaults to config * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * */ public sendCustomChallengeAnswer( user: CognitoUser | any, @@ -1354,6 +1469,8 @@ export class InternalAuthClass { clientMetadata: ClientMetaData = this._config.clientMetadata, customUserAgentDetails?: CustomUserAgentDetails ): Promise { + const internalUser: InternalCognitoUser | any = user; + if (!this.userPool) { return this.rejectNoUserPool(); } @@ -1362,11 +1479,16 @@ export class InternalAuthClass { } const that = this; + const userAgentDetails = getAuthUserAgentDetails( + AuthAction.SendCustomChallengeAnswer, + customUserAgentDetails + ); return new Promise((resolve, reject) => { - user.sendCustomChallengeAnswer( + internalUser.sendCustomChallengeAnswer( challengeResponses, - this.authCallbacks(user, resolve, reject), - clientMetadata + this.authCallbacks(internalUser, resolve, reject, userAgentDetails), + clientMetadata, + getAmplifyUserAgent(userAgentDetails) ); }); } @@ -1383,16 +1505,25 @@ export class InternalAuthClass { attributeNames: string[], customUserAgentDetails?: CustomUserAgentDetails ) { + const internalUser: InternalCognitoUser | any = user; const that = this; + const userAgentValue = getAuthUserAgentValue( + AuthAction.DeleteUserAttributes, + customUserAgentDetails + ); return new Promise((resolve, reject) => { - that.userSession(user).then(session => { - user.deleteAttributes(attributeNames, (err, result) => { - if (err) { - return reject(err); - } else { - return resolve(result); - } - }); + that._userSession(userAgentValue, internalUser).then(session => { + internalUser.deleteAttributes( + attributeNames, + (err, result) => { + if (err) { + return reject(err); + } else { + return resolve(result); + } + }, + userAgentValue + ); }); }); } @@ -1419,18 +1550,26 @@ export class InternalAuthClass { return new Promise(async (res, rej) => { if (this.userPool) { - const user = this.userPool.getCurrentUser(); + const internalUser = + this.userPool.getCurrentUser() as InternalCognitoUser; - if (!user) { + if (!internalUser) { logger.debug('Failed to get user from user pool'); return rej(new Error('No current user.')); } else { - user.getSession(async (err, session) => { + const userAgentValue = getAuthUserAgentValue( + AuthAction.DeleteUser, + customUserAgentDetails + ); + internalUser.getSession(async (err, session) => { if (err) { logger.debug('Failed to get the user session', err); if (this.isSessionInvalid(err)) { try { - await this.cleanUpInvalidSession(user); + await this.cleanUpInvalidSession( + internalUser, + userAgentValue + ); } catch (cleanUpError) { rej( new Error( @@ -1442,36 +1581,40 @@ export class InternalAuthClass { } return rej(err); } else { - user.deleteUser((err, result: string) => { - if (err) { - rej(err); - } else { - dispatchAuthEvent( - 'userDeleted', - result, - 'The authenticated user has been deleted.' - ); - user.signOut(); - this.user = null; - try { - this.cleanCachedItems(); // clean aws credentials - } catch (e) { - // TODO: change to rejects in refactor - logger.debug('failed to clear cached items'); - } - - if (isSignedInHostedUI) { - this.oAuthSignOutRedirect(res, rej); + internalUser.deleteUser( + (err, result: string) => { + if (err) { + rej(err); } else { dispatchAuthEvent( - 'signOut', - this.user, - `A user has been signed out` + 'userDeleted', + result, + 'The authenticated user has been deleted.' ); - res(result); + internalUser.signOut(undefined, userAgentValue); + this.user = null; + try { + this.cleanCachedItems(); // clean aws credentials + } catch (e) { + // TODO: change to rejects in refactor + logger.debug('failed to clear cached items'); + } + + if (isSignedInHostedUI) { + this.oAuthSignOutRedirect(res, rej); + } else { + dispatchAuthEvent( + 'signOut', + this.user, + `A user has been signed out` + ); + res(result); + } } - } - }); + }, + undefined, + userAgentValue + ); } }); } @@ -1496,10 +1639,15 @@ export class InternalAuthClass { clientMetadata: ClientMetaData = this._config.clientMetadata, customUserAgentDetails?: CustomUserAgentDetails ): Promise { + const internalUser: InternalCognitoUser | any = user; const attributeList: ICognitoUserAttributeData[] = []; const that = this; + const userAgentValue = getAuthUserAgentValue( + AuthAction.UpdateUserAttributes, + customUserAgentDetails + ); return new Promise((resolve, reject) => { - that.userSession(user).then(session => { + that._userSession(userAgentValue, internalUser).then(session => { for (const key in attributes) { if (key !== 'sub' && key.indexOf('_verified') < 0) { const attr: ICognitoUserAttributeData = { @@ -1509,7 +1657,7 @@ export class InternalAuthClass { attributeList.push(attr); } } - user.updateAttributes( + internalUser.updateAttributes( attributeList, (err, result, details) => { if (err) { @@ -1532,7 +1680,8 @@ export class InternalAuthClass { return resolve(result); } }, - clientMetadata + clientMetadata, + userAgentValue ); }); }); @@ -1568,15 +1717,27 @@ export class InternalAuthClass { user: CognitoUser | any, customUserAgentDetails?: CustomUserAgentDetails ): Promise { + return this._userAttributes(user, customUserAgentDetails); + } + + private _userAttributes( + user: CognitoUser | any, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + const internalUser: InternalCognitoUser | any = user; + const userAgentValue = getAuthUserAgentValue( + AuthAction.UserAttributes, + customUserAgentDetails + ); return new Promise((resolve, reject) => { - this.userSession(user).then(session => { - user.getUserAttributes((err, attributes) => { + this._userSession(userAgentValue, internalUser).then(session => { + internalUser.getUserAttributes((err, attributes) => { if (err) { reject(err); } else { resolve(attributes); } - }); + }, userAgentValue); }); }); } @@ -1586,7 +1747,13 @@ export class InternalAuthClass { customUserAgentDetails?: CustomUserAgentDetails ) { const that = this; - return this.userAttributes(user).then(attributes => { + return this._userAttributes( + user, + getAuthUserAgentDetails( + AuthAction.VerifiedContact, + customUserAgentDetails + ) + ).then(attributes => { const attrs = that.attributesToObject(attributes); const unverified = {}; const verified = {}; @@ -1687,8 +1854,11 @@ export class InternalAuthClass { ); } - private async cleanUpInvalidSession(user: CognitoUser) { - user.signOut(); + private async cleanUpInvalidSession( + internalUser: InternalCognitoUser, + userAgentValue: string + ) { + internalUser.signOut(undefined, userAgentValue); this.user = null; try { await this.cleanCachedItems(); // clean aws credentials @@ -1713,6 +1883,13 @@ export class InternalAuthClass { public currentUserPoolUser( params?: CurrentUserOpts, customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + return this._currentUserPoolUser(params, customUserAgentDetails); + } + + private _currentUserPoolUser( + params?: CurrentUserOpts, + customUserAgentDetails?: CustomUserAgentDetails ): Promise { if (!this.userPool) { return this.rejectNoUserPool(); @@ -1753,9 +1930,10 @@ export class InternalAuthClass { }); } - const user = this.userPool.getCurrentUser(); + const internalUser = + this.userPool.getCurrentUser() as InternalCognitoUser; - if (!user) { + if (!internalUser) { logger.debug('Failed to get user from user pool'); rej('No current user'); return; @@ -1763,7 +1941,14 @@ export class InternalAuthClass { // refresh the session if the session expired. try { - const session = await this._userSession(user); + const userAgentValue = getAuthUserAgentValue( + AuthAction.CurrentUserPoolUser, + customUserAgentDetails + ); + const session = await this._userSession( + userAgentValue, + internalUser + ); // get user data from Cognito const bypassCache = params ? params.bypassCache : false; @@ -1777,13 +1962,16 @@ export class InternalAuthClass { // validate the token's scope first before calling this function const { scope = '' } = session.getAccessToken().decodePayload(); if (scope.split(' ').includes(USER_ADMIN_SCOPE)) { - user.getUserData( + internalUser.getUserData( async (err, data) => { if (err) { logger.debug('getting user data failed', err); if (this.isSessionInvalid(err)) { try { - await this.cleanUpInvalidSession(user); + await this.cleanUpInvalidSession( + internalUser, + userAgentValue + ); } catch (cleanUpError) { rej( new Error( @@ -1794,12 +1982,12 @@ export class InternalAuthClass { } rej(err); } else { - res(user); + res(internalUser); } return; } const preferredMFA = data.PreferredMfaSetting || 'NOMFA'; - const attributeList = []; + const attributeList: CognitoUserAttribute[] = []; for (let i = 0; i < data.UserAttributes.length; i++) { const attribute = { @@ -1811,17 +1999,18 @@ export class InternalAuthClass { } const attributes = this.attributesToObject(attributeList); - Object.assign(user, { attributes, preferredMFA }); - return res(user); + Object.assign(internalUser, { attributes, preferredMFA }); + return res(internalUser); }, - { bypassCache, clientMetadata } + { bypassCache, clientMetadata }, + userAgentValue ); } else { logger.debug( `Unable to get the user data because the ${USER_ADMIN_SCOPE} ` + `is not in the scopes of the access token` ); - return res(user); + return res(internalUser); } } catch (err) { rej(err); @@ -1844,7 +2033,14 @@ export class InternalAuthClass { * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details * @return - A promise resolves to current authenticated CognitoUser if success */ - public async currentAuthenticatedUser( + public currentAuthenticatedUser( + params?: CurrentUserOpts, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + return this._currentAuthenticatedUser(params, customUserAgentDetails); + } + + private async _currentAuthenticatedUser( params?: CurrentUserOpts, customUserAgentDetails?: CustomUserAgentDetails ): Promise { @@ -1879,7 +2075,13 @@ export class InternalAuthClass { logger.debug('get current authenticated userpool user'); let user = null; try { - user = await this.currentUserPoolUser(params); + user = await this._currentUserPoolUser( + params, + getAuthUserAgentDetails( + AuthAction.CurrentAuthenticatedUser, + customUserAgentDetails + ) + ); } catch (e) { if (e === 'No userPool') { logger.error( @@ -1902,8 +2104,18 @@ export class InternalAuthClass { */ public currentSession( customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + return this._currentSession(customUserAgentDetails); + } + + private _currentSession( + customUserAgentDetails?: CustomUserAgentDetails ): Promise { const that = this; + const userAgentDetails = getAuthUserAgentDetails( + AuthAction.CurrentSession, + customUserAgentDetails + ); logger.debug('Getting current session'); // Purposely not calling the reject method here because we don't need a console error if (!this.userPool) { @@ -1912,10 +2124,10 @@ export class InternalAuthClass { return new Promise((res, rej) => { that - .currentUserPoolUser() + ._currentUserPoolUser(undefined, userAgentDetails) .then(user => { that - .userSession(user) + ._userSession(getAmplifyUserAgent(userAgentDetails), user) .then(session => { res(session); return; @@ -1934,8 +2146,11 @@ export class InternalAuthClass { }); } - private async _userSession(user?: CognitoUser): Promise { - if (!user) { + private async _userSession( + userAgentValue: string, + internalUser?: InternalCognitoUser + ): Promise { + if (!internalUser) { logger.debug('the user is null'); return this.rejectAuthError(AuthErrorTypes.NoUserSession); } @@ -1945,13 +2160,19 @@ export class InternalAuthClass { if (this.inflightSessionPromiseCounter === 0) { this.inflightSessionPromise = new Promise( (res, rej) => { - user.getSession( + internalUser.getSession( async (err, session) => { if (err) { - logger.debug('Failed to get the session from user', user); + logger.debug( + 'Failed to get the session from user', + internalUser + ); if (this.isSessionInvalid(err)) { try { - await this.cleanUpInvalidSession(user); + await this.cleanUpInvalidSession( + internalUser, + userAgentValue + ); } catch (cleanUpError) { rej( new Error( @@ -1969,7 +2190,8 @@ export class InternalAuthClass { return; } }, - { clientMetadata } + { clientMetadata }, + userAgentValue ); } ); @@ -1980,7 +2202,7 @@ export class InternalAuthClass { const userSession = await this.inflightSessionPromise; // Set private member. Avoid user.setSignInUserSession() to prevent excessive localstorage refresh. // @ts-ignore - user.signInUserSession = userSession; + internalUser.signInUserSession = userSession; return userSession!; } finally { this.inflightSessionPromiseCounter--; @@ -1997,7 +2219,10 @@ export class InternalAuthClass { user, customUserAgentDetails?: CustomUserAgentDetails ): Promise { - return this._userSession(user); + return this._userSession( + getAuthUserAgentValue(AuthAction.UserSession, customUserAgentDetails), + user + ); } /** @@ -2031,7 +2256,12 @@ export class InternalAuthClass { // refresh the jwt token here if necessary return this.Credentials.refreshFederatedToken(federatedInfo); } else { - return this.currentSession() + return this._currentSession( + getAuthUserAgentDetails( + AuthAction.CurrentUserCredentials, + customUserAgentDetails + ) + ) .then(session => { logger.debug('getting session success', session); return this.Credentials.set(session, 'session'); @@ -2054,6 +2284,8 @@ export class InternalAuthClass { * Initiate an attribute confirmation request * @param {Object} user - The CognitoUser * @param {Object} attr - The attributes to be verified + * @param {ClientMetaData} clientMetadata - optional client metadata, defaults to config + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details * @return - A promise resolves to callback data if success */ public verifyUserAttribute( @@ -2062,8 +2294,24 @@ export class InternalAuthClass { clientMetadata: ClientMetaData = this._config.clientMetadata, customUserAgentDetails?: CustomUserAgentDetails ): Promise { + return this._verifyUserAttribute( + user, + attr, + clientMetadata, + customUserAgentDetails + ); + } + + private _verifyUserAttribute( + user: CognitoUser | any, + attr: string, + clientMetadata: ClientMetaData = this._config.clientMetadata, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + const internalUser: InternalCognitoUser | any = user; + return new Promise((resolve, reject) => { - user.getAttributeVerificationCode( + internalUser.getAttributeVerificationCode( attr, { onSuccess(success) { @@ -2073,7 +2321,11 @@ export class InternalAuthClass { return reject(err); }, }, - clientMetadata + clientMetadata, + getAuthUserAgentValue( + AuthAction.VerifyUserAttribute, + customUserAgentDetails + ) ); }); } @@ -2091,22 +2343,45 @@ export class InternalAuthClass { attr: string, code: string, customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + return this._verifyUserAttributeSubmit( + user, + attr, + code, + customUserAgentDetails + ); + } + + private _verifyUserAttributeSubmit( + user: CognitoUser | any, + attr: string, + code: string, + customUserAgentDetails?: CustomUserAgentDetails ): Promise { if (!code) { return this.rejectAuthError(AuthErrorTypes.EmptyCode); } + const internalUser: InternalCognitoUser | any = user; return new Promise((resolve, reject) => { - user.verifyAttribute(attr, code, { - onSuccess(data) { - resolve(data); - return; - }, - onFailure(err) { - reject(err); - return; + internalUser.verifyAttribute( + attr, + code, + { + onSuccess(data) { + resolve(data); + return; + }, + onFailure(err) { + reject(err); + return; + }, }, - }); + getAuthUserAgentValue( + AuthAction.VerifyUserAttributeSubmit, + customUserAgentDetails + ) + ); }); } @@ -2114,10 +2389,16 @@ export class InternalAuthClass { attr: string, customUserAgentDetails?: CustomUserAgentDetails ): Promise { + const userAgentDetails = getAuthUserAgentDetails( + AuthAction.VerifyCurrentUserAttribute, + customUserAgentDetails + ); const that = this; return that - .currentUserPoolUser() - .then(user => that.verifyUserAttribute(user, attr)); + ._currentUserPoolUser(undefined, userAgentDetails) + .then(user => + that._verifyUserAttribute(user, attr, undefined, userAgentDetails) + ); } /** @@ -2132,15 +2413,22 @@ export class InternalAuthClass { code: string, customUserAgentDetails?: CustomUserAgentDetails ): Promise { + const userAgentDetails = getAuthUserAgentDetails( + AuthAction.VerifyCurrentUserAttributeSubmit, + customUserAgentDetails + ); const that = this; return that - .currentUserPoolUser() - .then(user => that.verifyUserAttributeSubmit(user, attr, code)); + ._currentUserPoolUser(undefined, userAgentDetails) + .then(user => + that._verifyUserAttributeSubmit(user, attr, code, userAgentDetails) + ); } private async cognitoIdentitySignOut( opts: SignOutOpts, - user: CognitoUser | any + internalUser: InternalCognitoUser | any, + userAgentValue: string ) { try { await this._storageSync; @@ -2155,18 +2443,21 @@ export class InternalAuthClass { return new Promise((res, rej) => { if (opts && opts.global) { - logger.debug('user global sign out', user); + logger.debug('user global sign out', internalUser); // in order to use global signout // we must validate the user as an authenticated user by using getSession const clientMetadata = this._config.clientMetadata; // TODO: verify behavior if this is override during signIn - user.getSession( + internalUser.getSession( async (err, result) => { if (err) { logger.debug('failed to get the user session', err); if (this.isSessionInvalid(err)) { try { - await this.cleanUpInvalidSession(user); + await this.cleanUpInvalidSession( + internalUser, + userAgentValue + ); } catch (cleanUpError) { rej( new Error( @@ -2178,32 +2469,36 @@ export class InternalAuthClass { } return rej(err); } - user.globalSignOut({ - onSuccess: data => { - logger.debug('global sign out success'); - if (isSignedInHostedUI) { - this.oAuthSignOutRedirect(res, rej); - } else { - return res(); - } - }, - onFailure: err => { - logger.debug('global sign out failed', err); - return rej(err); + internalUser.globalSignOut( + { + onSuccess: data => { + logger.debug('global sign out success'); + if (isSignedInHostedUI) { + this.oAuthSignOutRedirect(res, rej); + } else { + return res(); + } + }, + onFailure: err => { + logger.debug('global sign out failed', err); + return rej(err); + }, }, - }); + userAgentValue + ); }, - { clientMetadata } + { clientMetadata }, + userAgentValue ); } else { - logger.debug('user sign out', user); - user.signOut(() => { + logger.debug('user sign out', internalUser); + internalUser.signOut(() => { if (isSignedInHostedUI) { this.oAuthSignOutRedirect(res, rej); } else { return res(); } - }); + }, userAgentValue); } }); } @@ -2250,9 +2545,14 @@ export class InternalAuthClass { } if (this.userPool) { - const user = this.userPool.getCurrentUser(); - if (user) { - await this.cognitoIdentitySignOut(opts, user); + const internalUser = + this.userPool.getCurrentUser() as InternalCognitoUser; + if (internalUser) { + await this.cognitoIdentitySignOut( + opts, + internalUser, + getAuthUserAgentValue(AuthAction.SignOut, customUserAgentDetails) + ); } else { logger.debug('no current Cognito user'); } @@ -2291,9 +2591,15 @@ export class InternalAuthClass { clientMetadata: ClientMetaData = this._config.clientMetadata, customUserAgentDetails?: CustomUserAgentDetails ): Promise<'SUCCESS'> { + const internalUser: InternalCognitoUser | any = user; + const userAgentValue = getAuthUserAgentValue( + AuthAction.ChangePassword, + customUserAgentDetails + ); + return new Promise((resolve, reject) => { - this.userSession(user).then(session => { - user.changePassword( + this._userSession(userAgentValue, internalUser).then(session => { + internalUser.changePassword( oldPassword, newPassword, (err, data) => { @@ -2304,7 +2610,8 @@ export class InternalAuthClass { return resolve(data); } }, - clientMetadata + clientMetadata, + userAgentValue ); }); }); @@ -2329,9 +2636,9 @@ export class InternalAuthClass { return this.rejectAuthError(AuthErrorTypes.EmptyUsername); } - const user = this.createCognitoUser(username); + const internalUser = this.createCognitoUser(username); return new Promise((resolve, reject) => { - user.forgotPassword( + internalUser.forgotPassword( { onSuccess: () => { resolve(); @@ -2350,14 +2657,15 @@ export class InternalAuthClass { inputVerificationCode: data => { dispatchAuthEvent( 'forgotPassword', - user, + internalUser, `${username} has initiated forgot password flow` ); resolve(data); return; }, }, - clientMetadata + clientMetadata, + getAuthUserAgentValue(AuthAction.ForgotPassword, customUserAgentDetails) ); }); } @@ -2391,16 +2699,16 @@ export class InternalAuthClass { return this.rejectAuthError(AuthErrorTypes.EmptyPassword); } - const user = this.createCognitoUser(username); + const internalUser = this.createCognitoUser(username); return new Promise((resolve, reject) => { - user.confirmPassword( + internalUser.confirmPassword( code, password, { onSuccess: success => { dispatchAuthEvent( 'forgotPasswordSubmit', - user, + internalUser, `${username} forgotPasswordSubmit successful` ); resolve(success); @@ -2416,7 +2724,11 @@ export class InternalAuthClass { return; }, }, - clientMetadata + clientMetadata, + getAuthUserAgentValue( + AuthAction.ForgotPasswordSubmit, + customUserAgentDetails + ) ); }); } @@ -2431,17 +2743,25 @@ export class InternalAuthClass { customUserAgentDetails?: CustomUserAgentDetails ) { const source = this.Credentials.getCredSource(); + const userAgentDetails = getAuthUserAgentDetails( + AuthAction.CurrentUserInfo, + customUserAgentDetails + ); if (!source || source === 'aws' || source === 'userPool') { - const user = await this.currentUserPoolUser().catch(err => - logger.error(err) - ); - if (!user) { + const internalUser: InternalCognitoUser = await this._currentUserPoolUser( + undefined, + userAgentDetails + ).catch(err => logger.error(err)); + if (!internalUser) { return null; } try { - const attributes = await this.userAttributes(user); + const attributes = await this._userAttributes( + internalUser, + userAgentDetails + ); const userAttrs: object = this.attributesToObject(attributes); let credentials = null; try { @@ -2455,7 +2775,7 @@ export class InternalAuthClass { const info = { id: credentials ? credentials.identityId : undefined, - username: user.getUsername(), + username: internalUser.getUsername(), attributes: userAttrs, }; return info; @@ -2521,6 +2841,14 @@ export class InternalAuthClass { ? this._config.oauth.redirectSignIn : this._config.oauth.redirectUri; + this._storage.setItem( + 'aws-amplify-federatedUserAgent', + getAuthUserAgentValue( + AuthAction.FederatedSignIn, + customUserAgentDetails + ) + ); + this._oAuthHandler.oauthSignIn( this._config.oauth.responseType, this._config.oauth.domain, @@ -2550,7 +2878,7 @@ export class InternalAuthClass { { provider, token, identity_id, user, expires_at }, 'federation' ); - const currentUser = await this.currentAuthenticatedUser(); + const currentUser = await this._currentAuthenticatedUser(); dispatchAuthEvent( 'signIn', currentUser, @@ -2601,9 +2929,15 @@ export class InternalAuthClass { if (hasCodeOrError || hasTokenOrError) { this._storage.setItem('amplify-redirected-from-hosted-ui', 'true'); + const userAgentValue = + this._storage.getItem('aws-amplify-federatedUserAgent') || undefined; + this._storage.removeItem('aws-amplify-federatedUserAgent'); try { const { accessToken, idToken, refreshToken, state } = - await this._oAuthHandler.handleAuthResponse(currentUrl); + await this._oAuthHandler.handleAuthResponse( + currentUrl, + userAgentValue + ); const session = new CognitoUserSession({ IdToken: new CognitoIdToken({ IdToken: idToken }), RefreshToken: new CognitoRefreshToken({ @@ -2745,7 +3079,7 @@ export class InternalAuthClass { ); } - private createCognitoUser(username: string): CognitoUser { + private createCognitoUser(username: string): InternalCognitoUser { const userData: ICognitoUserData = { Username: username, Pool: this.userPool, @@ -2754,11 +3088,11 @@ export class InternalAuthClass { const { authenticationFlowType } = this._config; - const user = new CognitoUser(userData); + const internalUser = new InternalCognitoUser(userData); if (authenticationFlowType) { - user.setAuthenticationFlowType(authenticationFlowType); + internalUser.setAuthenticationFlowType(authenticationFlowType); } - return user; + return internalUser; } private _isValidAuthStorage(obj) { @@ -2793,78 +3127,105 @@ export class InternalAuthClass { public async rememberDevice( customUserAgentDetails?: CustomUserAgentDetails ): Promise { - let currUser; + let internalUser: InternalCognitoUser | any; + const userAgentDetails = getAuthUserAgentDetails( + AuthAction.RememberDevice, + customUserAgentDetails + ); try { - currUser = await this.currentUserPoolUser(); + internalUser = await this._currentUserPoolUser( + undefined, + userAgentDetails + ); } catch (error) { logger.debug('The user is not authenticated by the error', error); return Promise.reject('The user is not authenticated'); } - currUser.getCachedDeviceKeyAndPassword(); + internalUser.getCachedDeviceKeyAndPassword(); return new Promise((res, rej) => { - currUser.setDeviceStatusRemembered({ - onSuccess: data => { - res(data); - }, - onFailure: err => { - if (err.code === 'InvalidParameterException') { - rej(new AuthError(AuthErrorTypes.DeviceConfig)); - } else if (err.code === 'NetworkError') { - rej(new AuthError(AuthErrorTypes.NetworkError)); - } else { - rej(err); - } + internalUser.setDeviceStatusRemembered( + { + onSuccess: data => { + res(data); + }, + onFailure: err => { + if (err.code === 'InvalidParameterException') { + rej(new AuthError(AuthErrorTypes.DeviceConfig)); + } else if (err.code === 'NetworkError') { + rej(new AuthError(AuthErrorTypes.NetworkError)); + } else { + rej(err); + } + }, }, - }); + getAmplifyUserAgent(userAgentDetails) + ); }); } public async forgetDevice( customUserAgentDetails?: CustomUserAgentDetails ): Promise { - let currUser; + let internalUser: InternalCognitoUser | any; + const userAgentDetails = getAuthUserAgentDetails( + AuthAction.ForgetDevice, + customUserAgentDetails + ); try { - currUser = await this.currentUserPoolUser(); + internalUser = await this._currentUserPoolUser( + undefined, + userAgentDetails + ); } catch (error) { logger.debug('The user is not authenticated by the error', error); return Promise.reject('The user is not authenticated'); } - currUser.getCachedDeviceKeyAndPassword(); + internalUser.getCachedDeviceKeyAndPassword(); return new Promise((res, rej) => { - currUser.forgetDevice({ - onSuccess: data => { - res(data); - }, - onFailure: err => { - if (err.code === 'InvalidParameterException') { - rej(new AuthError(AuthErrorTypes.DeviceConfig)); - } else if (err.code === 'NetworkError') { - rej(new AuthError(AuthErrorTypes.NetworkError)); - } else { - rej(err); - } + internalUser.forgetDevice( + { + onSuccess: data => { + res(data); + }, + onFailure: err => { + if (err.code === 'InvalidParameterException') { + rej(new AuthError(AuthErrorTypes.DeviceConfig)); + } else if (err.code === 'NetworkError') { + rej(new AuthError(AuthErrorTypes.NetworkError)); + } else { + rej(err); + } + }, }, - }); + getAmplifyUserAgent(userAgentDetails) + ); }); } public async fetchDevices( customUserAgentDetails?: CustomUserAgentDetails ): Promise { - let currUser; + let internalUser: InternalCognitoUser | any; + const userAgentDetails = getAuthUserAgentDetails( + AuthAction.FetchDevices, + customUserAgentDetails + ); try { - currUser = await this.currentUserPoolUser(); + internalUser = await this._currentUserPoolUser( + undefined, + userAgentDetails + ); } catch (error) { logger.debug('The user is not authenticated by the error', error); throw new Error('The user is not authenticated'); } - currUser.getCachedDeviceKeyAndPassword(); + internalUser.getCachedDeviceKeyAndPassword(); return new Promise((res, rej) => { const cb = { onSuccess(data) { @@ -2892,7 +3253,12 @@ export class InternalAuthClass { } }, }; - currUser.listDevices(MAX_DEVICES, null, cb); + internalUser.listDevices( + MAX_DEVICES, + null, + cb, + getAmplifyUserAgent(userAgentDetails) + ); }); } } diff --git a/packages/auth/src/utils.ts b/packages/auth/src/utils.ts new file mode 100644 index 00000000000..233395b0828 --- /dev/null +++ b/packages/auth/src/utils.ts @@ -0,0 +1,27 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { + AuthAction, + Category, + CustomUserAgentDetails, + getAmplifyUserAgent, +} from '@aws-amplify/core'; + +export function getAuthUserAgentValue( + action: AuthAction, + customUserAgentDetails?: CustomUserAgentDetails +) { + return getAmplifyUserAgent({ + category: Category.Auth, + action, + ...customUserAgentDetails, + }); +} + +export function getAuthUserAgentDetails( + action: AuthAction, + customUserAgentDetails?: CustomUserAgentDetails +): CustomUserAgentDetails { + return { category: Category.Auth, action, ...customUserAgentDetails }; +} diff --git a/packages/core/package.json b/packages/core/package.json index bc61e898113..b03f7293710 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -104,7 +104,7 @@ "name": "Core (Credentials)", "path": "./lib-esm/index.js", "import": "{ Credentials }", - "limit": "13.45 kB" + "limit": "13.78 kB" }, { "name": "Core (Signer)", diff --git a/packages/core/src/Platform/types.ts b/packages/core/src/Platform/types.ts index b2a7e98e701..af10b9ef787 100644 --- a/packages/core/src/Platform/types.ts +++ b/packages/core/src/Platform/types.ts @@ -54,40 +54,44 @@ export enum ApiAction { Head = '7', } export enum AuthAction { - // SignUp = '1', - // ConfirmSignUp = '2', - // ResendSignUp = '3', - // SignIn = '4', - // GetMFAOptions = '5', - // GetPreferredMFA = '6', - // SetPreferredMFA = '7', - // DisableSMS = '8', - // EnableSMS = '9', - // SetupTOTP = '10', - // VerifyTotpToken = '11', - // ConfirmSignIn = '12', - // CompleteNewPassword = '13', - // SendCustomChallengeAnswer = '14', - // DeleteUserAttributes = '15', - // DeleteUser = '16', - // UpdateUserAttributes = '17', - // UserAttributes = '18', - // CurrentUserPoolUser = '19', - // CurrentAuthenticatedUser = '20', - // CurrentSession = '21', - // VerifyUserAttribute = '22', - // VerifyUserAttributeSubmit = '23', - // VerifyCurrentUserAttribute = '24', - // VerifyCurrentUserAttributeSubmit = '25', - // SignOut = '26', - // ChangePassword = '27', - // ForgotPassword = '28', - // ForgotPasswordSubmit = '29', + SignUp = '1', + ConfirmSignUp = '2', + ResendSignUp = '3', + SignIn = '4', + GetMFAOptions = '5', + GetPreferredMFA = '6', + SetPreferredMFA = '7', + DisableSMS = '8', + EnableSMS = '9', + SetupTOTP = '10', + VerifyTotpToken = '11', + ConfirmSignIn = '12', + CompleteNewPassword = '13', + SendCustomChallengeAnswer = '14', + DeleteUserAttributes = '15', + DeleteUser = '16', + UpdateUserAttributes = '17', + UserAttributes = '18', + CurrentUserPoolUser = '19', + CurrentAuthenticatedUser = '20', + CurrentSession = '21', + VerifyUserAttribute = '22', + VerifyUserAttributeSubmit = '23', + VerifyCurrentUserAttribute = '24', + VerifyCurrentUserAttributeSubmit = '25', + SignOut = '26', + ChangePassword = '27', + ForgotPassword = '28', + ForgotPasswordSubmit = '29', FederatedSignIn = '30', - // CurrentUserInfo = '31', - // RememberDevice = '32', - // ForgetDevice = '33', - // FetchDevices = '34', + VerifiedContact = '31', + UserSession = '32', + CurrentUserCredentials = '33', + CurrentCredentials = '34', + CurrentUserInfo = '35', + RememberDevice = '36', + ForgetDevice = '37', + FetchDevices = '38', } export enum DataStoreAction { Subscribe = '1', diff --git a/packages/datastore/package.json b/packages/datastore/package.json index 53c53abd41f..2102e2fb31d 100644 --- a/packages/datastore/package.json +++ b/packages/datastore/package.json @@ -73,7 +73,7 @@ "name": "DataStore (top-level class)", "path": "./lib-esm/index.js", "import": "{ Amplify, DataStore }", - "limit": "138.27 kB" + "limit": "139.12 kB" } ], "jest": { diff --git a/packages/geo/package.json b/packages/geo/package.json index a1c91e4270d..f4a0ada2ae3 100644 --- a/packages/geo/package.json +++ b/packages/geo/package.json @@ -58,7 +58,7 @@ "name": "Geo (top-level class)", "path": "./lib-esm/index.js", "import": "{ Amplify, Geo }", - "limit": "52.12 kB" + "limit": "52.45 kB" } ], "jest": { diff --git a/packages/interactions/package.json b/packages/interactions/package.json index 392c935cb06..9b6e5992232 100644 --- a/packages/interactions/package.json +++ b/packages/interactions/package.json @@ -59,7 +59,7 @@ "name": "Interactions (top-level class with Lex v2)", "path": "./lib-esm/index.js", "import": "{ Amplify, Interactions, AWSLexV2Provider }", - "limit": "76.02 kB" + "limit": "76.35 kB" } ], "jest": { diff --git a/packages/notifications/package.json b/packages/notifications/package.json index 3df5c832fae..15ab8497b5c 100644 --- a/packages/notifications/package.json +++ b/packages/notifications/package.json @@ -66,13 +66,13 @@ "name": "Notifications (top-level class)", "path": "./lib-esm/index.js", "import": "{ Amplify, Notifications }", - "limit": "30.5 kB" + "limit": "30.67 kB" }, { "name": "Notifications (with Analytics)", "path": "./lib-esm/index.js", "import": "{ Amplify, Notifications, Analytics }", - "limit": "30.5 kB" + "limit": "30.68 kB" } ] } diff --git a/packages/predictions/package.json b/packages/predictions/package.json index 1edef20e7da..22dab367616 100644 --- a/packages/predictions/package.json +++ b/packages/predictions/package.json @@ -63,25 +63,25 @@ "name": "Predictions (all providers)", "path": "./lib-esm/index.js", "import": "{ Amplify, Predictions, AmazonAIPredictionsProvider }", - "limit": "103.55 kB" + "limit": "103.87 kB" }, { "name": "Predictions (Convert provider)", "path": "./lib-esm/index.js", "import": "{ Amplify, Predictions, AmazonAIConvertPredictionsProvider }", - "limit": "58 kB" + "limit": "58.32 kB" }, { "name": "Predictions (Identify provider)", "path": "./lib-esm/index.js", "import": "{ Amplify, Predictions, AmazonAIIdentifyPredictionsProvider }", - "limit": "73.57 kB" + "limit": "73.9 kB" }, { "name": "Predictions (Interpret provider)", "path": "./lib-esm/index.js", "import": "{ Amplify, Predictions, AmazonAIInterpretPredictionsProvider }", - "limit": "43.45 kB" + "limit": "43.77 kB" } ], "jest": { diff --git a/packages/pubsub/package.json b/packages/pubsub/package.json index ff41331d72f..160e53d713f 100644 --- a/packages/pubsub/package.json +++ b/packages/pubsub/package.json @@ -65,13 +65,13 @@ "name": "PubSub (IoT provider)", "path": "./lib-esm/index.js", "import": "{ Amplify, PubSub, AWSIoTProvider }", - "limit": "81.39 kB" + "limit": "82.2 kB" }, { "name": "PubSub (Mqtt provider)", "path": "./lib-esm/index.js", "import": "{ Amplify, PubSub, MqttOverWSProvider }", - "limit": "81.26 kB" + "limit": "82.07 kB" } ], "jest": { diff --git a/packages/storage/package.json b/packages/storage/package.json index 46d757fc559..a10ae566719 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -60,7 +60,7 @@ "name": "Storage (top-level class)", "path": "./lib-esm/index.js", "import": "{ Amplify, Storage }", - "limit": "38.96 kB" + "limit": "39.29 kB" } ], "jest": {