diff --git a/.github/canary-config/canary-all.yml b/.github/canary-config/canary-all.yml index b9a433bb3d7..0882d94931c 100644 --- a/.github/canary-config/canary-all.yml +++ b/.github/canary-config/canary-all.yml @@ -86,6 +86,8 @@ tests: sample_name: [related-models] spec: related-models browser: *minimal_browser_list + timeout_minutes: 45 + retry_count: 10 - test_name: integ_react_datastore_selective_sync desc: 'DataStore - Selective Sync' framework: react @@ -123,6 +125,8 @@ tests: sample_name: [next-13-js] spec: nextjs-13 browser: *minimal_browser_list + timeout_minutes: 45 + retry_count: 10 - test_name: integ_rollup_datastore_basic_crud desc: 'Rollup + DataStore - Basic CRUD' framework: rollup diff --git a/.github/workflows/callable-canary-e2e.yml b/.github/workflows/callable-canary-e2e.yml index 2225b0c78ff..e5f07fbd452 100644 --- a/.github/workflows/callable-canary-e2e.yml +++ b/.github/workflows/callable-canary-e2e.yml @@ -42,7 +42,5 @@ jobs: WEBHOOK_URL: ${{ env.WEBHOOK_URL }} run: | curl -X POST -H "Content-Type: application/json" \ - --data '{"URL":"$WORKFLOW_URL"}' \ + --data {URL:$WORKFLOW_URL} \ $WEBHOOK_URL - - \ No newline at end of file diff --git a/.github/workflows/on-schedule-canary-test.yml b/.github/workflows/on-schedule-canary-test.yml index 6a366cd5853..d61491ab3ce 100644 --- a/.github/workflows/on-schedule-canary-test.yml +++ b/.github/workflows/on-schedule-canary-test.yml @@ -1,7 +1,8 @@ on: - # Tests scheduled for every 1 hour + # Tests scheduled at 4pm(UTC) / 9am(PDT) everyday + # default supported timezone is UTC schedule: - - cron: '0 */1 * * *' + - cron: '0 16 * * *' jobs: canaries: diff --git a/packages/amazon-cognito-identity-js/.eslintignore b/packages/amazon-cognito-identity-js/.eslintignore deleted file mode 100644 index 9ef57f39f0b..00000000000 --- a/packages/amazon-cognito-identity-js/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -src/BigInteger.js diff --git a/packages/amazon-cognito-identity-js/.eslintrc.yaml b/packages/amazon-cognito-identity-js/.eslintrc.yaml deleted file mode 100644 index 6100ad00b5e..00000000000 --- a/packages/amazon-cognito-identity-js/.eslintrc.yaml +++ /dev/null @@ -1,30 +0,0 @@ -root: true -extends: - - airbnb-base - - prettier -env: - browser: true -settings: - import/resolver: webpack -overrides: - files: *.test.js - rules: jest/valid-expect -rules: - # Git-controlled - linebreak-style: off - # sjcl hmac breaks new-cap - new-cap: - - error - - newIsCapExceptions: - - sjcl.misc.hmac - # Check JSDocs - valid-jsdoc: error - require-jsdoc: - - error - - require: - FunctionDeclaration: true - ClassDeclaration: true - MethodDefinition: true - # Seems this doesn't use import/resolver? import/no-unresolved catches most of these anyway. - # https://github.com/benmosher/eslint-plugin-import/issues/496 - import/no-extraneous-dependencies: off diff --git a/packages/amazon-cognito-identity-js/__tests__/CognitoUser.test.js b/packages/amazon-cognito-identity-js/__tests__/CognitoUser.test.js index 23a015b5c4c..c9c86c439d4 100644 --- a/packages/amazon-cognito-identity-js/__tests__/CognitoUser.test.js +++ b/packages/amazon-cognito-identity-js/__tests__/CognitoUser.test.js @@ -203,7 +203,7 @@ describe('authenticateUser()', () => { user.setAuthenticationFlowType('USER_PASSWORD_AUTH'); user.authenticateUser(authDetails, callback); - expect(spyon).toHaveBeenCalledWith(authDetails, callback); + expect(spyon).toHaveBeenCalledWith(authDetails, callback, undefined); }); test('USER_SRP_AUTH and CUSTOM_AUTH flow types', () => { @@ -212,12 +212,12 @@ describe('authenticateUser()', () => { user.setAuthenticationFlowType('USER_SRP_AUTH'); user.authenticateUser(authDetails, callback); - expect(spyon).toHaveBeenCalledWith(authDetails, callback); + expect(spyon).toHaveBeenCalledWith(authDetails, callback, undefined); user.setAuthenticationFlowType('CUSTOM_AUTH'); user.authenticateUser(authDetails, callback); - expect(spyon).toHaveBeenCalledWith(authDetails, callback); + expect(spyon).toHaveBeenCalledWith(authDetails, callback, undefined); }); test('throws error for invalid Authentication flow type', () => { @@ -310,7 +310,8 @@ describe('authenticateUserPlainUsernamePassword()', () => { expect(userSpy3).toBeCalledWith( 'test auth result', userSpy3.mock.calls[0][1], - callback + callback, + undefined ); expect(userSpy3.mock.results[0].value).toBe('test return value'); }); @@ -755,7 +756,8 @@ describe('sendCustomChallengeAnswer()', () => { expect(spyon3).toBeCalledWith( vCognitoUserSession, expect.any(AuthenticationHelper), - callback + callback, + undefined ); }); @@ -1176,33 +1178,33 @@ describe('confirmPassword() and forgotPassword()', () => { jest.clearAllMocks(); }); - test('happy path should callback onSuccess', () => { + test('confirmPassword happy path should callback onSuccess', () => { netRequestMockSuccess(true); cognitoUser.confirmPassword(...confirmPasswordDefaults); expect(callback.onSuccess).toHaveBeenCalledWith('SUCCESS'); }); - test('client request throws an error', () => { + test('confirmPassword client request throws an error', () => { netRequestMockSuccess(false); cognitoUser.confirmPassword(...confirmPasswordDefaults); expect(callback.onFailure.mock.calls.length).toEqual(1); }); - test('happy path should callback onSuccess', () => { + test('forgotPassword happy path should callback onSuccess', () => { callback.inputVerificationCode = null; netRequestMockSuccess(true); cognitoUser.forgotPassword(...forgotPasswordDefaults); expect(callback.onSuccess.mock.calls.length).toEqual(1); }); - test('inputVerification code is a function should callback inputVerificationCode', () => { + test('forgotPassword inputVerification code is a function should callback inputVerificationCode', () => { callback.inputVerificationCode = jest.fn(); netRequestMockSuccess(true); cognitoUser.forgotPassword(...forgotPasswordDefaults); expect(callback.inputVerificationCode.mock.calls.length).toEqual(1); }); - test('client returning an error should call onFailure', () => { + test('forgotPassword client returning an error should call onFailure', () => { netRequestMockSuccess(false); cognitoUser.forgotPassword(...forgotPasswordDefaults); expect(callback.onFailure.mock.calls.length).toEqual(1); diff --git a/packages/amazon-cognito-identity-js/__tests__/internalsIndex.test.js b/packages/amazon-cognito-identity-js/__tests__/internalsIndex.test.js index 4b448a3e8a1..12b4ffa9676 100644 --- a/packages/amazon-cognito-identity-js/__tests__/internalsIndex.test.js +++ b/packages/amazon-cognito-identity-js/__tests__/internalsIndex.test.js @@ -6,6 +6,7 @@ describe('import * keys', () => { Array [ "addAuthCategoryToCognitoUserAgent", "addFrameworkToCognitoUserAgent", + "InternalCognitoUser", ] `); }); diff --git a/packages/amazon-cognito-identity-js/index.d.ts b/packages/amazon-cognito-identity-js/index.d.ts index 0a32f2c9fc5..2c526568121 100644 --- a/packages/amazon-cognito-identity-js/index.d.ts +++ b/packages/amazon-cognito-identity-js/index.d.ts @@ -1,3 +1,4 @@ +import { InternalCognitoUser } from './internals'; declare module 'amazon-cognito-identity-js' { //import * as AWS from "aws-sdk"; @@ -89,19 +90,7 @@ declare module 'amazon-cognito-identity-js' { | 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA'; - export class CognitoUser { - constructor(data: ICognitoUserData); - - challengeName?: ChallengeName; - - public setSignInUserSession(signInUserSession: CognitoUserSession): void; - public getSignInUserSession(): CognitoUserSession | null; - public getUsername(): string; - - public getAuthenticationFlowType(): string; - public setAuthenticationFlowType(authenticationFlowType: string): string; - public getCachedDeviceKeyAndPassword(): void; - + export class CognitoUser extends InternalCognitoUser { public getSession( callback: | ((error: Error, session: null) => void) diff --git a/packages/amazon-cognito-identity-js/internals/index.d.ts b/packages/amazon-cognito-identity-js/internals/index.d.ts index c7ecb6928c7..f1160c9563d 100644 --- a/packages/amazon-cognito-identity-js/internals/index.d.ts +++ b/packages/amazon-cognito-identity-js/internals/index.d.ts @@ -1,2 +1,268 @@ +import { + NodeCallback, + UpdateAttributesNodeCallback, + ClientMetadata, + IAuthenticationCallback, + IMfaSettings, + AuthenticationDetails, + ICognitoUserData, + GetSessionOptions, + ChallengeName, + CognitoUserSession, + CognitoRefreshToken, + CognitoUserAttribute, + ICognitoUserAttributeData, + MFAOption, + UserData, +} from 'amazon-cognito-identity-js'; + export const addAuthCategoryToCognitoUserAgent: () => void; export const addFrameworkToCognitoUserAgent: (content: string) => void; + +export class InternalCognitoUser { + constructor(data: ICognitoUserData); + + challengeName?: ChallengeName; + + public setSignInUserSession(signInUserSession: CognitoUserSession): void; + public getSignInUserSession(): CognitoUserSession | null; + public getUsername(): string; + + public getAuthenticationFlowType(): string; + public setAuthenticationFlowType(authenticationFlowType: string): string; + public getCachedDeviceKeyAndPassword(): void; + + public getSession( + callback: + | ((error: Error, session: null) => void) + | ((error: null, session: CognitoUserSession) => void), + options?: GetSessionOptions, + userAgentValue?: string + ): void; + public refreshSession( + refreshToken: CognitoRefreshToken, + callback: NodeCallback, + clientMetadata?: ClientMetadata, + userAgentValue?: string + ): void; + public authenticateUser( + authenticationDetails: AuthenticationDetails, + callbacks: IAuthenticationCallback, + userAgentValue?: string + ): void; + public initiateAuth( + authenticationDetails: AuthenticationDetails, + callbacks: IAuthenticationCallback, + userAgentValue?: string + ): void; + public confirmRegistration( + code: string, + forceAliasCreation: boolean, + callback: NodeCallback, + clientMetadata?: ClientMetadata, + userAgentValue?: string + ): void; + public sendCustomChallengeAnswer( + answerChallenge: any, + callback: IAuthenticationCallback, + clientMetaData?: ClientMetadata, + userAgentValue?: string + ): void; + public resendConfirmationCode( + callback: NodeCallback, + clientMetaData?: ClientMetadata, + userAgentValue?: string + ): void; + public changePassword( + oldPassword: string, + newPassword: string, + callback: NodeCallback, + clientMetadata?: ClientMetadata, + userAgentValue?: string + ): void; + public forgotPassword( + callbacks: { + onSuccess: (data: any) => void; + onFailure: (err: Error) => void; + inputVerificationCode?: (data: any) => void; + }, + clientMetaData?: ClientMetadata, + userAgentValue?: string + ): void; + public confirmPassword( + verificationCode: string, + newPassword: string, + callbacks: { + onSuccess: (success: string) => void; + onFailure: (err: Error) => void; + }, + clientMetaData?: ClientMetadata, + userAgentValue?: string + ): void; + public setDeviceStatusRemembered( + callbacks: { + onSuccess: (success: string) => void; + onFailure: (err: any) => void; + }, + userAgentValue?: string + ): void; + public setDeviceStatusNotRemembered( + callbacks: { + onSuccess: (success: string) => void; + onFailure: (err: any) => void; + }, + userAgentValue?: string + ): void; + public getDevice( + callbacks: { + onSuccess: (success: string) => void; + onFailure: (err: Error) => void; + }, + userAgentValue?: string + ): any; + public forgetDevice( + callbacks: { + onSuccess: (success: string) => void; + onFailure: (err: Error) => void; + }, + userAgentValue?: string + ): void; + public forgetSpecificDevice( + deviceKey: string, + callbacks: { + onSuccess: (success: string) => void; + onFailure: (err: Error) => void; + }, + userAgentValue?: string + ): void; + public sendMFACode( + confirmationCode: string, + callbacks: { + onSuccess: ( + session: CognitoUserSession, + userConfirmationNecessary?: boolean + ) => void; + onFailure: (err: any) => void; + }, + mfaType?: string, + clientMetadata?: ClientMetadata, + userAgentValue?: string + ): void; + public listDevices( + limit: number, + paginationToken: string | null, + callbacks: { + onSuccess: (data: any) => void; + onFailure: (err: Error) => void; + }, + userAgentValue?: string + ): void; + public completeNewPasswordChallenge( + newPassword: string, + requiredAttributeData: any, + callbacks: IAuthenticationCallback, + clientMetadata?: ClientMetadata, + userAgentValue?: string + ): void; + public signOut(callback?: () => void, userAgentValue?: string): void; + public globalSignOut( + callbacks: { + onSuccess: (msg: string) => void; + onFailure: (err: Error) => void; + }, + userAgentValue?: string + ): void; + public verifyAttribute( + attributeName: string, + confirmationCode: string, + callbacks: { + onSuccess: (success: string) => void; + onFailure: (err: Error) => void; + }, + userAgentValue?: string + ): void; + public getUserAttributes( + callback: NodeCallback, + userAgentValue?: string + ): void; + public updateAttributes( + attributes: (CognitoUserAttribute | ICognitoUserAttributeData)[], + callback: UpdateAttributesNodeCallback, + clientMetadata?: ClientMetadata, + userAgentValue?: string + ): void; + public deleteAttributes( + attributeList: string[], + callback: NodeCallback, + userAgentValue?: string + ): void; + public getAttributeVerificationCode( + name: string, + callback: { + onSuccess: (success: string) => void; + onFailure: (err: Error) => void; + inputVerificationCode?: (data: string) => void | null; + }, + clientMetadata?: ClientMetadata, + userAgentValue?: string + ): void; + public deleteUser( + callback: NodeCallback, + userAgentValue?: string + ): void; + public enableMFA( + callback: NodeCallback, + userAgentValue?: string + ): void; + public disableMFA( + callback: NodeCallback, + userAgentValue?: string + ): void; + public getMFAOptions( + callback: NodeCallback, + userAgentValue?: string + ): void; + public getUserData( + callback: NodeCallback, + params?: any, + userAgentValue?: string + ): void; + public associateSoftwareToken( + callbacks: { + associateSecretCode: (secretCode: string) => void; + onFailure: (err: any) => void; + }, + userAgentValue?: string + ): void; + public verifySoftwareToken( + totpCode: string, + friendlyDeviceName: string, + callbacks: { + onSuccess: (session: CognitoUserSession) => void; + onFailure: (err: Error) => void; + }, + userAgentValue?: string + ): void; + public setUserMfaPreference( + smsMfaSettings: IMfaSettings | null, + softwareTokenMfaSettings: IMfaSettings | null, + callback: NodeCallback, + userAgentValue?: string + ): void; + public sendMFASelectionAnswer( + answerChallenge: string, + callbacks: { + onSuccess: (session: CognitoUserSession) => void; + onFailure: (err: any) => void; + mfaRequired?: ( + challengeName: ChallengeName, + challengeParameters: any + ) => void; + totpRequired?: ( + challengeName: ChallengeName, + challengeParameters: any + ) => void; + }, + userAgentValue?: string + ): void; +} diff --git a/packages/amazon-cognito-identity-js/package.json b/packages/amazon-cognito-identity-js/package.json index 6b1cd612ed9..e9ddcb81ed9 100644 --- a/packages/amazon-cognito-identity-js/package.json +++ b/packages/amazon-cognito-identity-js/package.json @@ -44,8 +44,6 @@ "build:umd": "webpack", "build": "npm run clean && npm run build:cjs && npm run build:esm && npm run build:umd", "generate-version": "genversion src/Platform/version.ts --es6 --semi", - "lint": "eslint src", - "lint2": "eslint enhance-rn.js", "test": "jest --config ./jest.config.js", "format": "echo \"Not implemented\"", "ts-coverage": "typescript-coverage-report -p ./tsconfig.build.json" @@ -76,14 +74,6 @@ "@react-native-async-storage/async-storage": "^1.17.12", "babel-loader": "^8.0.6", "cross-env": "^3.1.4", - "eslint": "^3.19.0", - "eslint-config-airbnb-base": "^5.0.2", - "eslint-config-prettier": "^6.3.0", - "eslint-import-resolver-webpack": "^0.5.1", - "eslint-plugin-import": "^2.7.0", - "eslint-plugin-node": "^5.2.0", - "eslint-plugin-promise": "^3.6.0", - "eslint-plugin-standard": "^3.0.1", "genversion": "^2.2.0", "react": "^16.0.0", "rimraf": "^2.5.4", diff --git a/packages/amazon-cognito-identity-js/src/Client.js b/packages/amazon-cognito-identity-js/src/Client.js index 1ee3f8b4dbc..199432eb44a 100644 --- a/packages/amazon-cognito-identity-js/src/Client.js +++ b/packages/amazon-cognito-identity-js/src/Client.js @@ -32,33 +32,43 @@ export default class Client { * @param {object} params Input parameters * @returns Promise */ - promisifyRequest(operation, params) { + promisifyRequest(operation, params, userAgentValue) { return new Promise((resolve, reject) => { - this.request(operation, params, (err, data) => { - if (err) { - reject( - new CognitoError(err.message, err.code, err.name, err.statusCode) - ); - } else { - resolve(data); - } - }); + this.request( + operation, + params, + (err, data) => { + if (err) { + reject( + new CognitoError(err.message, err.code, err.name, err.statusCode) + ); + } else { + resolve(data); + } + }, + userAgentValue + ); }); } - requestWithRetry(operation, params, callback) { + requestWithRetry(operation, params, callback, userAgentValue) { const MAX_DELAY_IN_MILLIS = 5 * 1000; jitteredExponentialRetry( p => new Promise((res, rej) => { - this.request(operation, p, (error, result) => { - if (error) { - rej(error); - } else { - res(result); - } - }); + this.request( + operation, + p, + (error, result) => { + if (error) { + rej(error); + } else { + res(result); + } + }, + userAgentValue + ); }), [params], MAX_DELAY_IN_MILLIS @@ -75,11 +85,11 @@ export default class Client { * @param {function} callback Callback called when a response is returned * @returns {void} */ - request(operation, params, callback) { + request(operation, params, callback, userAgentValue) { const headers = { 'Content-Type': 'application/x-amz-json-1.1', 'X-Amz-Target': `AWSCognitoIdentityProviderService.${operation}`, - 'X-Amz-User-Agent': getAmplifyUserAgent(), + 'X-Amz-User-Agent': userAgentValue || getAmplifyUserAgent(), 'Cache-Control': 'no-store', }; diff --git a/packages/amazon-cognito-identity-js/src/CognitoUser.js b/packages/amazon-cognito-identity-js/src/CognitoUser.js index 9dc61d9e228..345e8bf88c0 100644 --- a/packages/amazon-cognito-identity-js/src/CognitoUser.js +++ b/packages/amazon-cognito-identity-js/src/CognitoUser.js @@ -3,19 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { Buffer } from 'buffer'; -import { Sha256 } from '@aws-crypto/sha256-js'; -import { Platform } from './Platform'; - -import BigInteger from './BigInteger'; -import AuthenticationHelper from './AuthenticationHelper'; -import CognitoAccessToken from './CognitoAccessToken'; -import CognitoIdToken from './CognitoIdToken'; import CognitoRefreshToken from './CognitoRefreshToken'; import CognitoUserSession from './CognitoUserSession'; -import DateHelper from './DateHelper'; import CognitoUserAttribute from './CognitoUserAttribute'; -import StorageHelper from './StorageHelper'; +import { InternalCognitoUser } from './internals'; /** * @callback nodeCallback @@ -56,83 +47,8 @@ import StorageHelper from './StorageHelper'; * @param {bool=} userConfirmationNecessary User must be confirmed. */ -const isNavigatorAvailable = typeof navigator !== 'undefined'; -const userAgent = isNavigatorAvailable - ? Platform.isReactNative - ? 'react-native' - : navigator.userAgent - : 'nodejs'; - /** @class */ -export default class CognitoUser { - /** - * Constructs a new CognitoUser object - * @param {object} data Creation options - * @param {string} data.Username The user's username. - * @param {CognitoUserPool} data.Pool Pool containing the user. - * @param {object} data.Storage Optional storage object. - */ - constructor(data) { - if (data == null || data.Username == null || data.Pool == null) { - throw new Error('Username and Pool information are required.'); - } - - this.username = data.Username || ''; - this.pool = data.Pool; - this.Session = null; - - this.client = data.Pool.client; - - this.signInUserSession = null; - this.authenticationFlowType = 'USER_SRP_AUTH'; - - this.storage = data.Storage || new StorageHelper().getStorage(); - - this.keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}`; - this.userDataKey = `${this.keyPrefix}.${this.username}.userData`; - } - - /** - * Sets the session for this user - * @param {CognitoUserSession} signInUserSession the session - * @returns {void} - */ - setSignInUserSession(signInUserSession) { - this.clearCachedUserData(); - this.signInUserSession = signInUserSession; - this.cacheTokens(); - } - - /** - * @returns {CognitoUserSession} the current session for this user - */ - getSignInUserSession() { - return this.signInUserSession; - } - - /** - * @returns {string} the user's username - */ - getUsername() { - return this.username; - } - - /** - * @returns {String} the authentication flow type - */ - getAuthenticationFlowType() { - return this.authenticationFlowType; - } - - /** - * sets authentication flow type - * @param {string} authenticationFlowType New value. - * @returns {void} - */ - setAuthenticationFlowType(authenticationFlowType) { - this.authenticationFlowType = authenticationFlowType; - } - +export default class CognitoUser extends InternalCognitoUser { /** * This is used for authenticating the user through the custom authentication flow. * @param {AuthenticationDetails} authDetails Contains the authentication data @@ -144,41 +60,7 @@ export default class CognitoUser { * @returns {void} */ initiateAuth(authDetails, callback) { - const authParameters = authDetails.getAuthParameters(); - authParameters.USERNAME = this.username; - - const clientMetaData = - Object.keys(authDetails.getValidationData()).length !== 0 - ? authDetails.getValidationData() - : authDetails.getClientMetadata(); - - const jsonReq = { - AuthFlow: 'CUSTOM_AUTH', - ClientId: this.pool.getClientId(), - AuthParameters: authParameters, - ClientMetadata: clientMetaData, - }; - if (this.getUserContextData()) { - jsonReq.UserContextData = this.getUserContextData(); - } - - this.client.request('InitiateAuth', jsonReq, (err, data) => { - if (err) { - return callback.onFailure(err); - } - const challengeName = data.ChallengeName; - const challengeParameters = data.ChallengeParameters; - - if (challengeName === 'CUSTOM_CHALLENGE') { - this.Session = data.Session; - return callback.customChallenge(challengeParameters); - } - this.signInUserSession = this.getCognitoUserSession( - data.AuthenticationResult - ); - this.cacheTokens(); - return callback.onSuccess(this.signInUserSession); - }); + super.initiateAuth(authDetails, callback); } /** @@ -197,375 +79,7 @@ export default class CognitoUser { * @returns {void} */ authenticateUser(authDetails, callback) { - if (this.authenticationFlowType === 'USER_PASSWORD_AUTH') { - return this.authenticateUserPlainUsernamePassword(authDetails, callback); - } else if ( - this.authenticationFlowType === 'USER_SRP_AUTH' || - this.authenticationFlowType === 'CUSTOM_AUTH' - ) { - return this.authenticateUserDefaultAuth(authDetails, callback); - } - return callback.onFailure( - new Error('Authentication flow type is invalid.') - ); - } - - /** - * PRIVATE ONLY: This is an internal only method and should not - * be directly called by the consumers. - * It calls the AuthenticationHelper for SRP related - * stuff - * @param {AuthenticationDetails} authDetails Contains the authentication data - * @param {object} callback Result callback map. - * @param {onFailure} callback.onFailure Called on any error. - * @param {newPasswordRequired} callback.newPasswordRequired new - * password and any required attributes are required to continue - * @param {mfaRequired} callback.mfaRequired MFA code - * required to continue. - * @param {customChallenge} callback.customChallenge Custom challenge - * response required to continue. - * @param {authSuccess} callback.onSuccess Called on success with the new session. - * @returns {void} - */ - authenticateUserDefaultAuth(authDetails, callback) { - const authenticationHelper = new AuthenticationHelper( - this.pool.getUserPoolName() - ); - const dateHelper = new DateHelper(); - - let serverBValue; - let salt; - const authParameters = {}; - - if (this.deviceKey != null) { - authParameters.DEVICE_KEY = this.deviceKey; - } - - authParameters.USERNAME = this.username; - authenticationHelper.getLargeAValue((errOnAValue, aValue) => { - // getLargeAValue callback start - if (errOnAValue) { - callback.onFailure(errOnAValue); - } - - authParameters.SRP_A = aValue.toString(16); - - if (this.authenticationFlowType === 'CUSTOM_AUTH') { - authParameters.CHALLENGE_NAME = 'SRP_A'; - } - - const clientMetaData = - Object.keys(authDetails.getValidationData()).length !== 0 - ? authDetails.getValidationData() - : authDetails.getClientMetadata(); - - const jsonReq = { - AuthFlow: this.authenticationFlowType, - ClientId: this.pool.getClientId(), - AuthParameters: authParameters, - ClientMetadata: clientMetaData, - }; - if (this.getUserContextData(this.username)) { - jsonReq.UserContextData = this.getUserContextData(this.username); - } - - this.client.request('InitiateAuth', jsonReq, (err, data) => { - if (err) { - return callback.onFailure(err); - } - - const challengeParameters = data.ChallengeParameters; - - this.username = challengeParameters.USER_ID_FOR_SRP; - this.userDataKey = `${this.keyPrefix}.${this.username}.userData`; - serverBValue = new BigInteger(challengeParameters.SRP_B, 16); - salt = new BigInteger(challengeParameters.SALT, 16); - this.getCachedDeviceKeyAndPassword(); - - authenticationHelper.getPasswordAuthenticationKey( - this.username, - authDetails.getPassword(), - serverBValue, - salt, - (errOnHkdf, hkdf) => { - // getPasswordAuthenticationKey callback start - if (errOnHkdf) { - callback.onFailure(errOnHkdf); - } - - const dateNow = dateHelper.getNowString(); - - const concatBuffer = Buffer.concat([ - Buffer.from(this.pool.getUserPoolName(), 'utf8'), - Buffer.from(this.username, 'utf8'), - Buffer.from(challengeParameters.SECRET_BLOCK, 'base64'), - Buffer.from(dateNow, 'utf8'), - ]); - - const awsCryptoHash = new Sha256(hkdf); - awsCryptoHash.update(concatBuffer); - - const resultFromAWSCrypto = awsCryptoHash.digestSync(); - const signatureString = - Buffer.from(resultFromAWSCrypto).toString('base64'); - - const challengeResponses = {}; - - challengeResponses.USERNAME = this.username; - challengeResponses.PASSWORD_CLAIM_SECRET_BLOCK = - challengeParameters.SECRET_BLOCK; - challengeResponses.TIMESTAMP = dateNow; - challengeResponses.PASSWORD_CLAIM_SIGNATURE = signatureString; - - if (this.deviceKey != null) { - challengeResponses.DEVICE_KEY = this.deviceKey; - } - - const respondToAuthChallenge = (challenge, challengeCallback) => - this.client.request( - 'RespondToAuthChallenge', - challenge, - (errChallenge, dataChallenge) => { - if ( - errChallenge && - errChallenge.code === 'ResourceNotFoundException' && - errChallenge.message.toLowerCase().indexOf('device') !== -1 - ) { - challengeResponses.DEVICE_KEY = null; - this.deviceKey = null; - this.randomPassword = null; - this.deviceGroupKey = null; - this.clearCachedDeviceKeyAndPassword(); - return respondToAuthChallenge(challenge, challengeCallback); - } - return challengeCallback(errChallenge, dataChallenge); - } - ); - - const jsonReqResp = { - ChallengeName: 'PASSWORD_VERIFIER', - ClientId: this.pool.getClientId(), - ChallengeResponses: challengeResponses, - Session: data.Session, - ClientMetadata: clientMetaData, - }; - if (this.getUserContextData()) { - jsonReqResp.UserContextData = this.getUserContextData(); - } - respondToAuthChallenge( - jsonReqResp, - (errAuthenticate, dataAuthenticate) => { - if (errAuthenticate) { - return callback.onFailure(errAuthenticate); - } - - return this.authenticateUserInternal( - dataAuthenticate, - authenticationHelper, - callback - ); - } - ); - return undefined; - // getPasswordAuthenticationKey callback end - } - ); - return undefined; - }); - // getLargeAValue callback end - }); - } - - /** - * PRIVATE ONLY: This is an internal only method and should not - * be directly called by the consumers. - * @param {AuthenticationDetails} authDetails Contains the authentication data. - * @param {object} callback Result callback map. - * @param {onFailure} callback.onFailure Called on any error. - * @param {mfaRequired} callback.mfaRequired MFA code - * required to continue. - * @param {authSuccess} callback.onSuccess Called on success with the new session. - * @returns {void} - */ - authenticateUserPlainUsernamePassword(authDetails, callback) { - const authParameters = {}; - authParameters.USERNAME = this.username; - authParameters.PASSWORD = authDetails.getPassword(); - if (!authParameters.PASSWORD) { - callback.onFailure(new Error('PASSWORD parameter is required')); - return; - } - const authenticationHelper = new AuthenticationHelper( - this.pool.getUserPoolName() - ); - this.getCachedDeviceKeyAndPassword(); - if (this.deviceKey != null) { - authParameters.DEVICE_KEY = this.deviceKey; - } - - const clientMetaData = - Object.keys(authDetails.getValidationData()).length !== 0 - ? authDetails.getValidationData() - : authDetails.getClientMetadata(); - - const jsonReq = { - AuthFlow: 'USER_PASSWORD_AUTH', - ClientId: this.pool.getClientId(), - AuthParameters: authParameters, - ClientMetadata: clientMetaData, - }; - if (this.getUserContextData(this.username)) { - jsonReq.UserContextData = this.getUserContextData(this.username); - } - // USER_PASSWORD_AUTH happens in a single round-trip: client sends userName and password, - // Cognito UserPools verifies password and returns tokens. - this.client.request('InitiateAuth', jsonReq, (err, authResult) => { - if (err) { - return callback.onFailure(err); - } - return this.authenticateUserInternal( - authResult, - authenticationHelper, - callback - ); - }); - } - - /** - * PRIVATE ONLY: This is an internal only method and should not - * be directly called by the consumers. - * @param {object} dataAuthenticate authentication data - * @param {object} authenticationHelper helper created - * @param {callback} callback passed on from caller - * @returns {void} - */ - authenticateUserInternal(dataAuthenticate, authenticationHelper, callback) { - const challengeName = dataAuthenticate.ChallengeName; - const challengeParameters = dataAuthenticate.ChallengeParameters; - - if (challengeName === 'SMS_MFA') { - this.Session = dataAuthenticate.Session; - return callback.mfaRequired(challengeName, challengeParameters); - } - - if (challengeName === 'SELECT_MFA_TYPE') { - this.Session = dataAuthenticate.Session; - return callback.selectMFAType(challengeName, challengeParameters); - } - - if (challengeName === 'MFA_SETUP') { - this.Session = dataAuthenticate.Session; - return callback.mfaSetup(challengeName, challengeParameters); - } - - if (challengeName === 'SOFTWARE_TOKEN_MFA') { - this.Session = dataAuthenticate.Session; - return callback.totpRequired(challengeName, challengeParameters); - } - - if (challengeName === 'CUSTOM_CHALLENGE') { - this.Session = dataAuthenticate.Session; - return callback.customChallenge(challengeParameters); - } - - if (challengeName === 'NEW_PASSWORD_REQUIRED') { - this.Session = dataAuthenticate.Session; - - let userAttributes = null; - let rawRequiredAttributes = null; - const requiredAttributes = []; - const userAttributesPrefix = - authenticationHelper.getNewPasswordRequiredChallengeUserAttributePrefix(); - - if (challengeParameters) { - userAttributes = JSON.parse( - dataAuthenticate.ChallengeParameters.userAttributes - ); - rawRequiredAttributes = JSON.parse( - dataAuthenticate.ChallengeParameters.requiredAttributes - ); - } - - if (rawRequiredAttributes) { - for (let i = 0; i < rawRequiredAttributes.length; i++) { - requiredAttributes[i] = rawRequiredAttributes[i].substr( - userAttributesPrefix.length - ); - } - } - return callback.newPasswordRequired(userAttributes, requiredAttributes); - } - - if (challengeName === 'DEVICE_SRP_AUTH') { - this.Session = dataAuthenticate.Session; - this.getDeviceResponse(callback); - return undefined; - } - - this.signInUserSession = this.getCognitoUserSession( - dataAuthenticate.AuthenticationResult - ); - this.challengeName = challengeName; - this.cacheTokens(); - - const newDeviceMetadata = - dataAuthenticate.AuthenticationResult.NewDeviceMetadata; - if (newDeviceMetadata == null) { - return callback.onSuccess(this.signInUserSession); - } - - authenticationHelper.generateHashDevice( - dataAuthenticate.AuthenticationResult.NewDeviceMetadata.DeviceGroupKey, - dataAuthenticate.AuthenticationResult.NewDeviceMetadata.DeviceKey, - errGenHash => { - if (errGenHash) { - return callback.onFailure(errGenHash); - } - - const deviceSecretVerifierConfig = { - Salt: Buffer.from( - authenticationHelper.getSaltDevices(), - 'hex' - ).toString('base64'), - PasswordVerifier: Buffer.from( - authenticationHelper.getVerifierDevices(), - 'hex' - ).toString('base64'), - }; - - this.verifierDevices = deviceSecretVerifierConfig.PasswordVerifier; - this.deviceGroupKey = newDeviceMetadata.DeviceGroupKey; - this.randomPassword = authenticationHelper.getRandomPassword(); - - this.client.request( - 'ConfirmDevice', - { - DeviceKey: newDeviceMetadata.DeviceKey, - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - DeviceSecretVerifierConfig: deviceSecretVerifierConfig, - DeviceName: userAgent, - }, - (errConfirm, dataConfirm) => { - if (errConfirm) { - return callback.onFailure(errConfirm); - } - - this.deviceKey = - dataAuthenticate.AuthenticationResult.NewDeviceMetadata.DeviceKey; - this.cacheDeviceKeyAndPassword(); - if (dataConfirm.UserConfirmationNecessary === true) { - return callback.onSuccess( - this.signInUserSession, - dataConfirm.UserConfirmationNecessary - ); - } - return callback.onSuccess(this.signInUserSession); - } - ); - return undefined; - } - ); - return undefined; + super.authenticateUser(authDetails, callback); } /** @@ -589,51 +103,12 @@ export default class CognitoUser { callback, clientMetadata ) { - if (!newPassword) { - return callback.onFailure(new Error('New password is required.')); - } - const authenticationHelper = new AuthenticationHelper( - this.pool.getUserPoolName() - ); - const userAttributesPrefix = - authenticationHelper.getNewPasswordRequiredChallengeUserAttributePrefix(); - - const finalUserAttributes = {}; - if (requiredAttributeData) { - Object.keys(requiredAttributeData).forEach(key => { - finalUserAttributes[userAttributesPrefix + key] = - requiredAttributeData[key]; - }); - } - - finalUserAttributes.NEW_PASSWORD = newPassword; - finalUserAttributes.USERNAME = this.username; - const jsonReq = { - ChallengeName: 'NEW_PASSWORD_REQUIRED', - ClientId: this.pool.getClientId(), - ChallengeResponses: finalUserAttributes, - Session: this.Session, - ClientMetadata: clientMetadata, - }; - if (this.getUserContextData()) { - jsonReq.UserContextData = this.getUserContextData(); - } - - this.client.request( - 'RespondToAuthChallenge', - jsonReq, - (errAuthenticate, dataAuthenticate) => { - if (errAuthenticate) { - return callback.onFailure(errAuthenticate); - } - return this.authenticateUserInternal( - dataAuthenticate, - authenticationHelper, - callback - ); - } + super.completeNewPasswordChallenge( + newPassword, + requiredAttributeData, + callback, + clientMetadata ); - return undefined; } /** @@ -648,111 +123,7 @@ export default class CognitoUser { * @private */ getDeviceResponse(callback, clientMetadata) { - const authenticationHelper = new AuthenticationHelper(this.deviceGroupKey); - const dateHelper = new DateHelper(); - - const authParameters = {}; - - authParameters.USERNAME = this.username; - authParameters.DEVICE_KEY = this.deviceKey; - authenticationHelper.getLargeAValue((errAValue, aValue) => { - // getLargeAValue callback start - if (errAValue) { - callback.onFailure(errAValue); - } - - authParameters.SRP_A = aValue.toString(16); - - const jsonReq = { - ChallengeName: 'DEVICE_SRP_AUTH', - ClientId: this.pool.getClientId(), - ChallengeResponses: authParameters, - ClientMetadata: clientMetadata, - Session: this.Session, - }; - if (this.getUserContextData()) { - jsonReq.UserContextData = this.getUserContextData(); - } - this.client.request('RespondToAuthChallenge', jsonReq, (err, data) => { - if (err) { - return callback.onFailure(err); - } - - const challengeParameters = data.ChallengeParameters; - - const serverBValue = new BigInteger(challengeParameters.SRP_B, 16); - const salt = new BigInteger(challengeParameters.SALT, 16); - - authenticationHelper.getPasswordAuthenticationKey( - this.deviceKey, - this.randomPassword, - serverBValue, - salt, - (errHkdf, hkdf) => { - // getPasswordAuthenticationKey callback start - if (errHkdf) { - return callback.onFailure(errHkdf); - } - - const dateNow = dateHelper.getNowString(); - - const concatBuffer = Buffer.concat([ - Buffer.from(this.deviceGroupKey, 'utf8'), - Buffer.from(this.deviceKey, 'utf8'), - Buffer.from(challengeParameters.SECRET_BLOCK, 'base64'), - Buffer.from(dateNow, 'utf8'), - ]); - - const awsCryptoHash = new Sha256(hkdf); - awsCryptoHash.update(concatBuffer); - - const resultFromAWSCrypto = awsCryptoHash.digestSync(); - const signatureString = - Buffer.from(resultFromAWSCrypto).toString('base64'); - - const challengeResponses = {}; - - challengeResponses.USERNAME = this.username; - challengeResponses.PASSWORD_CLAIM_SECRET_BLOCK = - challengeParameters.SECRET_BLOCK; - challengeResponses.TIMESTAMP = dateNow; - challengeResponses.PASSWORD_CLAIM_SIGNATURE = signatureString; - challengeResponses.DEVICE_KEY = this.deviceKey; - - const jsonReqResp = { - ChallengeName: 'DEVICE_PASSWORD_VERIFIER', - ClientId: this.pool.getClientId(), - ChallengeResponses: challengeResponses, - Session: data.Session, - }; - if (this.getUserContextData()) { - jsonReqResp.UserContextData = this.getUserContextData(); - } - - this.client.request( - 'RespondToAuthChallenge', - jsonReqResp, - (errAuthenticate, dataAuthenticate) => { - if (errAuthenticate) { - return callback.onFailure(errAuthenticate); - } - - this.signInUserSession = this.getCognitoUserSession( - dataAuthenticate.AuthenticationResult - ); - this.cacheTokens(); - - return callback.onSuccess(this.signInUserSession); - } - ); - return undefined; - // getPasswordAuthenticationKey callback end - } - ); - return undefined; - }); - // getLargeAValue callback end - }); + super.getDeviceResponse(callback, clientMetadata); } /** @@ -769,22 +140,12 @@ export default class CognitoUser { callback, clientMetadata ) { - const jsonReq = { - ClientId: this.pool.getClientId(), - ConfirmationCode: confirmationCode, - Username: this.username, - ForceAliasCreation: forceAliasCreation, - ClientMetadata: clientMetadata, - }; - if (this.getUserContextData()) { - jsonReq.UserContextData = this.getUserContextData(); - } - this.client.request('ConfirmSignUp', jsonReq, err => { - if (err) { - return callback(err, null); - } - return callback(null, 'SUCCESS'); - }); + super.confirmRegistration( + confirmationCode, + forceAliasCreation, + callback, + clientMetadata + ); } /** @@ -799,39 +160,7 @@ export default class CognitoUser { * @returns {void} */ sendCustomChallengeAnswer(answerChallenge, callback, clientMetadata) { - const challengeResponses = {}; - challengeResponses.USERNAME = this.username; - challengeResponses.ANSWER = answerChallenge; - - const authenticationHelper = new AuthenticationHelper( - this.pool.getUserPoolName() - ); - this.getCachedDeviceKeyAndPassword(); - if (this.deviceKey != null) { - challengeResponses.DEVICE_KEY = this.deviceKey; - } - - const jsonReq = { - ChallengeName: 'CUSTOM_CHALLENGE', - ChallengeResponses: challengeResponses, - ClientId: this.pool.getClientId(), - Session: this.Session, - ClientMetadata: clientMetadata, - }; - if (this.getUserContextData()) { - jsonReq.UserContextData = this.getUserContextData(); - } - this.client.request('RespondToAuthChallenge', jsonReq, (err, data) => { - if (err) { - return callback.onFailure(err); - } - - return this.authenticateUserInternal( - data, - authenticationHelper, - callback - ); - }); + super.sendCustomChallengeAnswer(answerChallenge, callback, clientMetadata); } /** @@ -845,116 +174,7 @@ export default class CognitoUser { * @returns {void} */ sendMFACode(confirmationCode, callback, mfaType, clientMetadata) { - const challengeResponses = {}; - challengeResponses.USERNAME = this.username; - challengeResponses.SMS_MFA_CODE = confirmationCode; - const mfaTypeSelection = mfaType || 'SMS_MFA'; - if (mfaTypeSelection === 'SOFTWARE_TOKEN_MFA') { - challengeResponses.SOFTWARE_TOKEN_MFA_CODE = confirmationCode; - } - - if (this.deviceKey != null) { - challengeResponses.DEVICE_KEY = this.deviceKey; - } - - const jsonReq = { - ChallengeName: mfaTypeSelection, - ChallengeResponses: challengeResponses, - ClientId: this.pool.getClientId(), - Session: this.Session, - ClientMetadata: clientMetadata, - }; - if (this.getUserContextData()) { - jsonReq.UserContextData = this.getUserContextData(); - } - - this.client.request( - 'RespondToAuthChallenge', - jsonReq, - (err, dataAuthenticate) => { - if (err) { - return callback.onFailure(err); - } - - const challengeName = dataAuthenticate.ChallengeName; - - if (challengeName === 'DEVICE_SRP_AUTH') { - this.getDeviceResponse(callback); - return undefined; - } - - this.signInUserSession = this.getCognitoUserSession( - dataAuthenticate.AuthenticationResult - ); - this.cacheTokens(); - - if (dataAuthenticate.AuthenticationResult.NewDeviceMetadata == null) { - return callback.onSuccess(this.signInUserSession); - } - - const authenticationHelper = new AuthenticationHelper( - this.pool.getUserPoolName() - ); - authenticationHelper.generateHashDevice( - dataAuthenticate.AuthenticationResult.NewDeviceMetadata - .DeviceGroupKey, - dataAuthenticate.AuthenticationResult.NewDeviceMetadata.DeviceKey, - errGenHash => { - if (errGenHash) { - return callback.onFailure(errGenHash); - } - - const deviceSecretVerifierConfig = { - Salt: Buffer.from( - authenticationHelper.getSaltDevices(), - 'hex' - ).toString('base64'), - PasswordVerifier: Buffer.from( - authenticationHelper.getVerifierDevices(), - 'hex' - ).toString('base64'), - }; - - this.verifierDevices = deviceSecretVerifierConfig.PasswordVerifier; - this.deviceGroupKey = - dataAuthenticate.AuthenticationResult.NewDeviceMetadata.DeviceGroupKey; - this.randomPassword = authenticationHelper.getRandomPassword(); - - this.client.request( - 'ConfirmDevice', - { - DeviceKey: - dataAuthenticate.AuthenticationResult.NewDeviceMetadata - .DeviceKey, - AccessToken: this.signInUserSession - .getAccessToken() - .getJwtToken(), - DeviceSecretVerifierConfig: deviceSecretVerifierConfig, - DeviceName: userAgent, - }, - (errConfirm, dataConfirm) => { - if (errConfirm) { - return callback.onFailure(errConfirm); - } - - this.deviceKey = - dataAuthenticate.AuthenticationResult.NewDeviceMetadata.DeviceKey; - this.cacheDeviceKeyAndPassword(); - if (dataConfirm.UserConfirmationNecessary === true) { - return callback.onSuccess( - this.signInUserSession, - dataConfirm.UserConfirmationNecessary - ); - } - return callback.onSuccess(this.signInUserSession); - } - ); - return undefined; - } - ); - return undefined; - } - ); + super.sendMFACode(confirmationCode, callback, mfaType, clientMetadata); } /** @@ -966,26 +186,12 @@ export default class CognitoUser { * @returns {void} */ changePassword(oldUserPassword, newUserPassword, callback, clientMetadata) { - if (!(this.signInUserSession != null && this.signInUserSession.isValid())) { - return callback(new Error('User is not authenticated'), null); - } - - this.client.request( - 'ChangePassword', - { - PreviousPassword: oldUserPassword, - ProposedPassword: newUserPassword, - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - ClientMetadata: clientMetadata, - }, - err => { - if (err) { - return callback(err, null); - } - return callback(null, 'SUCCESS'); - } + super.changePassword( + oldUserPassword, + newUserPassword, + callback, + clientMetadata ); - return undefined; } /** @@ -995,31 +201,7 @@ export default class CognitoUser { * @returns {void} */ enableMFA(callback) { - if (this.signInUserSession == null || !this.signInUserSession.isValid()) { - return callback(new Error('User is not authenticated'), null); - } - - const mfaOptions = []; - const mfaEnabled = { - DeliveryMedium: 'SMS', - AttributeName: 'phone_number', - }; - mfaOptions.push(mfaEnabled); - - this.client.request( - 'SetUserSettings', - { - MFAOptions: mfaOptions, - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - }, - err => { - if (err) { - return callback(err, null); - } - return callback(null, 'SUCCESS'); - } - ); - return undefined; + super.enableMFA(callback); } /** @@ -1030,25 +212,11 @@ export default class CognitoUser { * @returns {void} */ setUserMfaPreference(smsMfaSettings, softwareTokenMfaSettings, callback) { - if (this.signInUserSession == null || !this.signInUserSession.isValid()) { - return callback(new Error('User is not authenticated'), null); - } - - this.client.request( - 'SetUserMFAPreference', - { - SMSMfaSettings: smsMfaSettings, - SoftwareTokenMfaSettings: softwareTokenMfaSettings, - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - }, - err => { - if (err) { - return callback(err, null); - } - return callback(null, 'SUCCESS'); - } + super.setUserMfaPreference( + smsMfaSettings, + softwareTokenMfaSettings, + callback ); - return undefined; } /** @@ -1058,26 +226,7 @@ export default class CognitoUser { * @returns {void} */ disableMFA(callback) { - if (this.signInUserSession == null || !this.signInUserSession.isValid()) { - return callback(new Error('User is not authenticated'), null); - } - - const mfaOptions = []; - - this.client.request( - 'SetUserSettings', - { - MFAOptions: mfaOptions, - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - }, - err => { - if (err) { - return callback(err, null); - } - return callback(null, 'SUCCESS'); - } - ); - return undefined; + super.disableMFA(callback); } /** @@ -1087,25 +236,7 @@ export default class CognitoUser { * @returns {void} */ deleteUser(callback, clientMetadata) { - if (this.signInUserSession == null || !this.signInUserSession.isValid()) { - return callback(new Error('User is not authenticated'), null); - } - - this.client.request( - 'DeleteUser', - { - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - ClientMetadata: clientMetadata, - }, - err => { - if (err) { - return callback(err, null); - } - this.clearCachedUser(); - return callback(null, 'SUCCESS'); - } - ); - return undefined; + super.deleteUser(callback, clientMetadata); } /** @@ -1119,29 +250,7 @@ export default class CognitoUser { * @returns {void} */ updateAttributes(attributes, callback, clientMetadata) { - if (this.signInUserSession == null || !this.signInUserSession.isValid()) { - return callback(new Error('User is not authenticated'), null); - } - - this.client.request( - 'UpdateUserAttributes', - { - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - UserAttributes: attributes, - ClientMetadata: clientMetadata, - }, - (err,result) => { - if (err) { - return callback(err, null); - } - - // update cached user - return this.getUserData(() => callback(null, 'SUCCESS', result), { - bypassCache: true, - }); - } - ); - return undefined; + super.updateAttributes(attributes, callback, clientMetadata); } /** @@ -1150,35 +259,7 @@ export default class CognitoUser { * @returns {void} */ getUserAttributes(callback) { - if (!(this.signInUserSession != null && this.signInUserSession.isValid())) { - return callback(new Error('User is not authenticated'), null); - } - - this.client.request( - 'GetUser', - { - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - }, - (err, userData) => { - if (err) { - return callback(err, null); - } - - const attributeList = []; - - for (let i = 0; i < userData.UserAttributes.length; i++) { - const attribute = { - Name: userData.UserAttributes[i].Name, - Value: userData.UserAttributes[i].Value, - }; - const userAttribute = new CognitoUserAttribute(attribute); - attributeList.push(userAttribute); - } - - return callback(null, attributeList); - } - ); - return undefined; + super.getUserAttributes(callback); } /** @@ -1190,50 +271,7 @@ export default class CognitoUser { * @returns {void} */ getMFAOptions(callback) { - if (!(this.signInUserSession != null && this.signInUserSession.isValid())) { - return callback(new Error('User is not authenticated'), null); - } - - this.client.request( - 'GetUser', - { - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - }, - (err, userData) => { - if (err) { - return callback(err, null); - } - - return callback(null, userData.MFAOptions); - } - ); - return undefined; - } - - /** - * PRIVATE ONLY: This is an internal only method and should not - * be directly called by the consumers. - */ - createGetUserRequest() { - return this.client.promisifyRequest('GetUser', { - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - }); - } - - /** - * PRIVATE ONLY: This is an internal only method and should not - * be directly called by the consumers. - */ - refreshSessionIfPossible(options = {}) { - // best effort, if not possible - return new Promise(resolve => { - const refresh = this.signInUserSession.getRefreshToken(); - if (refresh && refresh.getToken()) { - this.refreshSession(refresh, resolve, options.clientMetadata); - } else { - resolve(); - } - }); + super.getMFAOptions(callback); } /** @@ -1249,73 +287,7 @@ export default class CognitoUser { * @returns {void} */ getUserData(callback, params) { - if (!(this.signInUserSession != null && this.signInUserSession.isValid())) { - this.clearCachedUserData(); - return callback(new Error('User is not authenticated'), null); - } - - const userData = this.getUserDataFromCache(); - - if (!userData) { - this.fetchUserData() - .then(data => { - callback(null, data); - }) - .catch(callback); - return; - } - - if (this.isFetchUserDataAndTokenRequired(params)) { - this.fetchUserData() - .then(data => { - return this.refreshSessionIfPossible(params).then(() => data); - }) - .then(data => callback(null, data)) - .catch(callback); - return; - } - - try { - callback(null, JSON.parse(userData)); - return; - } catch (err) { - this.clearCachedUserData(); - callback(err, null); - return; - } - } - - /** - * - * PRIVATE ONLY: This is an internal only method and should not - * be directly called by the consumers. - */ - getUserDataFromCache() { - const userData = this.storage.getItem(this.userDataKey); - - return userData; - } - - /** - * - * PRIVATE ONLY: This is an internal only method and should not - * be directly called by the consumers. - */ - isFetchUserDataAndTokenRequired(params) { - const { bypassCache = false } = params || {}; - - return bypassCache; - } - /** - * - * PRIVATE ONLY: This is an internal only method and should not - * be directly called by the consumers. - */ - fetchUserData() { - return this.createGetUserRequest().then(data => { - this.cacheUserData(data); - return data; - }); + super.getUserData(callback, params); } /** @@ -1325,28 +297,7 @@ export default class CognitoUser { * @returns {void} */ deleteAttributes(attributeList, callback) { - if (!(this.signInUserSession != null && this.signInUserSession.isValid())) { - return callback(new Error('User is not authenticated'), null); - } - - this.client.request( - 'DeleteUserAttributes', - { - UserAttributeNames: attributeList, - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - }, - err => { - if (err) { - return callback(err, null); - } - - // update cached user - return this.getUserData(() => callback(null, 'SUCCESS'), { - bypassCache: true, - }); - } - ); - return undefined; + super.deleteAttributes(attributeList, callback); } /** @@ -1356,18 +307,7 @@ export default class CognitoUser { * @returns {void} */ resendConfirmationCode(callback, clientMetadata) { - const jsonReq = { - ClientId: this.pool.getClientId(), - Username: this.username, - ClientMetadata: clientMetadata, - }; - - this.client.request('ResendConfirmationCode', jsonReq, (err, result) => { - if (err) { - return callback(err, null); - } - return callback(null, result); - }); + super.resendConfirmationCode(callback, clientMetadata); } /** @@ -1384,66 +324,7 @@ export default class CognitoUser { * @returns {void} */ getSession(callback, options = {}) { - if (this.username == null) { - return callback( - new Error('Username is null. Cannot retrieve a new session'), - null - ); - } - - if (this.signInUserSession != null && this.signInUserSession.isValid()) { - return callback(null, this.signInUserSession); - } - - const keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}.${ - this.username - }`; - const idTokenKey = `${keyPrefix}.idToken`; - const accessTokenKey = `${keyPrefix}.accessToken`; - const refreshTokenKey = `${keyPrefix}.refreshToken`; - const clockDriftKey = `${keyPrefix}.clockDrift`; - - if (this.storage.getItem(idTokenKey)) { - const idToken = new CognitoIdToken({ - IdToken: this.storage.getItem(idTokenKey), - }); - const accessToken = new CognitoAccessToken({ - AccessToken: this.storage.getItem(accessTokenKey), - }); - const refreshToken = new CognitoRefreshToken({ - RefreshToken: this.storage.getItem(refreshTokenKey), - }); - const clockDrift = parseInt(this.storage.getItem(clockDriftKey), 0) || 0; - - const sessionData = { - IdToken: idToken, - AccessToken: accessToken, - RefreshToken: refreshToken, - ClockDrift: clockDrift, - }; - const cachedSession = new CognitoUserSession(sessionData); - - if (cachedSession.isValid()) { - this.signInUserSession = cachedSession; - return callback(null, this.signInUserSession); - } - - if (!refreshToken.getToken()) { - return callback( - new Error('Cannot retrieve a new session. Please authenticate.'), - null - ); - } - - this.refreshSession(refreshToken, callback, options.clientMetadata); - } else { - callback( - new Error('Local storage is missing an ID Token, Please authenticate'), - null - ); - } - - return undefined; + super.getSession(callback, options); } /** @@ -1454,196 +335,7 @@ export default class CognitoUser { * @returns {void} */ refreshSession(refreshToken, callback, clientMetadata) { - const wrappedCallback = this.pool.wrapRefreshSessionCallback - ? this.pool.wrapRefreshSessionCallback(callback) - : callback; - const authParameters = {}; - authParameters.REFRESH_TOKEN = refreshToken.getToken(); - const keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}`; - const lastUserKey = `${keyPrefix}.LastAuthUser`; - - if (this.storage.getItem(lastUserKey)) { - this.username = this.storage.getItem(lastUserKey); - const deviceKeyKey = `${keyPrefix}.${this.username}.deviceKey`; - this.deviceKey = this.storage.getItem(deviceKeyKey); - authParameters.DEVICE_KEY = this.deviceKey; - } - - const jsonReq = { - ClientId: this.pool.getClientId(), - AuthFlow: 'REFRESH_TOKEN_AUTH', - AuthParameters: authParameters, - ClientMetadata: clientMetadata, - }; - if (this.getUserContextData()) { - jsonReq.UserContextData = this.getUserContextData(); - } - this.client.request('InitiateAuth', jsonReq, (err, authResult) => { - if (err) { - if (err.code === 'NotAuthorizedException') { - this.clearCachedUser(); - } - return wrappedCallback(err, null); - } - if (authResult) { - const authenticationResult = authResult.AuthenticationResult; - if ( - !Object.prototype.hasOwnProperty.call( - authenticationResult, - 'RefreshToken' - ) - ) { - authenticationResult.RefreshToken = refreshToken.getToken(); - } - this.signInUserSession = - this.getCognitoUserSession(authenticationResult); - this.cacheTokens(); - return wrappedCallback(null, this.signInUserSession); - } - return undefined; - }); - } - - /** - * This is used to save the session tokens to local storage - * @returns {void} - */ - cacheTokens() { - const keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}`; - const idTokenKey = `${keyPrefix}.${this.username}.idToken`; - const accessTokenKey = `${keyPrefix}.${this.username}.accessToken`; - const refreshTokenKey = `${keyPrefix}.${this.username}.refreshToken`; - const clockDriftKey = `${keyPrefix}.${this.username}.clockDrift`; - const lastUserKey = `${keyPrefix}.LastAuthUser`; - - this.storage.setItem( - idTokenKey, - this.signInUserSession.getIdToken().getJwtToken() - ); - this.storage.setItem( - accessTokenKey, - this.signInUserSession.getAccessToken().getJwtToken() - ); - this.storage.setItem( - refreshTokenKey, - this.signInUserSession.getRefreshToken().getToken() - ); - this.storage.setItem( - clockDriftKey, - `${this.signInUserSession.getClockDrift()}` - ); - this.storage.setItem(lastUserKey, this.username); - } - - /** - * This is to cache user data - */ - cacheUserData(userData) { - this.storage.setItem(this.userDataKey, JSON.stringify(userData)); - } - - /** - * This is to remove cached user data - */ - clearCachedUserData() { - this.storage.removeItem(this.userDataKey); - } - - clearCachedUser() { - this.clearCachedTokens(); - this.clearCachedUserData(); - } - - /** - * This is used to cache the device key and device group and device password - * @returns {void} - */ - cacheDeviceKeyAndPassword() { - const keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}.${ - this.username - }`; - const deviceKeyKey = `${keyPrefix}.deviceKey`; - const randomPasswordKey = `${keyPrefix}.randomPasswordKey`; - const deviceGroupKeyKey = `${keyPrefix}.deviceGroupKey`; - - this.storage.setItem(deviceKeyKey, this.deviceKey); - this.storage.setItem(randomPasswordKey, this.randomPassword); - this.storage.setItem(deviceGroupKeyKey, this.deviceGroupKey); - } - - /** - * This is used to get current device key and device group and device password - * @returns {void} - */ - getCachedDeviceKeyAndPassword() { - const keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}.${ - this.username - }`; - const deviceKeyKey = `${keyPrefix}.deviceKey`; - const randomPasswordKey = `${keyPrefix}.randomPasswordKey`; - const deviceGroupKeyKey = `${keyPrefix}.deviceGroupKey`; - - if (this.storage.getItem(deviceKeyKey)) { - this.deviceKey = this.storage.getItem(deviceKeyKey); - this.randomPassword = this.storage.getItem(randomPasswordKey); - this.deviceGroupKey = this.storage.getItem(deviceGroupKeyKey); - } - } - - /** - * This is used to clear the device key info from local storage - * @returns {void} - */ - clearCachedDeviceKeyAndPassword() { - const keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}.${ - this.username - }`; - const deviceKeyKey = `${keyPrefix}.deviceKey`; - const randomPasswordKey = `${keyPrefix}.randomPasswordKey`; - const deviceGroupKeyKey = `${keyPrefix}.deviceGroupKey`; - - this.storage.removeItem(deviceKeyKey); - this.storage.removeItem(randomPasswordKey); - this.storage.removeItem(deviceGroupKeyKey); - } - - /** - * This is used to clear the session tokens from local storage - * @returns {void} - */ - clearCachedTokens() { - const keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}`; - const idTokenKey = `${keyPrefix}.${this.username}.idToken`; - const accessTokenKey = `${keyPrefix}.${this.username}.accessToken`; - const refreshTokenKey = `${keyPrefix}.${this.username}.refreshToken`; - const lastUserKey = `${keyPrefix}.LastAuthUser`; - const clockDriftKey = `${keyPrefix}.${this.username}.clockDrift`; - - this.storage.removeItem(idTokenKey); - this.storage.removeItem(accessTokenKey); - this.storage.removeItem(refreshTokenKey); - this.storage.removeItem(lastUserKey); - this.storage.removeItem(clockDriftKey); - } - - /** - * This is used to build a user session from tokens retrieved in the authentication result - * @param {object} authResult Successful auth response from server. - * @returns {CognitoUserSession} The new user session. - * @private - */ - getCognitoUserSession(authResult) { - const idToken = new CognitoIdToken(authResult); - const accessToken = new CognitoAccessToken(authResult); - const refreshToken = new CognitoRefreshToken(authResult); - - const sessionData = { - IdToken: idToken, - AccessToken: accessToken, - RefreshToken: refreshToken, - }; - - return new CognitoUserSession(sessionData); + super.refreshSession(refreshToken, callback, clientMetadata); } /** @@ -1657,23 +349,7 @@ export default class CognitoUser { * @returns {void} */ forgotPassword(callback, clientMetadata) { - const jsonReq = { - ClientId: this.pool.getClientId(), - Username: this.username, - ClientMetadata: clientMetadata, - }; - if (this.getUserContextData()) { - jsonReq.UserContextData = this.getUserContextData(); - } - this.client.request('ForgotPassword', jsonReq, (err, data) => { - if (err) { - return callback.onFailure(err); - } - if (typeof callback.inputVerificationCode === 'function') { - return callback.inputVerificationCode(data); - } - return callback.onSuccess(data); - }); + super.forgotPassword(callback, clientMetadata); } /** @@ -1687,22 +363,12 @@ export default class CognitoUser { * @returns {void} */ confirmPassword(confirmationCode, newPassword, callback, clientMetadata) { - const jsonReq = { - ClientId: this.pool.getClientId(), - Username: this.username, - ConfirmationCode: confirmationCode, - Password: newPassword, - ClientMetadata: clientMetadata, - }; - if (this.getUserContextData()) { - jsonReq.UserContextData = this.getUserContextData(); - } - this.client.request('ConfirmForgotPassword', jsonReq, err => { - if (err) { - return callback.onFailure(err); - } - return callback.onSuccess('SUCCESS'); - }); + super.confirmPassword( + confirmationCode, + newPassword, + callback, + clientMetadata + ); } /** @@ -1715,28 +381,7 @@ export default class CognitoUser { * @returns {void} */ getAttributeVerificationCode(attributeName, callback, clientMetadata) { - if (this.signInUserSession == null || !this.signInUserSession.isValid()) { - return callback.onFailure(new Error('User is not authenticated')); - } - - this.client.request( - 'GetUserAttributeVerificationCode', - { - AttributeName: attributeName, - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - ClientMetadata: clientMetadata, - }, - (err, data) => { - if (err) { - return callback.onFailure(err); - } - if (typeof callback.inputVerificationCode === 'function') { - return callback.inputVerificationCode(data); - } - return callback.onSuccess('SUCCESS'); - } - ); - return undefined; + super.getAttributeVerificationCode(attributeName, callback, clientMetadata); } /** @@ -1749,25 +394,7 @@ export default class CognitoUser { * @returns {void} */ verifyAttribute(attributeName, confirmationCode, callback) { - if (this.signInUserSession == null || !this.signInUserSession.isValid()) { - return callback.onFailure(new Error('User is not authenticated')); - } - - this.client.request( - 'VerifyUserAttribute', - { - AttributeName: attributeName, - Code: confirmationCode, - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - }, - err => { - if (err) { - return callback.onFailure(err); - } - return callback.onSuccess('SUCCESS'); - } - ); - return undefined; + super.verifyAttribute(attributeName, confirmationCode, callback); } /** @@ -1778,24 +405,7 @@ export default class CognitoUser { * @returns {void} */ getDevice(callback) { - if (this.signInUserSession == null || !this.signInUserSession.isValid()) { - return callback.onFailure(new Error('User is not authenticated')); - } - - this.client.request( - 'GetDevice', - { - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - DeviceKey: this.deviceKey, - }, - (err, data) => { - if (err) { - return callback.onFailure(err); - } - return callback.onSuccess(data); - } - ); - return undefined; + super.getDevice(callback); } /** @@ -1807,24 +417,7 @@ export default class CognitoUser { * @returns {void} */ forgetSpecificDevice(deviceKey, callback) { - if (this.signInUserSession == null || !this.signInUserSession.isValid()) { - return callback.onFailure(new Error('User is not authenticated')); - } - - this.client.request( - 'ForgetDevice', - { - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - DeviceKey: deviceKey, - }, - err => { - if (err) { - return callback.onFailure(err); - } - return callback.onSuccess('SUCCESS'); - } - ); - return undefined; + super.forgetSpecificDevice(deviceKey, callback); } /** @@ -1835,16 +428,7 @@ export default class CognitoUser { * @returns {void} */ forgetDevice(callback) { - this.forgetSpecificDevice(this.deviceKey, { - onFailure: callback.onFailure, - onSuccess: result => { - this.deviceKey = null; - this.deviceGroupKey = null; - this.randomPassword = null; - this.clearCachedDeviceKeyAndPassword(); - return callback.onSuccess(result); - }, - }); + super.forgetDevice(callback); } /** @@ -1855,25 +439,7 @@ export default class CognitoUser { * @returns {void} */ setDeviceStatusRemembered(callback) { - if (this.signInUserSession == null || !this.signInUserSession.isValid()) { - return callback.onFailure(new Error('User is not authenticated')); - } - - this.client.request( - 'UpdateDeviceStatus', - { - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - DeviceKey: this.deviceKey, - DeviceRememberedStatus: 'remembered', - }, - err => { - if (err) { - return callback.onFailure(err); - } - return callback.onSuccess('SUCCESS'); - } - ); - return undefined; + super.setDeviceStatusRemembered(callback); } /** @@ -1884,25 +450,7 @@ export default class CognitoUser { * @returns {void} */ setDeviceStatusNotRemembered(callback) { - if (this.signInUserSession == null || !this.signInUserSession.isValid()) { - return callback.onFailure(new Error('User is not authenticated')); - } - - this.client.request( - 'UpdateDeviceStatus', - { - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - DeviceKey: this.deviceKey, - DeviceRememberedStatus: 'not_remembered', - }, - err => { - if (err) { - return callback.onFailure(err); - } - return callback.onSuccess('SUCCESS'); - } - ); - return undefined; + super.setDeviceStatusNotRemembered(callback); } /** @@ -1916,25 +464,7 @@ export default class CognitoUser { * @returns {void} */ listDevices(limit, paginationToken, callback) { - if (this.signInUserSession == null || !this.signInUserSession.isValid()) { - return callback.onFailure(new Error('User is not authenticated')); - } - const requestParams = { - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - Limit: limit, - }; - - if (paginationToken) { - requestParams.PaginationToken = paginationToken; - } - - this.client.request('ListDevices', requestParams, (err, data) => { - if (err) { - return callback.onFailure(err); - } - return callback.onSuccess(data); - }); - return undefined; + super.listDevices(limit, paginationToken, callback); } /** @@ -1945,24 +475,7 @@ export default class CognitoUser { * @returns {void} */ globalSignOut(callback) { - if (this.signInUserSession == null || !this.signInUserSession.isValid()) { - return callback.onFailure(new Error('User is not authenticated')); - } - - this.client.request( - 'GlobalSignOut', - { - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - }, - err => { - if (err) { - return callback.onFailure(err); - } - this.clearCachedUser(); - return callback.onSuccess('SUCCESS'); - } - ); - return undefined; + super.globalSignOut(callback); } /** @@ -1970,92 +483,15 @@ export default class CognitoUser { * @returns {void} */ signOut(revokeTokenCallback) { - // If tokens won't be revoked, we just clean the client data. - if (!revokeTokenCallback || typeof revokeTokenCallback !== 'function') { - this.cleanClientData(); - - return; - } - - this.getSession((error, _session) => { - if (error) { - return revokeTokenCallback(error); - } - - this.revokeTokens(err => { - this.cleanClientData(); - - revokeTokenCallback(err); - }); - }); + super.signOut(revokeTokenCallback); } - revokeTokens(revokeTokenCallback = () => {}) { - if (typeof revokeTokenCallback !== 'function') { - throw new Error('Invalid revokeTokenCallback. It should be a function.'); - } - - const tokensToBeRevoked = []; - - if (!this.signInUserSession) { - const error = new Error('User is not authenticated'); - - return revokeTokenCallback(error); - } - - if (!this.signInUserSession.getAccessToken()) { - const error = new Error('No Access token available'); - - return revokeTokenCallback(error); - } - - const refreshToken = this.signInUserSession.getRefreshToken().getToken(); - const accessToken = this.signInUserSession.getAccessToken(); - - if (this.isSessionRevocable(accessToken)) { - if (refreshToken) { - return this.revokeToken({ - token: refreshToken, - callback: revokeTokenCallback, - }); - } - } - revokeTokenCallback(); - } - - isSessionRevocable(token) { - if (token && typeof token.decodePayload === 'function') { - try { - const { origin_jti } = token.decodePayload(); - return !!origin_jti; - } catch (err) { - // Nothing to do, token doesnt have origin_jti claim - } - } - - return false; - } - - cleanClientData() { - this.signInUserSession = null; - this.clearCachedUser(); + revokeTokens(revokeTokenCallback = () => { }) { + super.revokeTokens(revokeTokenCallback); } revokeToken({ token, callback }) { - this.client.requestWithRetry( - 'RevokeToken', - { - Token: token, - ClientId: this.pool.getClientId(), - }, - err => { - if (err) { - return callback(err); - } - - callback(); - } - ); + super.revokeToken({ token, callback }); } /** @@ -2065,47 +501,7 @@ export default class CognitoUser { * @returns {void} */ sendMFASelectionAnswer(answerChallenge, callback) { - const challengeResponses = {}; - challengeResponses.USERNAME = this.username; - challengeResponses.ANSWER = answerChallenge; - - const jsonReq = { - ChallengeName: 'SELECT_MFA_TYPE', - ChallengeResponses: challengeResponses, - ClientId: this.pool.getClientId(), - Session: this.Session, - }; - if (this.getUserContextData()) { - jsonReq.UserContextData = this.getUserContextData(); - } - this.client.request('RespondToAuthChallenge', jsonReq, (err, data) => { - if (err) { - return callback.onFailure(err); - } - this.Session = data.Session; - if (answerChallenge === 'SMS_MFA') { - return callback.mfaRequired( - data.ChallengeName, - data.ChallengeParameters - ); - } - if (answerChallenge === 'SOFTWARE_TOKEN_MFA') { - return callback.totpRequired( - data.ChallengeName, - data.ChallengeParameters - ); - } - return undefined; - }); - } - - /** - * This returns the user context data for advanced security feature. - * @returns {string} the user context data from CognitoUserPool - */ - getUserContextData() { - const pool = this.pool; - return pool.getUserContextData(this.username); + super.sendMFASelectionAnswer(answerChallenge, callback); } /** @@ -2114,34 +510,7 @@ export default class CognitoUser { * @returns {void} */ associateSoftwareToken(callback) { - if (!(this.signInUserSession != null && this.signInUserSession.isValid())) { - this.client.request( - 'AssociateSoftwareToken', - { - Session: this.Session, - }, - (err, data) => { - if (err) { - return callback.onFailure(err); - } - this.Session = data.Session; - return callback.associateSecretCode(data.SecretCode); - } - ); - } else { - this.client.request( - 'AssociateSoftwareToken', - { - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - }, - (err, data) => { - if (err) { - return callback.onFailure(err); - } - return callback.associateSecretCode(data.SecretCode); - } - ); - } + super.associateSoftwareToken(callback); } /** @@ -2152,62 +521,6 @@ export default class CognitoUser { * @returns {void} */ verifySoftwareToken(totpCode, friendlyDeviceName, callback) { - if (!(this.signInUserSession != null && this.signInUserSession.isValid())) { - this.client.request( - 'VerifySoftwareToken', - { - Session: this.Session, - UserCode: totpCode, - FriendlyDeviceName: friendlyDeviceName, - }, - (err, data) => { - if (err) { - return callback.onFailure(err); - } - this.Session = data.Session; - const challengeResponses = {}; - challengeResponses.USERNAME = this.username; - const jsonReq = { - ChallengeName: 'MFA_SETUP', - ClientId: this.pool.getClientId(), - ChallengeResponses: challengeResponses, - Session: this.Session, - }; - if (this.getUserContextData()) { - jsonReq.UserContextData = this.getUserContextData(); - } - this.client.request( - 'RespondToAuthChallenge', - jsonReq, - (errRespond, dataRespond) => { - if (errRespond) { - return callback.onFailure(errRespond); - } - this.signInUserSession = this.getCognitoUserSession( - dataRespond.AuthenticationResult - ); - this.cacheTokens(); - return callback.onSuccess(this.signInUserSession); - } - ); - return undefined; - } - ); - } else { - this.client.request( - 'VerifySoftwareToken', - { - AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), - UserCode: totpCode, - FriendlyDeviceName: friendlyDeviceName, - }, - (err, data) => { - if (err) { - return callback.onFailure(err); - } - return callback.onSuccess(data); - } - ); - } + super.verifySoftwareToken(totpCode, friendlyDeviceName, callback); } } diff --git a/packages/amazon-cognito-identity-js/src/internals/InternalCognitoUser.js b/packages/amazon-cognito-identity-js/src/internals/InternalCognitoUser.js new file mode 100644 index 00000000000..19f0e87b79e --- /dev/null +++ b/packages/amazon-cognito-identity-js/src/internals/InternalCognitoUser.js @@ -0,0 +1,2428 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { Buffer } from 'buffer'; +import { Sha256 } from '@aws-crypto/sha256-js'; +import { Platform } from '../Platform'; + +import BigInteger from '../BigInteger'; +import AuthenticationHelper from '../AuthenticationHelper'; +import CognitoAccessToken from '../CognitoAccessToken'; +import CognitoIdToken from '../CognitoIdToken'; +import CognitoRefreshToken from '../CognitoRefreshToken'; +import CognitoUserSession from '../CognitoUserSession'; +import DateHelper from '../DateHelper'; +import CognitoUserAttribute from '../CognitoUserAttribute'; +import StorageHelper from '../StorageHelper'; + +/** + * @callback nodeCallback + * @template T result + * @param {*} err The operation failure reason, or null. + * @param {T} result The operation result. + */ + +/** + * @callback onFailure + * @param {*} err Failure reason. + */ + +/** + * @callback onSuccess + * @template T result + * @param {T} result The operation result. + */ + +/** + * @callback mfaRequired + * @param {*} details MFA challenge details. + */ + +/** + * @callback customChallenge + * @param {*} details Custom challenge details. + */ + +/** + * @callback inputVerificationCode + * @param {*} data Server response. + */ + +/** + * @callback authSuccess + * @param {CognitoUserSession} session The new session. + * @param {bool=} userConfirmationNecessary User must be confirmed. + */ + +const isNavigatorAvailable = typeof navigator !== 'undefined'; +const userAgent = isNavigatorAvailable + ? Platform.isReactNative + ? 'react-native' + : navigator.userAgent + : 'nodejs'; + +/** @class */ +export class InternalCognitoUser { + /** + * Constructs a new CognitoUser object + * @param {object} data Creation options + * @param {string} data.Username The user's username. + * @param {CognitoUserPool} data.Pool Pool containing the user. + * @param {object} data.Storage Optional storage object. + */ + constructor(data) { + if (data == null || data.Username == null || data.Pool == null) { + throw new Error('Username and Pool information are required.'); + } + + this.username = data.Username || ''; + this.pool = data.Pool; + this.Session = null; + + this.client = data.Pool.client; + + this.signInUserSession = null; + this.authenticationFlowType = 'USER_SRP_AUTH'; + + this.storage = data.Storage || new StorageHelper().getStorage(); + + this.keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}`; + this.userDataKey = `${this.keyPrefix}.${this.username}.userData`; + } + + /** + * Sets the session for this user + * @param {CognitoUserSession} signInUserSession the session + * @returns {void} + */ + setSignInUserSession(signInUserSession) { + this.clearCachedUserData(); + this.signInUserSession = signInUserSession; + this.cacheTokens(); + } + + /** + * @returns {CognitoUserSession} the current session for this user + */ + getSignInUserSession() { + return this.signInUserSession; + } + + /** + * @returns {string} the user's username + */ + getUsername() { + return this.username; + } + + /** + * @returns {String} the authentication flow type + */ + getAuthenticationFlowType() { + return this.authenticationFlowType; + } + + /** + * sets authentication flow type + * @param {string} authenticationFlowType New value. + * @returns {void} + */ + setAuthenticationFlowType(authenticationFlowType) { + this.authenticationFlowType = authenticationFlowType; + } + + /** + * This is used for authenticating the user through the custom authentication flow. + * @param {AuthenticationDetails} authDetails Contains the authentication data + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {customChallenge} callback.customChallenge Custom challenge + * response required to continue. + * @param {authSuccess} callback.onSuccess Called on success with the new session. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + initiateAuth(authDetails, callback, userAgentValue) { + const authParameters = authDetails.getAuthParameters(); + authParameters.USERNAME = this.username; + + const clientMetaData = + Object.keys(authDetails.getValidationData()).length !== 0 + ? authDetails.getValidationData() + : authDetails.getClientMetadata(); + + const jsonReq = { + AuthFlow: 'CUSTOM_AUTH', + ClientId: this.pool.getClientId(), + AuthParameters: authParameters, + ClientMetadata: clientMetaData, + }; + if (this.getUserContextData()) { + jsonReq.UserContextData = this.getUserContextData(); + } + + this.client.request( + 'InitiateAuth', + jsonReq, + (err, data) => { + if (err) { + return callback.onFailure(err); + } + const challengeName = data.ChallengeName; + const challengeParameters = data.ChallengeParameters; + + if (challengeName === 'CUSTOM_CHALLENGE') { + this.Session = data.Session; + return callback.customChallenge(challengeParameters); + } + this.signInUserSession = this.getCognitoUserSession( + data.AuthenticationResult + ); + this.cacheTokens(); + return callback.onSuccess(this.signInUserSession); + }, + userAgentValue + ); + } + + /** + * This is used for authenticating the user. + * stuff + * @param {AuthenticationDetails} authDetails Contains the authentication data + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {newPasswordRequired} callback.newPasswordRequired new + * password and any required attributes are required to continue + * @param {mfaRequired} callback.mfaRequired MFA code + * required to continue. + * @param {customChallenge} callback.customChallenge Custom challenge + * response required to continue. + * @param {authSuccess} callback.onSuccess Called on success with the new session. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + authenticateUser(authDetails, callback, userAgentValue) { + if (this.authenticationFlowType === 'USER_PASSWORD_AUTH') { + return this.authenticateUserPlainUsernamePassword( + authDetails, + callback, + userAgentValue + ); + } else if ( + this.authenticationFlowType === 'USER_SRP_AUTH' || + this.authenticationFlowType === 'CUSTOM_AUTH' + ) { + return this.authenticateUserDefaultAuth( + authDetails, + callback, + userAgentValue + ); + } + return callback.onFailure( + new Error('Authentication flow type is invalid.') + ); + } + + /** + * PRIVATE ONLY: This is an internal only method and should not + * be directly called by the consumers. + * It calls the AuthenticationHelper for SRP related + * stuff + * @param {AuthenticationDetails} authDetails Contains the authentication data + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {newPasswordRequired} callback.newPasswordRequired new + * password and any required attributes are required to continue + * @param {mfaRequired} callback.mfaRequired MFA code + * required to continue. + * @param {customChallenge} callback.customChallenge Custom challenge + * response required to continue. + * @param {authSuccess} callback.onSuccess Called on success with the new session. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + authenticateUserDefaultAuth(authDetails, callback, userAgentValue) { + const authenticationHelper = new AuthenticationHelper( + this.pool.getUserPoolName() + ); + const dateHelper = new DateHelper(); + + let serverBValue; + let salt; + const authParameters = {}; + + if (this.deviceKey != null) { + authParameters.DEVICE_KEY = this.deviceKey; + } + + authParameters.USERNAME = this.username; + authenticationHelper.getLargeAValue((errOnAValue, aValue) => { + // getLargeAValue callback start + if (errOnAValue) { + callback.onFailure(errOnAValue); + } + + authParameters.SRP_A = aValue.toString(16); + + if (this.authenticationFlowType === 'CUSTOM_AUTH') { + authParameters.CHALLENGE_NAME = 'SRP_A'; + } + + const clientMetaData = + Object.keys(authDetails.getValidationData()).length !== 0 + ? authDetails.getValidationData() + : authDetails.getClientMetadata(); + + const jsonReq = { + AuthFlow: this.authenticationFlowType, + ClientId: this.pool.getClientId(), + AuthParameters: authParameters, + ClientMetadata: clientMetaData, + }; + if (this.getUserContextData(this.username)) { + jsonReq.UserContextData = this.getUserContextData(this.username); + } + + this.client.request( + 'InitiateAuth', + jsonReq, + (err, data) => { + if (err) { + return callback.onFailure(err); + } + + const challengeParameters = data.ChallengeParameters; + + this.username = challengeParameters.USER_ID_FOR_SRP; + this.userDataKey = `${this.keyPrefix}.${this.username}.userData`; + serverBValue = new BigInteger(challengeParameters.SRP_B, 16); + salt = new BigInteger(challengeParameters.SALT, 16); + this.getCachedDeviceKeyAndPassword(); + + authenticationHelper.getPasswordAuthenticationKey( + this.username, + authDetails.getPassword(), + serverBValue, + salt, + (errOnHkdf, hkdf) => { + // getPasswordAuthenticationKey callback start + if (errOnHkdf) { + callback.onFailure(errOnHkdf); + } + + const dateNow = dateHelper.getNowString(); + + const concatBuffer = Buffer.concat([ + Buffer.from(this.pool.getUserPoolName(), 'utf8'), + Buffer.from(this.username, 'utf8'), + Buffer.from(challengeParameters.SECRET_BLOCK, 'base64'), + Buffer.from(dateNow, 'utf8'), + ]); + + const awsCryptoHash = new Sha256(hkdf); + awsCryptoHash.update(concatBuffer); + + const resultFromAWSCrypto = awsCryptoHash.digestSync(); + const signatureString = + Buffer.from(resultFromAWSCrypto).toString('base64'); + + const challengeResponses = {}; + + challengeResponses.USERNAME = this.username; + challengeResponses.PASSWORD_CLAIM_SECRET_BLOCK = + challengeParameters.SECRET_BLOCK; + challengeResponses.TIMESTAMP = dateNow; + challengeResponses.PASSWORD_CLAIM_SIGNATURE = signatureString; + + if (this.deviceKey != null) { + challengeResponses.DEVICE_KEY = this.deviceKey; + } + + const respondToAuthChallenge = (challenge, challengeCallback) => + this.client.request( + 'RespondToAuthChallenge', + challenge, + (errChallenge, dataChallenge) => { + if ( + errChallenge && + errChallenge.code === 'ResourceNotFoundException' && + errChallenge.message.toLowerCase().indexOf('device') !== + -1 + ) { + challengeResponses.DEVICE_KEY = null; + this.deviceKey = null; + this.randomPassword = null; + this.deviceGroupKey = null; + this.clearCachedDeviceKeyAndPassword(); + return respondToAuthChallenge( + challenge, + challengeCallback + ); + } + return challengeCallback(errChallenge, dataChallenge); + }, + userAgentValue + ); + + const jsonReqResp = { + ChallengeName: 'PASSWORD_VERIFIER', + ClientId: this.pool.getClientId(), + ChallengeResponses: challengeResponses, + Session: data.Session, + ClientMetadata: clientMetaData, + }; + if (this.getUserContextData()) { + jsonReqResp.UserContextData = this.getUserContextData(); + } + respondToAuthChallenge( + jsonReqResp, + (errAuthenticate, dataAuthenticate) => { + if (errAuthenticate) { + return callback.onFailure(errAuthenticate); + } + + return this.authenticateUserInternal( + dataAuthenticate, + authenticationHelper, + callback, + userAgentValue + ); + } + ); + return undefined; + // getPasswordAuthenticationKey callback end + } + ); + return undefined; + }, + userAgentValue + ); + // getLargeAValue callback end + }); + } + + /** + * PRIVATE ONLY: This is an internal only method and should not + * be directly called by the consumers. + * @param {AuthenticationDetails} authDetails Contains the authentication data. + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {mfaRequired} callback.mfaRequired MFA code + * required to continue. + * @param {authSuccess} callback.onSuccess Called on success with the new session. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + authenticateUserPlainUsernamePassword(authDetails, callback, userAgentValue) { + const authParameters = {}; + authParameters.USERNAME = this.username; + authParameters.PASSWORD = authDetails.getPassword(); + if (!authParameters.PASSWORD) { + callback.onFailure(new Error('PASSWORD parameter is required')); + return; + } + const authenticationHelper = new AuthenticationHelper( + this.pool.getUserPoolName() + ); + this.getCachedDeviceKeyAndPassword(); + if (this.deviceKey != null) { + authParameters.DEVICE_KEY = this.deviceKey; + } + + const clientMetaData = + Object.keys(authDetails.getValidationData()).length !== 0 + ? authDetails.getValidationData() + : authDetails.getClientMetadata(); + + const jsonReq = { + AuthFlow: 'USER_PASSWORD_AUTH', + ClientId: this.pool.getClientId(), + AuthParameters: authParameters, + ClientMetadata: clientMetaData, + }; + if (this.getUserContextData(this.username)) { + jsonReq.UserContextData = this.getUserContextData(this.username); + } + // USER_PASSWORD_AUTH happens in a single round-trip: client sends userName and password, + // Cognito UserPools verifies password and returns tokens. + this.client.request( + 'InitiateAuth', + jsonReq, + (err, authResult) => { + if (err) { + return callback.onFailure(err); + } + return this.authenticateUserInternal( + authResult, + authenticationHelper, + callback, + userAgentValue + ); + }, + userAgentValue + ); + } + + /** + * PRIVATE ONLY: This is an internal only method and should not + * be directly called by the consumers. + * @param {object} dataAuthenticate authentication data + * @param {object} authenticationHelper helper created + * @param {callback} callback passed on from caller + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + authenticateUserInternal( + dataAuthenticate, + authenticationHelper, + callback, + userAgentValue + ) { + const challengeName = dataAuthenticate.ChallengeName; + const challengeParameters = dataAuthenticate.ChallengeParameters; + + if (challengeName === 'SMS_MFA') { + this.Session = dataAuthenticate.Session; + return callback.mfaRequired(challengeName, challengeParameters); + } + + if (challengeName === 'SELECT_MFA_TYPE') { + this.Session = dataAuthenticate.Session; + return callback.selectMFAType(challengeName, challengeParameters); + } + + if (challengeName === 'MFA_SETUP') { + this.Session = dataAuthenticate.Session; + return callback.mfaSetup(challengeName, challengeParameters); + } + + if (challengeName === 'SOFTWARE_TOKEN_MFA') { + this.Session = dataAuthenticate.Session; + return callback.totpRequired(challengeName, challengeParameters); + } + + if (challengeName === 'CUSTOM_CHALLENGE') { + this.Session = dataAuthenticate.Session; + return callback.customChallenge(challengeParameters); + } + + if (challengeName === 'NEW_PASSWORD_REQUIRED') { + this.Session = dataAuthenticate.Session; + + let userAttributes = null; + let rawRequiredAttributes = null; + const requiredAttributes = []; + const userAttributesPrefix = + authenticationHelper.getNewPasswordRequiredChallengeUserAttributePrefix(); + + if (challengeParameters) { + userAttributes = JSON.parse( + dataAuthenticate.ChallengeParameters.userAttributes + ); + rawRequiredAttributes = JSON.parse( + dataAuthenticate.ChallengeParameters.requiredAttributes + ); + } + + if (rawRequiredAttributes) { + for (let i = 0; i < rawRequiredAttributes.length; i++) { + requiredAttributes[i] = rawRequiredAttributes[i].substr( + userAttributesPrefix.length + ); + } + } + return callback.newPasswordRequired(userAttributes, requiredAttributes); + } + + if (challengeName === 'DEVICE_SRP_AUTH') { + this.Session = dataAuthenticate.Session; + this.getDeviceResponse(callback, undefined, userAgentValue); + return undefined; + } + + this.signInUserSession = this.getCognitoUserSession( + dataAuthenticate.AuthenticationResult + ); + this.challengeName = challengeName; + this.cacheTokens(); + + const newDeviceMetadata = + dataAuthenticate.AuthenticationResult.NewDeviceMetadata; + if (newDeviceMetadata == null) { + return callback.onSuccess(this.signInUserSession); + } + + authenticationHelper.generateHashDevice( + dataAuthenticate.AuthenticationResult.NewDeviceMetadata.DeviceGroupKey, + dataAuthenticate.AuthenticationResult.NewDeviceMetadata.DeviceKey, + errGenHash => { + if (errGenHash) { + return callback.onFailure(errGenHash); + } + + const deviceSecretVerifierConfig = { + Salt: Buffer.from( + authenticationHelper.getSaltDevices(), + 'hex' + ).toString('base64'), + PasswordVerifier: Buffer.from( + authenticationHelper.getVerifierDevices(), + 'hex' + ).toString('base64'), + }; + + this.verifierDevices = deviceSecretVerifierConfig.PasswordVerifier; + this.deviceGroupKey = newDeviceMetadata.DeviceGroupKey; + this.randomPassword = authenticationHelper.getRandomPassword(); + + this.client.request( + 'ConfirmDevice', + { + DeviceKey: newDeviceMetadata.DeviceKey, + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + DeviceSecretVerifierConfig: deviceSecretVerifierConfig, + DeviceName: userAgent, + }, + (errConfirm, dataConfirm) => { + if (errConfirm) { + return callback.onFailure(errConfirm); + } + + this.deviceKey = + dataAuthenticate.AuthenticationResult.NewDeviceMetadata.DeviceKey; + this.cacheDeviceKeyAndPassword(); + if (dataConfirm.UserConfirmationNecessary === true) { + return callback.onSuccess( + this.signInUserSession, + dataConfirm.UserConfirmationNecessary + ); + } + return callback.onSuccess(this.signInUserSession); + }, + userAgentValue + ); + return undefined; + } + ); + return undefined; + } + + /** + * This method is user to complete the NEW_PASSWORD_REQUIRED challenge. + * Pass the new password with any new user attributes to be updated. + * User attribute keys must be of format userAttributes.. + * @param {string} newPassword new password for this user + * @param {object} requiredAttributeData map with values for all required attributes + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {mfaRequired} callback.mfaRequired MFA code required to continue. + * @param {customChallenge} callback.customChallenge Custom challenge + * response required to continue. + * @param {authSuccess} callback.onSuccess Called on success with the new session. + * @param {ClientMetadata} clientMetadata object which is passed from client to Cognito Lambda trigger + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + completeNewPasswordChallenge( + newPassword, + requiredAttributeData, + callback, + clientMetadata, + userAgentValue + ) { + if (!newPassword) { + return callback.onFailure(new Error('New password is required.')); + } + const authenticationHelper = new AuthenticationHelper( + this.pool.getUserPoolName() + ); + const userAttributesPrefix = + authenticationHelper.getNewPasswordRequiredChallengeUserAttributePrefix(); + + const finalUserAttributes = {}; + if (requiredAttributeData) { + Object.keys(requiredAttributeData).forEach(key => { + finalUserAttributes[userAttributesPrefix + key] = + requiredAttributeData[key]; + }); + } + + finalUserAttributes.NEW_PASSWORD = newPassword; + finalUserAttributes.USERNAME = this.username; + const jsonReq = { + ChallengeName: 'NEW_PASSWORD_REQUIRED', + ClientId: this.pool.getClientId(), + ChallengeResponses: finalUserAttributes, + Session: this.Session, + ClientMetadata: clientMetadata, + }; + if (this.getUserContextData()) { + jsonReq.UserContextData = this.getUserContextData(); + } + + this.client.request( + 'RespondToAuthChallenge', + jsonReq, + (errAuthenticate, dataAuthenticate) => { + if (errAuthenticate) { + return callback.onFailure(errAuthenticate); + } + return this.authenticateUserInternal( + dataAuthenticate, + authenticationHelper, + callback, + userAgentValue + ); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used to get a session using device authentication. It is called at the end of user + * authentication + * + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {authSuccess} callback.onSuccess Called on success with the new session. + * @param {ClientMetadata} clientMetadata object which is passed from client to Cognito Lambda trigger + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + * @private + */ + getDeviceResponse(callback, clientMetadata, userAgentValue) { + const authenticationHelper = new AuthenticationHelper(this.deviceGroupKey); + const dateHelper = new DateHelper(); + + const authParameters = {}; + + authParameters.USERNAME = this.username; + authParameters.DEVICE_KEY = this.deviceKey; + authenticationHelper.getLargeAValue((errAValue, aValue) => { + // getLargeAValue callback start + if (errAValue) { + callback.onFailure(errAValue); + } + + authParameters.SRP_A = aValue.toString(16); + + const jsonReq = { + ChallengeName: 'DEVICE_SRP_AUTH', + ClientId: this.pool.getClientId(), + ChallengeResponses: authParameters, + ClientMetadata: clientMetadata, + Session: this.Session, + }; + if (this.getUserContextData()) { + jsonReq.UserContextData = this.getUserContextData(); + } + this.client.request( + 'RespondToAuthChallenge', + jsonReq, + (err, data) => { + if (err) { + return callback.onFailure(err); + } + + const challengeParameters = data.ChallengeParameters; + + const serverBValue = new BigInteger(challengeParameters.SRP_B, 16); + const salt = new BigInteger(challengeParameters.SALT, 16); + + authenticationHelper.getPasswordAuthenticationKey( + this.deviceKey, + this.randomPassword, + serverBValue, + salt, + (errHkdf, hkdf) => { + // getPasswordAuthenticationKey callback start + if (errHkdf) { + return callback.onFailure(errHkdf); + } + + const dateNow = dateHelper.getNowString(); + + const concatBuffer = Buffer.concat([ + Buffer.from(this.deviceGroupKey, 'utf8'), + Buffer.from(this.deviceKey, 'utf8'), + Buffer.from(challengeParameters.SECRET_BLOCK, 'base64'), + Buffer.from(dateNow, 'utf8'), + ]); + + const awsCryptoHash = new Sha256(hkdf); + awsCryptoHash.update(concatBuffer); + + const resultFromAWSCrypto = awsCryptoHash.digestSync(); + const signatureString = + Buffer.from(resultFromAWSCrypto).toString('base64'); + + const challengeResponses = {}; + + challengeResponses.USERNAME = this.username; + challengeResponses.PASSWORD_CLAIM_SECRET_BLOCK = + challengeParameters.SECRET_BLOCK; + challengeResponses.TIMESTAMP = dateNow; + challengeResponses.PASSWORD_CLAIM_SIGNATURE = signatureString; + challengeResponses.DEVICE_KEY = this.deviceKey; + + const jsonReqResp = { + ChallengeName: 'DEVICE_PASSWORD_VERIFIER', + ClientId: this.pool.getClientId(), + ChallengeResponses: challengeResponses, + Session: data.Session, + }; + if (this.getUserContextData()) { + jsonReqResp.UserContextData = this.getUserContextData(); + } + + this.client.request( + 'RespondToAuthChallenge', + jsonReqResp, + (errAuthenticate, dataAuthenticate) => { + if (errAuthenticate) { + return callback.onFailure(errAuthenticate); + } + + this.signInUserSession = this.getCognitoUserSession( + dataAuthenticate.AuthenticationResult + ); + this.cacheTokens(); + + return callback.onSuccess(this.signInUserSession); + }, + userAgentValue + ); + return undefined; + // getPasswordAuthenticationKey callback end + } + ); + return undefined; + }, + userAgentValue + ); + // getLargeAValue callback end + }); + } + + /** + * This is used for a certain user to confirm the registration by using a confirmation code + * @param {string} confirmationCode Code entered by user. + * @param {bool} forceAliasCreation Allow migrating from an existing email / phone number. + * @param {nodeCallback} callback Called on success or error. + * @param {ClientMetadata} clientMetadata object which is passed from client to Cognito Lambda trigger + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + confirmRegistration( + confirmationCode, + forceAliasCreation, + callback, + clientMetadata, + userAgentValue + ) { + const jsonReq = { + ClientId: this.pool.getClientId(), + ConfirmationCode: confirmationCode, + Username: this.username, + ForceAliasCreation: forceAliasCreation, + ClientMetadata: clientMetadata, + }; + if (this.getUserContextData()) { + jsonReq.UserContextData = this.getUserContextData(); + } + this.client.request( + 'ConfirmSignUp', + jsonReq, + err => { + if (err) { + return callback(err, null); + } + return callback(null, 'SUCCESS'); + }, + userAgentValue + ); + } + + /** + * This is used by the user once he has the responses to a custom challenge + * @param {string} answerChallenge The custom challenge answer. + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {customChallenge} callback.customChallenge + * Custom challenge response required to continue. + * @param {authSuccess} callback.onSuccess Called on success with the new session. + * @param {ClientMetadata} clientMetadata object which is passed from client to Cognito Lambda trigger + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + sendCustomChallengeAnswer( + answerChallenge, + callback, + clientMetadata, + userAgentValue + ) { + const challengeResponses = {}; + challengeResponses.USERNAME = this.username; + challengeResponses.ANSWER = answerChallenge; + + const authenticationHelper = new AuthenticationHelper( + this.pool.getUserPoolName() + ); + this.getCachedDeviceKeyAndPassword(); + if (this.deviceKey != null) { + challengeResponses.DEVICE_KEY = this.deviceKey; + } + + const jsonReq = { + ChallengeName: 'CUSTOM_CHALLENGE', + ChallengeResponses: challengeResponses, + ClientId: this.pool.getClientId(), + Session: this.Session, + ClientMetadata: clientMetadata, + }; + if (this.getUserContextData()) { + jsonReq.UserContextData = this.getUserContextData(); + } + this.client.request('RespondToAuthChallenge', jsonReq, (err, data) => { + if (err) { + return callback.onFailure(err); + } + + return this.authenticateUserInternal( + data, + authenticationHelper, + callback, + userAgentValue + ); + }); + } + + /** + * This is used by the user once he has an MFA code + * @param {string} confirmationCode The MFA code entered by the user. + * @param {object} callback Result callback map. + * @param {string} mfaType The mfa we are replying to. + * @param {onFailure} callback.onFailure Called on any error. + * @param {authSuccess} callback.onSuccess Called on success with the new session. + * @param {ClientMetadata} clientMetadata object which is passed from client to Cognito Lambda trigger + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + sendMFACode( + confirmationCode, + callback, + mfaType, + clientMetadata, + userAgentValue + ) { + const challengeResponses = {}; + challengeResponses.USERNAME = this.username; + challengeResponses.SMS_MFA_CODE = confirmationCode; + const mfaTypeSelection = mfaType || 'SMS_MFA'; + if (mfaTypeSelection === 'SOFTWARE_TOKEN_MFA') { + challengeResponses.SOFTWARE_TOKEN_MFA_CODE = confirmationCode; + } + + if (this.deviceKey != null) { + challengeResponses.DEVICE_KEY = this.deviceKey; + } + + const jsonReq = { + ChallengeName: mfaTypeSelection, + ChallengeResponses: challengeResponses, + ClientId: this.pool.getClientId(), + Session: this.Session, + ClientMetadata: clientMetadata, + }; + if (this.getUserContextData()) { + jsonReq.UserContextData = this.getUserContextData(); + } + + this.client.request( + 'RespondToAuthChallenge', + jsonReq, + (err, dataAuthenticate) => { + if (err) { + return callback.onFailure(err); + } + + const challengeName = dataAuthenticate.ChallengeName; + + if (challengeName === 'DEVICE_SRP_AUTH') { + this.getDeviceResponse(callback, undefined, userAgentValue); + return undefined; + } + + this.signInUserSession = this.getCognitoUserSession( + dataAuthenticate.AuthenticationResult + ); + this.cacheTokens(); + + if (dataAuthenticate.AuthenticationResult.NewDeviceMetadata == null) { + return callback.onSuccess(this.signInUserSession); + } + + const authenticationHelper = new AuthenticationHelper( + this.pool.getUserPoolName() + ); + authenticationHelper.generateHashDevice( + dataAuthenticate.AuthenticationResult.NewDeviceMetadata + .DeviceGroupKey, + dataAuthenticate.AuthenticationResult.NewDeviceMetadata.DeviceKey, + errGenHash => { + if (errGenHash) { + return callback.onFailure(errGenHash); + } + + const deviceSecretVerifierConfig = { + Salt: Buffer.from( + authenticationHelper.getSaltDevices(), + 'hex' + ).toString('base64'), + PasswordVerifier: Buffer.from( + authenticationHelper.getVerifierDevices(), + 'hex' + ).toString('base64'), + }; + + this.verifierDevices = deviceSecretVerifierConfig.PasswordVerifier; + this.deviceGroupKey = + dataAuthenticate.AuthenticationResult.NewDeviceMetadata.DeviceGroupKey; + this.randomPassword = authenticationHelper.getRandomPassword(); + + this.client.request( + 'ConfirmDevice', + { + DeviceKey: + dataAuthenticate.AuthenticationResult.NewDeviceMetadata + .DeviceKey, + AccessToken: this.signInUserSession + .getAccessToken() + .getJwtToken(), + DeviceSecretVerifierConfig: deviceSecretVerifierConfig, + DeviceName: userAgent, + }, + (errConfirm, dataConfirm) => { + if (errConfirm) { + return callback.onFailure(errConfirm); + } + + this.deviceKey = + dataAuthenticate.AuthenticationResult.NewDeviceMetadata.DeviceKey; + this.cacheDeviceKeyAndPassword(); + if (dataConfirm.UserConfirmationNecessary === true) { + return callback.onSuccess( + this.signInUserSession, + dataConfirm.UserConfirmationNecessary + ); + } + return callback.onSuccess(this.signInUserSession); + }, + userAgentValue + ); + return undefined; + } + ); + return undefined; + }, + userAgentValue + ); + } + + /** + * This is used by an authenticated user to change the current password + * @param {string} oldUserPassword The current password. + * @param {string} newUserPassword The requested new password. + * @param {nodeCallback} callback Called on success or error. + * @param {ClientMetadata} clientMetadata object which is passed from client to Cognito Lambda trigger + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + changePassword( + oldUserPassword, + newUserPassword, + callback, + clientMetadata, + userAgentValue + ) { + if (!(this.signInUserSession != null && this.signInUserSession.isValid())) { + return callback(new Error('User is not authenticated'), null); + } + + this.client.request( + 'ChangePassword', + { + PreviousPassword: oldUserPassword, + ProposedPassword: newUserPassword, + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + ClientMetadata: clientMetadata, + }, + err => { + if (err) { + return callback(err, null); + } + return callback(null, 'SUCCESS'); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used by an authenticated user to enable MFA for itself + * @deprecated + * @param {nodeCallback} callback Called on success or error. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + enableMFA(callback, userAgentValue) { + if (this.signInUserSession == null || !this.signInUserSession.isValid()) { + return callback(new Error('User is not authenticated'), null); + } + + const mfaOptions = []; + const mfaEnabled = { + DeliveryMedium: 'SMS', + AttributeName: 'phone_number', + }; + mfaOptions.push(mfaEnabled); + + this.client.request( + 'SetUserSettings', + { + MFAOptions: mfaOptions, + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + }, + err => { + if (err) { + return callback(err, null); + } + return callback(null, 'SUCCESS'); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used by an authenticated user to enable MFA for itself + * @param {IMfaSettings} smsMfaSettings the sms mfa settings + * @param {IMFASettings} softwareTokenMfaSettings the software token mfa settings + * @param {nodeCallback} callback Called on success or error. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + setUserMfaPreference( + smsMfaSettings, + softwareTokenMfaSettings, + callback, + userAgentValue + ) { + if (this.signInUserSession == null || !this.signInUserSession.isValid()) { + return callback(new Error('User is not authenticated'), null); + } + + this.client.request( + 'SetUserMFAPreference', + { + SMSMfaSettings: smsMfaSettings, + SoftwareTokenMfaSettings: softwareTokenMfaSettings, + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + }, + err => { + if (err) { + return callback(err, null); + } + return callback(null, 'SUCCESS'); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used by an authenticated user to disable MFA for itself + * @deprecated + * @param {nodeCallback} callback Called on success or error. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + disableMFA(callback, userAgentValue) { + if (this.signInUserSession == null || !this.signInUserSession.isValid()) { + return callback(new Error('User is not authenticated'), null); + } + + const mfaOptions = []; + + this.client.request( + 'SetUserSettings', + { + MFAOptions: mfaOptions, + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + }, + err => { + if (err) { + return callback(err, null); + } + return callback(null, 'SUCCESS'); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used by an authenticated user to delete itself + * @param {nodeCallback} callback Called on success or error. + * @param {ClientMetadata} clientMetadata object which is passed from client to Cognito Lambda trigger + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + deleteUser(callback, clientMetadata, userAgentValue) { + if (this.signInUserSession == null || !this.signInUserSession.isValid()) { + return callback(new Error('User is not authenticated'), null); + } + + this.client.request( + 'DeleteUser', + { + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + ClientMetadata: clientMetadata, + }, + err => { + if (err) { + return callback(err, null); + } + this.clearCachedUser(); + return callback(null, 'SUCCESS'); + }, + userAgentValue + ); + return undefined; + } + + /** + * @typedef {CognitoUserAttribute | { Name:string, Value:string }} AttributeArg + */ + /** + * This is used by an authenticated user to change a list of attributes + * @param {AttributeArg[]} attributes A list of the new user attributes. + * @param {nodeCallback} callback Called on success or error. + * @param {ClientMetadata} clientMetadata object which is passed from client to Cognito Lambda trigger + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + updateAttributes(attributes, callback, clientMetadata, userAgentValue) { + if (this.signInUserSession == null || !this.signInUserSession.isValid()) { + return callback(new Error('User is not authenticated'), null); + } + + this.client.request( + 'UpdateUserAttributes', + { + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + UserAttributes: attributes, + ClientMetadata: clientMetadata, + }, + (err, result) => { + if (err) { + return callback(err, null); + } + + // update cached user + return this.getUserData( + () => callback(null, 'SUCCESS', result), + { + bypassCache: true, + }, + userAgentValue + ); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used by an authenticated user to get a list of attributes + * @param {nodeCallback} callback Called on success or error. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + getUserAttributes(callback, userAgentValue) { + if (!(this.signInUserSession != null && this.signInUserSession.isValid())) { + return callback(new Error('User is not authenticated'), null); + } + + this.client.request( + 'GetUser', + { + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + }, + (err, userData) => { + if (err) { + return callback(err, null); + } + + const attributeList = []; + + for (let i = 0; i < userData.UserAttributes.length; i++) { + const attribute = { + Name: userData.UserAttributes[i].Name, + Value: userData.UserAttributes[i].Value, + }; + const userAttribute = new CognitoUserAttribute(attribute); + attributeList.push(userAttribute); + } + + return callback(null, attributeList); + }, + userAgentValue + ); + return undefined; + } + + /** + * This was previously used by an authenticated user to get MFAOptions, + * but no longer returns a meaningful response. Refer to the documentation for + * how to setup and use MFA: https://docs.amplify.aws/lib/auth/mfa/q/platform/js + * @deprecated + * @param {nodeCallback} callback Called on success or error. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + getMFAOptions(callback, userAgentValue) { + if (!(this.signInUserSession != null && this.signInUserSession.isValid())) { + return callback(new Error('User is not authenticated'), null); + } + + this.client.request( + 'GetUser', + { + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + }, + (err, userData) => { + if (err) { + return callback(err, null); + } + + return callback(null, userData.MFAOptions); + }, + userAgentValue + ); + return undefined; + } + + /** + * PRIVATE ONLY: This is an internal only method and should not + * be directly called by the consumers. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {Promise} + */ + createGetUserRequest(userAgentValue) { + return this.client.promisifyRequest( + 'GetUser', + { + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + }, + userAgentValue + ); + } + + /** + * PRIVATE ONLY: This is an internal only method and should not + * be directly called by the consumers. + * @param {object} options + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {Promise} + */ + refreshSessionIfPossible(options = {}, userAgentValue) { + // best effort, if not possible + return new Promise(resolve => { + const refresh = this.signInUserSession.getRefreshToken(); + if (refresh && refresh.getToken()) { + this.refreshSession( + refresh, + resolve, + options.clientMetadata, + userAgentValue + ); + } else { + resolve(); + } + }); + } + + /** + * @typedef {Object} GetUserDataOptions + * @property {boolean} bypassCache - force getting data from Cognito service + * @property {Record} clientMetadata - clientMetadata for getSession + */ + + /** + * This is used by an authenticated users to get the userData + * @param {nodeCallback} callback Called on success or error. + * @param {GetUserDataOptions} params + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + getUserData(callback, params, userAgentValue) { + if (!(this.signInUserSession != null && this.signInUserSession.isValid())) { + this.clearCachedUserData(); + return callback(new Error('User is not authenticated'), null); + } + + const userData = this.getUserDataFromCache(); + + if (!userData) { + this.fetchUserData() + .then(data => { + callback(null, data); + }) + .catch(callback); + return; + } + + if (this.isFetchUserDataAndTokenRequired(params)) { + this.fetchUserData() + .then(data => { + return this.refreshSessionIfPossible(params, userAgentValue).then( + () => data + ); + }) + .then(data => callback(null, data)) + .catch(callback); + return; + } + + try { + callback(null, JSON.parse(userData)); + return; + } catch (err) { + this.clearCachedUserData(); + callback(err, null); + return; + } + } + + /** + * + * PRIVATE ONLY: This is an internal only method and should not + * be directly called by the consumers. + */ + getUserDataFromCache() { + const userData = this.storage.getItem(this.userDataKey); + + return userData; + } + + /** + * + * PRIVATE ONLY: This is an internal only method and should not + * be directly called by the consumers. + */ + isFetchUserDataAndTokenRequired(params) { + const { bypassCache = false } = params || {}; + + return bypassCache; + } + /** + * + * PRIVATE ONLY: This is an internal only method and should not + * be directly called by the consumers. + * @param {string} userAgentValue Optional string containing custom user agent value + */ + fetchUserData(userAgentValue) { + return this.createGetUserRequest().then(data => { + this.cacheUserData(data); + return data; + }, userAgentValue); + } + + /** + * This is used by an authenticated user to delete a list of attributes + * @param {string[]} attributeList Names of the attributes to delete. + * @param {nodeCallback} callback Called on success or error. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + deleteAttributes(attributeList, callback, userAgentValue) { + if (!(this.signInUserSession != null && this.signInUserSession.isValid())) { + return callback(new Error('User is not authenticated'), null); + } + + this.client.request( + 'DeleteUserAttributes', + { + UserAttributeNames: attributeList, + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + }, + err => { + if (err) { + return callback(err, null); + } + + // update cached user + return this.getUserData( + () => callback(null, 'SUCCESS'), + { + bypassCache: true, + }, + userAgentValue + ); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used by a user to resend a confirmation code + * @param {nodeCallback} callback Called on success or error. + * @param {ClientMetadata} clientMetadata object which is passed from client to Cognito Lambda trigger + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + resendConfirmationCode(callback, clientMetadata, userAgentValue) { + const jsonReq = { + ClientId: this.pool.getClientId(), + Username: this.username, + ClientMetadata: clientMetadata, + }; + + this.client.request( + 'ResendConfirmationCode', + jsonReq, + (err, result) => { + if (err) { + return callback(err, null); + } + return callback(null, result); + }, + userAgentValue + ); + } + + /** + * @typedef {Object} GetSessionOptions + * @property {Record} clientMetadata - clientMetadata for getSession + */ + + /** + * This is used to get a session, either from the session object + * or from the local storage, or by using a refresh token + * + * @param {nodeCallback} callback Called on success or error. + * @param {GetSessionOptions} options + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + getSession(callback, options = {}, userAgentValue) { + if (this.username == null) { + return callback( + new Error('Username is null. Cannot retrieve a new session'), + null + ); + } + + if (this.signInUserSession != null && this.signInUserSession.isValid()) { + return callback(null, this.signInUserSession); + } + + const keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}.${ + this.username + }`; + const idTokenKey = `${keyPrefix}.idToken`; + const accessTokenKey = `${keyPrefix}.accessToken`; + const refreshTokenKey = `${keyPrefix}.refreshToken`; + const clockDriftKey = `${keyPrefix}.clockDrift`; + + if (this.storage.getItem(idTokenKey)) { + const idToken = new CognitoIdToken({ + IdToken: this.storage.getItem(idTokenKey), + }); + const accessToken = new CognitoAccessToken({ + AccessToken: this.storage.getItem(accessTokenKey), + }); + const refreshToken = new CognitoRefreshToken({ + RefreshToken: this.storage.getItem(refreshTokenKey), + }); + const clockDrift = parseInt(this.storage.getItem(clockDriftKey), 0) || 0; + + const sessionData = { + IdToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + ClockDrift: clockDrift, + }; + const cachedSession = new CognitoUserSession(sessionData); + + if (cachedSession.isValid()) { + this.signInUserSession = cachedSession; + return callback(null, this.signInUserSession); + } + + if (!refreshToken.getToken()) { + return callback( + new Error('Cannot retrieve a new session. Please authenticate.'), + null + ); + } + + this.refreshSession( + refreshToken, + callback, + options.clientMetadata, + userAgentValue + ); + } else { + callback( + new Error('Local storage is missing an ID Token, Please authenticate'), + null + ); + } + + return undefined; + } + + /** + * This uses the refreshToken to retrieve a new session + * @param {CognitoRefreshToken} refreshToken A previous session's refresh token. + * @param {nodeCallback} callback Called on success or error. + * @param {ClientMetadata} clientMetadata object which is passed from client to Cognito Lambda trigger + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + refreshSession(refreshToken, callback, clientMetadata, userAgentValue) { + const wrappedCallback = this.pool.wrapRefreshSessionCallback + ? this.pool.wrapRefreshSessionCallback(callback) + : callback; + const authParameters = {}; + authParameters.REFRESH_TOKEN = refreshToken.getToken(); + const keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}`; + const lastUserKey = `${keyPrefix}.LastAuthUser`; + + if (this.storage.getItem(lastUserKey)) { + this.username = this.storage.getItem(lastUserKey); + const deviceKeyKey = `${keyPrefix}.${this.username}.deviceKey`; + this.deviceKey = this.storage.getItem(deviceKeyKey); + authParameters.DEVICE_KEY = this.deviceKey; + } + + const jsonReq = { + ClientId: this.pool.getClientId(), + AuthFlow: 'REFRESH_TOKEN_AUTH', + AuthParameters: authParameters, + ClientMetadata: clientMetadata, + }; + if (this.getUserContextData()) { + jsonReq.UserContextData = this.getUserContextData(); + } + this.client.request( + 'InitiateAuth', + jsonReq, + (err, authResult) => { + if (err) { + if (err.code === 'NotAuthorizedException') { + this.clearCachedUser(); + } + return wrappedCallback(err, null); + } + if (authResult) { + const authenticationResult = authResult.AuthenticationResult; + if ( + !Object.prototype.hasOwnProperty.call( + authenticationResult, + 'RefreshToken' + ) + ) { + authenticationResult.RefreshToken = refreshToken.getToken(); + } + this.signInUserSession = + this.getCognitoUserSession(authenticationResult); + this.cacheTokens(); + return wrappedCallback(null, this.signInUserSession); + } + return undefined; + }, + userAgentValue + ); + } + + /** + * This is used to save the session tokens to local storage + * @returns {void} + */ + cacheTokens() { + const keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}`; + const idTokenKey = `${keyPrefix}.${this.username}.idToken`; + const accessTokenKey = `${keyPrefix}.${this.username}.accessToken`; + const refreshTokenKey = `${keyPrefix}.${this.username}.refreshToken`; + const clockDriftKey = `${keyPrefix}.${this.username}.clockDrift`; + const lastUserKey = `${keyPrefix}.LastAuthUser`; + + this.storage.setItem( + idTokenKey, + this.signInUserSession.getIdToken().getJwtToken() + ); + this.storage.setItem( + accessTokenKey, + this.signInUserSession.getAccessToken().getJwtToken() + ); + this.storage.setItem( + refreshTokenKey, + this.signInUserSession.getRefreshToken().getToken() + ); + this.storage.setItem( + clockDriftKey, + `${this.signInUserSession.getClockDrift()}` + ); + this.storage.setItem(lastUserKey, this.username); + } + + /** + * This is to cache user data + */ + cacheUserData(userData) { + this.storage.setItem(this.userDataKey, JSON.stringify(userData)); + } + + /** + * This is to remove cached user data + */ + clearCachedUserData() { + this.storage.removeItem(this.userDataKey); + } + + clearCachedUser() { + this.clearCachedTokens(); + this.clearCachedUserData(); + } + + /** + * This is used to cache the device key and device group and device password + * @returns {void} + */ + cacheDeviceKeyAndPassword() { + const keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}.${ + this.username + }`; + const deviceKeyKey = `${keyPrefix}.deviceKey`; + const randomPasswordKey = `${keyPrefix}.randomPasswordKey`; + const deviceGroupKeyKey = `${keyPrefix}.deviceGroupKey`; + + this.storage.setItem(deviceKeyKey, this.deviceKey); + this.storage.setItem(randomPasswordKey, this.randomPassword); + this.storage.setItem(deviceGroupKeyKey, this.deviceGroupKey); + } + + /** + * This is used to get current device key and device group and device password + * @returns {void} + */ + getCachedDeviceKeyAndPassword() { + const keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}.${ + this.username + }`; + const deviceKeyKey = `${keyPrefix}.deviceKey`; + const randomPasswordKey = `${keyPrefix}.randomPasswordKey`; + const deviceGroupKeyKey = `${keyPrefix}.deviceGroupKey`; + + if (this.storage.getItem(deviceKeyKey)) { + this.deviceKey = this.storage.getItem(deviceKeyKey); + this.randomPassword = this.storage.getItem(randomPasswordKey); + this.deviceGroupKey = this.storage.getItem(deviceGroupKeyKey); + } + } + + /** + * This is used to clear the device key info from local storage + * @returns {void} + */ + clearCachedDeviceKeyAndPassword() { + const keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}.${ + this.username + }`; + const deviceKeyKey = `${keyPrefix}.deviceKey`; + const randomPasswordKey = `${keyPrefix}.randomPasswordKey`; + const deviceGroupKeyKey = `${keyPrefix}.deviceGroupKey`; + + this.storage.removeItem(deviceKeyKey); + this.storage.removeItem(randomPasswordKey); + this.storage.removeItem(deviceGroupKeyKey); + } + + /** + * This is used to clear the session tokens from local storage + * @returns {void} + */ + clearCachedTokens() { + const keyPrefix = `CognitoIdentityServiceProvider.${this.pool.getClientId()}`; + const idTokenKey = `${keyPrefix}.${this.username}.idToken`; + const accessTokenKey = `${keyPrefix}.${this.username}.accessToken`; + const refreshTokenKey = `${keyPrefix}.${this.username}.refreshToken`; + const lastUserKey = `${keyPrefix}.LastAuthUser`; + const clockDriftKey = `${keyPrefix}.${this.username}.clockDrift`; + + this.storage.removeItem(idTokenKey); + this.storage.removeItem(accessTokenKey); + this.storage.removeItem(refreshTokenKey); + this.storage.removeItem(lastUserKey); + this.storage.removeItem(clockDriftKey); + } + + /** + * This is used to build a user session from tokens retrieved in the authentication result + * @param {object} authResult Successful auth response from server. + * @returns {CognitoUserSession} The new user session. + * @private + */ + getCognitoUserSession(authResult) { + const idToken = new CognitoIdToken(authResult); + const accessToken = new CognitoAccessToken(authResult); + const refreshToken = new CognitoRefreshToken(authResult); + + const sessionData = { + IdToken: idToken, + AccessToken: accessToken, + RefreshToken: refreshToken, + }; + + return new CognitoUserSession(sessionData); + } + + /** + * This is used to initiate a forgot password request + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {inputVerificationCode?} callback.inputVerificationCode + * Optional callback raised instead of onSuccess with response data. + * @param {onSuccess} callback.onSuccess Called on success. + * @param {ClientMetadata} clientMetadata object which is passed from client to Cognito Lambda trigger + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + forgotPassword(callback, clientMetadata, userAgentValue) { + const jsonReq = { + ClientId: this.pool.getClientId(), + Username: this.username, + ClientMetadata: clientMetadata, + }; + if (this.getUserContextData()) { + jsonReq.UserContextData = this.getUserContextData(); + } + this.client.request( + 'ForgotPassword', + jsonReq, + (err, data) => { + if (err) { + return callback.onFailure(err); + } + if (typeof callback.inputVerificationCode === 'function') { + return callback.inputVerificationCode(data); + } + return callback.onSuccess(data); + }, + userAgentValue + ); + } + + /** + * This is used to confirm a new password using a confirmationCode + * @param {string} confirmationCode Code entered by user. + * @param {string} newPassword Confirm new password. + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {onSuccess} callback.onSuccess Called on success. + * @param {ClientMetadata} clientMetadata object which is passed from client to Cognito Lambda trigger + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + confirmPassword( + confirmationCode, + newPassword, + callback, + clientMetadata, + userAgentValue + ) { + const jsonReq = { + ClientId: this.pool.getClientId(), + Username: this.username, + ConfirmationCode: confirmationCode, + Password: newPassword, + ClientMetadata: clientMetadata, + }; + if (this.getUserContextData()) { + jsonReq.UserContextData = this.getUserContextData(); + } + this.client.request( + 'ConfirmForgotPassword', + jsonReq, + err => { + if (err) { + return callback.onFailure(err); + } + return callback.onSuccess('SUCCESS'); + }, + userAgentValue + ); + } + + /** + * This is used to initiate an attribute confirmation request + * @param {string} attributeName User attribute that needs confirmation. + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {inputVerificationCode} callback.inputVerificationCode Called on success. + * @param {ClientMetadata} clientMetadata object which is passed from client to Cognito Lambda trigger + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + getAttributeVerificationCode( + attributeName, + callback, + clientMetadata, + userAgentValue + ) { + if (this.signInUserSession == null || !this.signInUserSession.isValid()) { + return callback.onFailure(new Error('User is not authenticated')); + } + + this.client.request( + 'GetUserAttributeVerificationCode', + { + AttributeName: attributeName, + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + ClientMetadata: clientMetadata, + }, + (err, data) => { + if (err) { + return callback.onFailure(err); + } + if (typeof callback.inputVerificationCode === 'function') { + return callback.inputVerificationCode(data); + } + return callback.onSuccess('SUCCESS'); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used to confirm an attribute using a confirmation code + * @param {string} attributeName Attribute being confirmed. + * @param {string} confirmationCode Code entered by user. + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {onSuccess} callback.onSuccess Called on success. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + verifyAttribute(attributeName, confirmationCode, callback, userAgentValue) { + if (this.signInUserSession == null || !this.signInUserSession.isValid()) { + return callback.onFailure(new Error('User is not authenticated')); + } + + this.client.request( + 'VerifyUserAttribute', + { + AttributeName: attributeName, + Code: confirmationCode, + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + }, + err => { + if (err) { + return callback.onFailure(err); + } + return callback.onSuccess('SUCCESS'); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used to get the device information using the current device key + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {onSuccess<*>} callback.onSuccess Called on success with device data. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + getDevice(callback, userAgentValue) { + if (this.signInUserSession == null || !this.signInUserSession.isValid()) { + return callback.onFailure(new Error('User is not authenticated')); + } + + this.client.request( + 'GetDevice', + { + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + DeviceKey: this.deviceKey, + }, + (err, data) => { + if (err) { + return callback.onFailure(err); + } + return callback.onSuccess(data); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used to forget a specific device + * @param {string} deviceKey Device key. + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {onSuccess} callback.onSuccess Called on success. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + forgetSpecificDevice(deviceKey, callback, userAgentValue) { + if (this.signInUserSession == null || !this.signInUserSession.isValid()) { + return callback.onFailure(new Error('User is not authenticated')); + } + + this.client.request( + 'ForgetDevice', + { + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + DeviceKey: deviceKey, + }, + err => { + if (err) { + return callback.onFailure(err); + } + return callback.onSuccess('SUCCESS'); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used to forget the current device + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {onSuccess} callback.onSuccess Called on success. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + forgetDevice(callback, userAgentValue) { + this.forgetSpecificDevice( + this.deviceKey, + { + onFailure: callback.onFailure, + onSuccess: result => { + this.deviceKey = null; + this.deviceGroupKey = null; + this.randomPassword = null; + this.clearCachedDeviceKeyAndPassword(); + return callback.onSuccess(result); + }, + }, + userAgentValue + ); + } + + /** + * This is used to set the device status as remembered + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {onSuccess} callback.onSuccess Called on success. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + setDeviceStatusRemembered(callback, userAgentValue) { + if (this.signInUserSession == null || !this.signInUserSession.isValid()) { + return callback.onFailure(new Error('User is not authenticated')); + } + + this.client.request( + 'UpdateDeviceStatus', + { + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + DeviceKey: this.deviceKey, + DeviceRememberedStatus: 'remembered', + }, + err => { + if (err) { + return callback.onFailure(err); + } + return callback.onSuccess('SUCCESS'); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used to set the device status as not remembered + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {onSuccess} callback.onSuccess Called on success. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + setDeviceStatusNotRemembered(callback, userAgentValue) { + if (this.signInUserSession == null || !this.signInUserSession.isValid()) { + return callback.onFailure(new Error('User is not authenticated')); + } + + this.client.request( + 'UpdateDeviceStatus', + { + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + DeviceKey: this.deviceKey, + DeviceRememberedStatus: 'not_remembered', + }, + err => { + if (err) { + return callback.onFailure(err); + } + return callback.onSuccess('SUCCESS'); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used to list all devices for a user + * + * @param {int} limit the number of devices returned in a call + * @param {string | null} paginationToken the pagination token in case any was returned before + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {onSuccess<*>} callback.onSuccess Called on success with device list. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + listDevices(limit, paginationToken, callback, userAgentValue) { + if (this.signInUserSession == null || !this.signInUserSession.isValid()) { + return callback.onFailure(new Error('User is not authenticated')); + } + const requestParams = { + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + Limit: limit, + }; + + if (paginationToken) { + requestParams.PaginationToken = paginationToken; + } + + this.client.request( + 'ListDevices', + requestParams, + (err, data) => { + if (err) { + return callback.onFailure(err); + } + return callback.onSuccess(data); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used to globally revoke all tokens issued to a user + * @param {object} callback Result callback map. + * @param {onFailure} callback.onFailure Called on any error. + * @param {onSuccess} callback.onSuccess Called on success. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + globalSignOut(callback, userAgentValue) { + if (this.signInUserSession == null || !this.signInUserSession.isValid()) { + return callback.onFailure(new Error('User is not authenticated')); + } + + this.client.request( + 'GlobalSignOut', + { + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + }, + err => { + if (err) { + return callback.onFailure(err); + } + this.clearCachedUser(); + return callback.onSuccess('SUCCESS'); + }, + userAgentValue + ); + return undefined; + } + + /** + * This is used for the user to signOut of the application and clear the cached tokens. + * @param revokeTokenCallback + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + signOut(revokeTokenCallback, userAgentValue) { + // If tokens won't be revoked, we just clean the client data. + if (!revokeTokenCallback || typeof revokeTokenCallback !== 'function') { + this.cleanClientData(); + + return; + } + + this.getSession((error, _session) => { + if (error) { + return revokeTokenCallback(error); + } + + this.revokeTokens(err => { + this.cleanClientData(); + + revokeTokenCallback(err); + }, userAgentValue); + }); + } + + revokeTokens(revokeTokenCallback = () => {}, userAgentValue) { + if (typeof revokeTokenCallback !== 'function') { + throw new Error('Invalid revokeTokenCallback. It should be a function.'); + } + + const tokensToBeRevoked = []; + + if (!this.signInUserSession) { + const error = new Error('User is not authenticated'); + + return revokeTokenCallback(error); + } + + if (!this.signInUserSession.getAccessToken()) { + const error = new Error('No Access token available'); + + return revokeTokenCallback(error); + } + + const refreshToken = this.signInUserSession.getRefreshToken().getToken(); + const accessToken = this.signInUserSession.getAccessToken(); + + if (this.isSessionRevocable(accessToken)) { + if (refreshToken) { + return this.revokeToken( + { + token: refreshToken, + callback: revokeTokenCallback, + }, + userAgentValue + ); + } + } + revokeTokenCallback(); + } + + isSessionRevocable(token) { + if (token && typeof token.decodePayload === 'function') { + try { + const { origin_jti } = token.decodePayload(); + return !!origin_jti; + } catch (err) { + // Nothing to do, token doesnt have origin_jti claim + } + } + + return false; + } + + cleanClientData() { + this.signInUserSession = null; + this.clearCachedUser(); + } + + revokeToken({ token, callback }, userAgentValue) { + this.client.requestWithRetry( + 'RevokeToken', + { + Token: token, + ClientId: this.pool.getClientId(), + }, + err => { + if (err) { + return callback(err); + } + + callback(); + }, + userAgentValue + ); + } + + /** + * This is used by a user trying to select a given MFA + * @param {string} answerChallenge the mfa the user wants + * @param {nodeCallback} callback Called on success or error. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + sendMFASelectionAnswer(answerChallenge, callback, userAgentValue) { + const challengeResponses = {}; + challengeResponses.USERNAME = this.username; + challengeResponses.ANSWER = answerChallenge; + + const jsonReq = { + ChallengeName: 'SELECT_MFA_TYPE', + ChallengeResponses: challengeResponses, + ClientId: this.pool.getClientId(), + Session: this.Session, + }; + if (this.getUserContextData()) { + jsonReq.UserContextData = this.getUserContextData(); + } + this.client.request( + 'RespondToAuthChallenge', + jsonReq, + (err, data) => { + if (err) { + return callback.onFailure(err); + } + this.Session = data.Session; + if (answerChallenge === 'SMS_MFA') { + return callback.mfaRequired( + data.ChallengeName, + data.ChallengeParameters + ); + } + if (answerChallenge === 'SOFTWARE_TOKEN_MFA') { + return callback.totpRequired( + data.ChallengeName, + data.ChallengeParameters + ); + } + return undefined; + }, + userAgentValue + ); + } + + /** + * This returns the user context data for advanced security feature. + * @returns {string} the user context data from CognitoUserPool + */ + getUserContextData() { + const pool = this.pool; + return pool.getUserContextData(this.username); + } + + /** + * This is used by an authenticated or a user trying to authenticate to associate a TOTP MFA + * @param {nodeCallback} callback Called on success or error. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + associateSoftwareToken(callback, userAgentValue) { + if (!(this.signInUserSession != null && this.signInUserSession.isValid())) { + this.client.request( + 'AssociateSoftwareToken', + { + Session: this.Session, + }, + (err, data) => { + if (err) { + return callback.onFailure(err); + } + this.Session = data.Session; + return callback.associateSecretCode(data.SecretCode); + }, + userAgentValue + ); + } else { + this.client.request( + 'AssociateSoftwareToken', + { + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + }, + (err, data) => { + if (err) { + return callback.onFailure(err); + } + return callback.associateSecretCode(data.SecretCode); + }, + userAgentValue + ); + } + } + + /** + * This is used by an authenticated or a user trying to authenticate to verify a TOTP MFA + * @param {string} totpCode The MFA code entered by the user. + * @param {string} friendlyDeviceName The device name we are assigning to the device. + * @param {nodeCallback} callback Called on success or error. + * @param {string} userAgentValue Optional string containing custom user agent value + * @returns {void} + */ + verifySoftwareToken(totpCode, friendlyDeviceName, callback, userAgentValue) { + if (!(this.signInUserSession != null && this.signInUserSession.isValid())) { + this.client.request( + 'VerifySoftwareToken', + { + Session: this.Session, + UserCode: totpCode, + FriendlyDeviceName: friendlyDeviceName, + }, + (err, data) => { + if (err) { + return callback.onFailure(err); + } + this.Session = data.Session; + const challengeResponses = {}; + challengeResponses.USERNAME = this.username; + const jsonReq = { + ChallengeName: 'MFA_SETUP', + ClientId: this.pool.getClientId(), + ChallengeResponses: challengeResponses, + Session: this.Session, + }; + if (this.getUserContextData()) { + jsonReq.UserContextData = this.getUserContextData(); + } + this.client.request( + 'RespondToAuthChallenge', + jsonReq, + (errRespond, dataRespond) => { + if (errRespond) { + return callback.onFailure(errRespond); + } + this.signInUserSession = this.getCognitoUserSession( + dataRespond.AuthenticationResult + ); + this.cacheTokens(); + return callback.onSuccess(this.signInUserSession); + }, + userAgentValue + ); + return undefined; + }, + userAgentValue + ); + } else { + this.client.request( + 'VerifySoftwareToken', + { + AccessToken: this.signInUserSession.getAccessToken().getJwtToken(), + UserCode: totpCode, + FriendlyDeviceName: friendlyDeviceName, + }, + (err, data) => { + if (err) { + return callback.onFailure(err); + } + return callback.onSuccess(data); + }, + userAgentValue + ); + } + } +} diff --git a/packages/amazon-cognito-identity-js/src/internals/index.js b/packages/amazon-cognito-identity-js/src/internals/index.js index 42084ebe8da..5de97fc3cc3 100644 --- a/packages/amazon-cognito-identity-js/src/internals/index.js +++ b/packages/amazon-cognito-identity-js/src/internals/index.js @@ -2,3 +2,5 @@ export { addAuthCategoryToCognitoUserAgent, addFrameworkToCognitoUserAgent, } from '../UserAgent'; + +export { InternalCognitoUser } from './InternalCognitoUser'; diff --git a/packages/api-graphql/package.json b/packages/api-graphql/package.json index b8c8ad8b43b..ff9b88bf6eb 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": "88.35 kB" + "limit": "89.51 kB" } ], "jest": { diff --git a/packages/api/package.json b/packages/api/package.json index 7a7ada7de39..37ddb1461a4 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": "89.07 kB" + "limit": "90.27 kB" } ], "jest": { diff --git a/packages/auth/__tests__/auth-attribute-test.ts b/packages/auth/__tests__/auth-attribute-test.ts index 0050cb6ab8f..10963bcfd42 100644 --- a/packages/auth/__tests__/auth-attribute-test.ts +++ b/packages/auth/__tests__/auth-attribute-test.ts @@ -10,6 +10,7 @@ import { } from 'amazon-cognito-identity-js'; import { AuthOptions } from '../src/types'; +import { InternalAuthClass } from '../src/internals/InternalAuth'; const authOptions: AuthOptions = { userPoolId: 'us-west-2_0xxxxxxxx', @@ -22,7 +23,7 @@ const authOptions: AuthOptions = { describe('User-Attribute-validation', () => { it('Check-non-verified-attributes', async () => { const spyonAuthUserAttributes = jest - .spyOn(Auth.prototype, 'userAttributes') + .spyOn(InternalAuthClass.prototype, 'userAttributes') .mockImplementation((user: CognitoUser) => { const emailAttribute = new CognitoUserAttribute({ Name: 'email', diff --git a/packages/auth/__tests__/auth-unit-test.ts b/packages/auth/__tests__/auth-unit-test.ts index ecafed17013..eaf6b022370 100644 --- a/packages/auth/__tests__/auth-unit-test.ts +++ b/packages/auth/__tests__/auth-unit-test.ts @@ -285,6 +285,7 @@ import { 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'; const authOptions: AuthOptions = { userPoolId: 'awsUserPoolsId', @@ -1407,38 +1408,38 @@ describe('auth unit test', () => { spyon.mockClear(); }); - test('currentUserPoolUser fails but hub event still dispatches', async () => { - const auth = new Auth(authOptions); - const spyon = jest - .spyOn(CognitoUser.prototype, 'sendMFACode') - .mockImplementationOnce((code, callback) => { - callback.onSuccess(session); - }); - - const spyon2 = jest - .spyOn(auth, 'currentUserPoolUser') - .mockImplementationOnce(() => { - return Promise.reject('Could not get current user.'); - }); - const hubSpy = jest.spyOn(Hub, 'dispatch'); - const user = new CognitoUser({ - Username: 'username', - Pool: userPool, - }); - const result = await auth.confirmSignIn(user, 'code', null); - expect(result).toEqual(user); - expect(hubSpy).toHaveBeenCalledWith( - 'auth', - { - data: user, - event: 'signIn', - message: 'A user username has been signed in', - }, - 'Auth', - Symbol.for('amplify_default') - ); - spyon.mockClear(); - }); + test('currentUserPoolUser fails but hub event still dispatches', async () => { + const auth = new Auth(authOptions); + const spyon = jest + .spyOn(CognitoUser.prototype, 'sendMFACode') + .mockImplementationOnce((code, callback) => { + callback.onSuccess(session); + }); + + const spyon2 = jest + .spyOn(auth, 'currentUserPoolUser') + .mockImplementationOnce(() => { + return Promise.reject('Could not get current user.'); + }); + const hubSpy = jest.spyOn(Hub, 'dispatch'); + const user = new CognitoUser({ + Username: 'username', + Pool: userPool, + }); + const result = await auth.confirmSignIn(user, 'code', null); + expect(result).toEqual(user); + expect(hubSpy).toHaveBeenCalledWith( + 'auth', + { + data: user, + event: 'signIn', + message: 'A user username has been signed in', + }, + 'Auth', + Symbol.for('amplify_default') + ); + spyon.mockClear(); + }); test('onFailure', async () => { const spyon = jest @@ -1646,7 +1647,7 @@ describe('auth unit test', () => { describe('userAttributes', () => { test('happy case', async () => { const spyon = jest - .spyOn(Auth.prototype, 'userSession') + .spyOn(InternalAuthClass.prototype, 'userSession') .mockImplementationOnce(user => { return new Promise((res: any, rej) => { res('session'); @@ -1670,7 +1671,7 @@ describe('auth unit test', () => { test('get userattributes failed', async () => { const spyon = jest - .spyOn(Auth.prototype, 'userSession') + .spyOn(InternalAuthClass.prototype, 'userSession') .mockImplementationOnce(user => { return new Promise((res: any, rej) => { res('session'); @@ -1720,7 +1721,7 @@ describe('auth unit test', () => { }); const spyon2 = jest - .spyOn(Auth.prototype, 'userSession') + .spyOn(InternalAuthClass.prototype, 'userSession') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(session); @@ -1809,7 +1810,7 @@ describe('auth unit test', () => { }); const spyon = jest - .spyOn(Auth.prototype, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype, 'currentUserPoolUser') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(user); @@ -2299,7 +2300,7 @@ describe('auth unit test', () => { }); const spyon = jest - .spyOn(Auth.prototype, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype, 'currentUserPoolUser') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(user); @@ -2307,7 +2308,7 @@ describe('auth unit test', () => { }); const spyon2 = jest - .spyOn(Auth.prototype, 'verifyUserAttribute') + .spyOn(InternalAuthClass.prototype, 'verifyUserAttribute') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(); @@ -2318,7 +2319,7 @@ describe('auth unit test', () => { expect.assertions(2); expect(spyon).toBeCalled(); - expect(spyon2).toBeCalledWith(user, 'attr'); + expect(spyon2).toBeCalledWith(user, 'attr', undefined); spyon.mockClear(); spyon2.mockClear(); @@ -2334,7 +2335,7 @@ describe('auth unit test', () => { }); const spyon = jest - .spyOn(Auth.prototype, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype, 'currentUserPoolUser') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(user); @@ -2342,7 +2343,7 @@ describe('auth unit test', () => { }); const spyon2 = jest - .spyOn(Auth.prototype, 'verifyUserAttributeSubmit') + .spyOn(InternalAuthClass.prototype, 'verifyUserAttributeSubmit') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(); @@ -2414,7 +2415,7 @@ describe('auth unit test', () => { }; const spyonAuth = jest - .spyOn(Auth.prototype, 'currentUserCredentials') + .spyOn(InternalAuthClass.prototype, 'currentUserCredentials') .mockImplementationOnce(() => { return new Promise((resolve, reject) => { resolve(); @@ -2505,7 +2506,7 @@ describe('auth unit test', () => { const newPassword = 'newPassword1.'; const spyon = jest - .spyOn(Auth.prototype, 'userSession') + .spyOn(InternalAuthClass.prototype, 'userSession') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(session); @@ -2835,7 +2836,7 @@ describe('auth unit test', () => { }); const spyon = jest - .spyOn(Auth.prototype, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype, 'currentUserPoolUser') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(user); @@ -2843,7 +2844,7 @@ describe('auth unit test', () => { }); const spyon2 = jest - .spyOn(Auth.prototype, 'userAttributes') + .spyOn(InternalAuthClass.prototype, 'userAttributes') .mockImplementationOnce(() => { auth['credentials'] = { IdentityPoolId: 'identityPoolId', @@ -2902,7 +2903,7 @@ describe('auth unit test', () => { }); const spyon = jest - .spyOn(Auth.prototype, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype, 'currentUserPoolUser') .mockImplementationOnce(() => { return new Promise((res, rej) => { res({ @@ -2912,7 +2913,7 @@ describe('auth unit test', () => { }); const spyon2 = jest - .spyOn(Auth.prototype, 'userAttributes') + .spyOn(InternalAuthClass.prototype, 'userAttributes') .mockImplementationOnce(() => { return new Promise((res, rej) => { rej('err'); @@ -2920,7 +2921,7 @@ describe('auth unit test', () => { }); const spyon3 = jest - .spyOn(Auth.prototype, 'currentCredentials') + .spyOn(InternalAuthClass.prototype, 'currentCredentials') .mockImplementationOnce(() => { return Promise.resolve({ IdentityPoolId: 'identityPoolId', @@ -2952,7 +2953,7 @@ describe('auth unit test', () => { auth['credentials_source'] = 'aws'; const spyon = jest - .spyOn(Auth.prototype, 'currentUserPoolUser') + .spyOn(InternalAuthClass.prototype, 'currentUserPoolUser') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(null); @@ -3009,7 +3010,7 @@ describe('auth unit test', () => { }; const spyon = jest - .spyOn(Auth.prototype, 'userSession') + .spyOn(InternalAuthClass.prototype, 'userSession') .mockImplementationOnce(() => { return new Promise((res, rej) => { res(session); @@ -3058,12 +3059,13 @@ describe('auth unit test', () => { spyon.mockClear(); }); - test('error hub event', async (done) => { + test('error hub event', async done => { expect.assertions(3); - const spyon = jest.spyOn(CognitoUser.prototype, 'updateAttributes') + const spyon = jest + .spyOn(CognitoUser.prototype, 'updateAttributes') .mockImplementationOnce((attrs, callback: any) => { callback(new Error('Error'), null, null); - }); + }); const auth = new Auth(authOptions); @@ -3097,19 +3099,20 @@ describe('auth unit test', () => { spyon.mockClear(); }); - test('happy case code delivery details hub event', async (done) => { + test('happy case code delivery details hub event', async done => { expect.assertions(2); - + const codeDeliverDetailsResult: any = { - 'CodeDeliveryDetailsList': [ - { - 'AttributeName': 'email', - 'DeliveryMedium': 'EMAIL', - 'Destination': 'e***@e***' - } - ] + CodeDeliveryDetailsList: [ + { + AttributeName: 'email', + DeliveryMedium: 'EMAIL', + Destination: 'e***@e***', + }, + ], }; - const spyon = jest.spyOn(CognitoUser.prototype, 'updateAttributes') + const spyon = jest + .spyOn(CognitoUser.prototype, 'updateAttributes') .mockImplementationOnce((attrs, callback: any) => { callback(null, 'SUCCESS', codeDeliverDetailsResult); }); @@ -3126,20 +3129,20 @@ describe('auth unit test', () => { sub: 'sub', }; const payloadData = { - 'email': { + email: { isUpdated: false, codeDeliveryDetails: { AttributeName: 'email', DeliveryMedium: 'EMAIL', - Destination: 'e***@e***' - } + Destination: 'e***@e***', + }, }, - 'phone_number': { - isUpdated: true + phone_number: { + isUpdated: true, + }, + sub: { + isUpdated: true, }, - 'sub': { - isUpdated: true - } }; const listenToHub = Hub.listen('auth', ({ payload }) => { const { event } = payload; @@ -3167,7 +3170,7 @@ describe('auth unit test', () => { const attributeNames = ['email', 'phone_number']; const spyon = jest - .spyOn(Auth.prototype, 'userSession') + .spyOn(InternalAuthClass.prototype, 'userSession') .mockImplementationOnce(() => { return new Promise(res => { res(session); @@ -3414,7 +3417,7 @@ describe('auth unit test', () => { return Promise.resolve('cred' as any); }); const spyon2 = jest - .spyOn(Auth.prototype, 'currentAuthenticatedUser') + .spyOn(InternalAuthClass.prototype, 'currentAuthenticatedUser') .mockImplementation(() => { if (!user) return Promise.reject('error'); else return Promise.resolve(user); @@ -3488,7 +3491,7 @@ describe('auth unit test', () => { return Promise.resolve('cred' as any); }); const spyon2 = jest - .spyOn(Auth.prototype, 'currentAuthenticatedUser') + .spyOn(InternalAuthClass.prototype, 'currentAuthenticatedUser') .mockImplementation(() => { if (!user) return Promise.reject('error'); else return Promise.resolve(user); @@ -3514,7 +3517,7 @@ describe('auth unit test', () => { describe('handleAuthResponse test', () => { beforeAll(() => { jest - .spyOn(Auth.prototype, 'currentAuthenticatedUser') + .spyOn(InternalAuthClass.prototype, 'currentAuthenticatedUser') .mockImplementation(() => { throw new Error('no user logged in'); }); @@ -3727,7 +3730,7 @@ describe('auth unit test', () => { describe('verifiedContact test', () => { test('happy case with unverified', async () => { const spyon = jest - .spyOn(Auth.prototype, 'userAttributes') + .spyOn(InternalAuthClass.prototype, 'userAttributes') .mockImplementationOnce(() => { return new Promise((res: any, rej) => { res([ @@ -3759,7 +3762,7 @@ describe('auth unit test', () => { test('happy case with verified', async () => { const spyon = jest - .spyOn(Auth.prototype, 'userAttributes') + .spyOn(InternalAuthClass.prototype, 'userAttributes') .mockImplementationOnce(() => { return new Promise((res: any, rej) => { res([ @@ -3799,7 +3802,7 @@ describe('auth unit test', () => { test('happy case with verified as strings', async () => { const spyon = jest - .spyOn(Auth.prototype, 'userAttributes') + .spyOn(InternalAuthClass.prototype, 'userAttributes') .mockImplementationOnce(() => { return new Promise((res: any, rej) => { res([ diff --git a/packages/auth/__tests__/totp-unit-test.ts b/packages/auth/__tests__/totp-unit-test.ts index 149a06a1994..892de6a4587 100644 --- a/packages/auth/__tests__/totp-unit-test.ts +++ b/packages/auth/__tests__/totp-unit-test.ts @@ -195,6 +195,7 @@ import { CognitoAccessToken, } from 'amazon-cognito-identity-js'; import { Hub } from '@aws-amplify/core'; +import { InternalAuthClass } from '../src/internals/InternalAuth'; const authOptions: any = { Auth: { @@ -429,7 +430,7 @@ describe('auth unit test', () => { const spyon = jest.spyOn(CognitoUser.prototype, 'setUserMfaPreference'); const spyon2 = jest - .spyOn(Auth.prototype, 'getPreferredMFA') + .spyOn(InternalAuthClass.prototype, 'getPreferredMFA') .mockImplementationOnce(() => { return Promise.resolve('SMS_MFA'); }); @@ -454,7 +455,7 @@ describe('auth unit test', () => { callback(new Error('err'), null); }); const spyon2 = jest - .spyOn(Auth.prototype, 'getPreferredMFA') + .spyOn(InternalAuthClass.prototype, 'getPreferredMFA') .mockImplementationOnce(() => { return Promise.resolve('SMS_MFA'); }); diff --git a/packages/auth/internals/package.json b/packages/auth/internals/package.json new file mode 100644 index 00000000000..5ed72d2184b --- /dev/null +++ b/packages/auth/internals/package.json @@ -0,0 +1,8 @@ +{ + "name": "@aws-amplify/auth/internals", + "types": "../lib-esm/internals/index.d.ts", + "main": "../lib/internals/index.js", + "module": "../lib-esm/internals/index.js", + "react-native": "../lib-esm/internals/index.js", + "sideEffects": false +} diff --git a/packages/auth/package.json b/packages/auth/package.json index dc445c5e39c..e3419e0616e 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -43,7 +43,8 @@ "files": [ "lib", "lib-esm", - "src" + "src", + "internals" ], "dependencies": { "@aws-amplify/core": "5.8.0", @@ -59,7 +60,7 @@ "name": "Auth (top-level class)", "path": "./lib-esm/index.js", "import": "{ Amplify, Auth }", - "limit": "55.15 kB" + "limit": "56.29 kB" } ], "jest": { diff --git a/packages/auth/src/Auth.ts b/packages/auth/src/Auth.ts index 685fff7eaf1..509bd752167 100644 --- a/packages/auth/src/Auth.ts +++ b/packages/auth/src/Auth.ts @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 import { - AuthOptions, FederatedResponse, SignUpParams, FederatedUser, @@ -11,300 +10,33 @@ import { CurrentUserOpts, GetPreferredMFAOpts, SignInOpts, - isUsernamePasswordOpts, - isCognitoHostedOpts, - isFederatedSignInOptions, - isFederatedSignInOptionsCustom, - hasCustomState, FederatedSignInOptionsCustom, LegacyProvider, FederatedSignInOptions, - AwsCognitoOAuthOpts, ClientMetaData, } from './types'; +import { Amplify, ICredentials } from '@aws-amplify/core'; import { - Amplify, - ConsoleLogger as Logger, - Credentials, - Hub, - StorageHelper, - ICredentials, - Platform, - browserOrNode, - parseAWSExports, - UniversalStorage, - urlSafeDecode, - HubCallback, -} from '@aws-amplify/core'; -import { - CookieStorage, - CognitoUserPool, - AuthenticationDetails, - ICognitoUserPoolData, - ICognitoUserData, ISignUpResult, CognitoUser, MFAOption, CognitoUserSession, - IAuthenticationCallback, - ICognitoUserAttributeData, CognitoUserAttribute, - CognitoIdToken, - CognitoRefreshToken, - CognitoAccessToken, - NodeCallback, - CodeDeliveryDetails, } from 'amazon-cognito-identity-js'; -import { - addAuthCategoryToCognitoUserAgent, - addFrameworkToCognitoUserAgent, -} from 'amazon-cognito-identity-js/internals'; - -import { parse } from 'url'; -import OAuth from './OAuth/OAuth'; -import { default as urlListener } from './urlListener'; -import { AuthError, NoUserPoolError } from './Errors'; -import { - AuthErrorTypes, - AutoSignInOptions, - CognitoHostedUIIdentityProvider, - IAuthDevice, -} from './types/Auth'; - -const logger = new Logger('AuthClass'); -const USER_ADMIN_SCOPE = 'aws.cognito.signin.user.admin'; - -// 10 sec, following this guide https://www.nngroup.com/articles/response-times-3-important-limits/ -const OAUTH_FLOW_MS_TIMEOUT = 10 * 1000; - -const AMPLIFY_SYMBOL = ( - typeof Symbol !== 'undefined' && typeof Symbol.for === 'function' - ? Symbol.for('amplify_default') - : '@@amplify_default' -) as Symbol; - -const dispatchAuthEvent = (event: string, data: any, message: string) => { - Hub.dispatch('auth', { event, data, message }, 'Auth', AMPLIFY_SYMBOL); -}; -// Cognito Documentation for max device -// tslint:disable-next-line:max-line-length -// https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ListDevices.html#API_ListDevices_RequestSyntax -const MAX_DEVICES = 60; - -const MAX_AUTOSIGNIN_POLLING_MS = 3 * 60 * 1000; +import { AuthError } from './Errors'; +import { IAuthDevice } from './types/Auth'; +import { InternalAuthClass } from './internals/InternalAuth'; /** * Provide authentication steps */ -export class AuthClass { - private _config: AuthOptions; - private userPool: CognitoUserPool = null; - private user: any = null; - private _oAuthHandler: OAuth; - private _storage; - private _storageSync; - private oAuthFlowInProgress: boolean = false; - private pendingSignIn: ReturnType | null; - private autoSignInInitiated: boolean = false; - private inflightSessionPromise: Promise | null = null; - private inflightSessionPromiseCounter: number = 0; - Credentials = Credentials; - - /** - * Initialize Auth with AWS configurations - * @param {Object} config - Configuration of the Auth - */ - constructor(config: AuthOptions) { - this.configure(config); - this.currentCredentials = this.currentCredentials.bind(this); - this.currentUserCredentials = this.currentUserCredentials.bind(this); - - Hub.listen('auth', ({ payload }) => { - const { event } = payload; - switch (event) { - case 'verify': - case 'signIn': - this._storage.setItem('amplify-signin-with-hostedUI', 'false'); - break; - case 'signOut': - this._storage.removeItem('amplify-signin-with-hostedUI'); - break; - case 'cognitoHostedUI': - this._storage.setItem('amplify-signin-with-hostedUI', 'true'); - break; - } - }); - - addAuthCategoryToCognitoUserAgent(); - addFrameworkToCognitoUserAgent(Platform.framework); - Platform.observeFrameworkChanges(() => { - addFrameworkToCognitoUserAgent(Platform.framework); - }); - } - +export class AuthClass extends InternalAuthClass { public getModuleName() { return 'Auth'; } - configure(config?) { - if (!config) return this._config || {}; - logger.debug('configure Auth'); - const conf = Object.assign( - {}, - this._config, - parseAWSExports(config).Auth, - config - ); - this._config = conf; - const { - userPoolId, - userPoolWebClientId, - cookieStorage, - oauth, - region, - identityPoolId, - mandatorySignIn, - refreshHandlers, - identityPoolRegion, - clientMetadata, - endpoint, - storage, - } = this._config; - - if (!storage) { - // backward compatability - if (cookieStorage) this._storage = new CookieStorage(cookieStorage); - else { - this._storage = config.ssr - ? new UniversalStorage() - : new StorageHelper().getStorage(); - } - } else { - if (!this._isValidAuthStorage(storage)) { - logger.error('The storage in the Auth config is not valid!'); - throw new Error('Empty storage object'); - } - this._storage = storage; - } - - this._storageSync = Promise.resolve(); - if (typeof this._storage['sync'] === 'function') { - this._storageSync = this._storage['sync'](); - } - - if (userPoolId) { - const userPoolData: ICognitoUserPoolData = { - UserPoolId: userPoolId, - ClientId: userPoolWebClientId, - endpoint, - }; - userPoolData.Storage = this._storage; - - this.userPool = new CognitoUserPool( - userPoolData, - this.wrapRefreshSessionCallback - ); - } - - this.Credentials.configure({ - mandatorySignIn, - region, - userPoolId, - identityPoolId, - refreshHandlers, - storage: this._storage, - identityPoolRegion, - }); - - // initialize cognitoauth client if hosted ui options provided - // to keep backward compatibility: - const cognitoHostedUIConfig = oauth - ? isCognitoHostedOpts(this._config.oauth) - ? oauth - : (oauth).awsCognito - : undefined; - - if (cognitoHostedUIConfig) { - const cognitoAuthParams = Object.assign( - { - cognitoClientId: userPoolWebClientId, - UserPoolId: userPoolId, - domain: cognitoHostedUIConfig['domain'], - scopes: cognitoHostedUIConfig['scope'], - redirectSignIn: cognitoHostedUIConfig['redirectSignIn'], - redirectSignOut: cognitoHostedUIConfig['redirectSignOut'], - responseType: cognitoHostedUIConfig['responseType'], - Storage: this._storage, - urlOpener: cognitoHostedUIConfig['urlOpener'], - clientMetadata, - }, - cognitoHostedUIConfig['options'] - ); - - this._oAuthHandler = new OAuth({ - scopes: cognitoAuthParams.scopes, - config: cognitoAuthParams, - cognitoClientId: cognitoAuthParams.cognitoClientId, - }); - - // **NOTE** - Remove this in a future major release as it is a breaking change - // Prevents _handleAuthResponse from being called multiple times in Expo - // See https://github.com/aws-amplify/amplify-js/issues/4388 - const usedResponseUrls = {}; - urlListener(({ url }) => { - if (usedResponseUrls[url]) { - return; - } - - usedResponseUrls[url] = true; - this._handleAuthResponse(url); - }); - } - - dispatchAuthEvent( - 'configured', - null, - `The Auth category has been configured successfully` - ); - - if ( - !this.autoSignInInitiated && - typeof this._storage['getItem'] === 'function' - ) { - const pollingInitiated = this.isTrueStorageValue( - 'amplify-polling-started' - ); - if (pollingInitiated) { - dispatchAuthEvent( - 'autoSignIn_failure', - null, - AuthErrorTypes.AutoSignInError - ); - this._storage.removeItem('amplify-auto-sign-in'); - } - this._storage.removeItem('amplify-polling-started'); - } - return this._config; - } - - wrapRefreshSessionCallback = (callback: NodeCallback.Any) => { - const wrapped: NodeCallback.Any = (error, data) => { - if (data) { - dispatchAuthEvent('tokenRefresh', undefined, `New token retrieved`); - } else { - dispatchAuthEvent( - 'tokenRefresh_failure', - error, - `Failed to retrieve new token` - ); - } - return callback(error, data); - }; - return wrapped; - } // prettier-ignore - /** * Sign up with username, password and other attributes like phone, email * @param {String | object} params - The user attributes used for signin @@ -315,215 +47,7 @@ export class AuthClass { params: string | SignUpParams, ...restOfAttrs: string[] ): Promise { - if (!this.userPool) { - return this.rejectNoUserPool(); - } - - let username: string = null; - let password: string = null; - const attributes: CognitoUserAttribute[] = []; - let validationData: CognitoUserAttribute[] = null; - let clientMetadata; - let autoSignIn: AutoSignInOptions = { enabled: false }; - let autoSignInValidationData = {}; - let autoSignInClientMetaData: ClientMetaData = {}; - - if (params && typeof params === 'string') { - username = params; - password = restOfAttrs ? restOfAttrs[0] : null; - const email: string = restOfAttrs ? restOfAttrs[1] : null; - const phone_number: string = restOfAttrs ? restOfAttrs[2] : null; - - if (email) - attributes.push( - new CognitoUserAttribute({ Name: 'email', Value: email }) - ); - - if (phone_number) - attributes.push( - new CognitoUserAttribute({ - Name: 'phone_number', - Value: phone_number, - }) - ); - } else if (params && typeof params === 'object') { - username = params['username']; - password = params['password']; - - if (params && params.clientMetadata) { - clientMetadata = params.clientMetadata; - } else if (this._config.clientMetadata) { - clientMetadata = this._config.clientMetadata; - } - - const attrs = params['attributes']; - if (attrs) { - Object.keys(attrs).map(key => { - attributes.push( - new CognitoUserAttribute({ Name: key, Value: attrs[key] }) - ); - }); - } - - const validationDataObject = params['validationData']; - if (validationDataObject) { - validationData = []; - Object.keys(validationDataObject).map(key => { - validationData.push( - new CognitoUserAttribute({ - Name: key, - Value: validationDataObject[key], - }) - ); - }); - } - - autoSignIn = params.autoSignIn ?? { enabled: false }; - if (autoSignIn.enabled) { - this._storage.setItem('amplify-auto-sign-in', 'true'); - autoSignInValidationData = autoSignIn.validationData ?? {}; - autoSignInClientMetaData = autoSignIn.clientMetaData ?? {}; - } - } else { - return this.rejectAuthError(AuthErrorTypes.SignUpError); - } - - if (!username) { - return this.rejectAuthError(AuthErrorTypes.EmptyUsername); - } - if (!password) { - return this.rejectAuthError(AuthErrorTypes.EmptyPassword); - } - - logger.debug('signUp attrs:', attributes); - logger.debug('signUp validation data:', validationData); - - return new Promise((resolve, reject) => { - this.userPool.signUp( - username, - password, - attributes, - validationData, - (err, data) => { - if (err) { - dispatchAuthEvent( - 'signUp_failure', - err, - `${username} failed to signup` - ); - reject(err); - } else { - dispatchAuthEvent( - 'signUp', - data, - `${username} has signed up successfully` - ); - if (autoSignIn.enabled) { - this.handleAutoSignIn( - username, - password, - autoSignInValidationData, - autoSignInClientMetaData, - data - ); - } - resolve(data); - } - }, - clientMetadata - ); - }); - } - - private handleAutoSignIn( - username: string, - password: string, - validationData: {}, - clientMetadata: any, - data: any - ) { - this.autoSignInInitiated = true; - const authDetails = new AuthenticationDetails({ - Username: username, - Password: password, - ValidationData: validationData, - ClientMetadata: clientMetadata, - }); - if (data.userConfirmed) { - this.signInAfterUserConfirmed(authDetails); - } else if (this._config.signUpVerificationMethod === 'link') { - this.handleLinkAutoSignIn(authDetails); - } else { - this.handleCodeAutoSignIn(authDetails); - } - } - - private handleCodeAutoSignIn(authDetails: AuthenticationDetails) { - const listenEvent = ({ payload }) => { - if (payload.event === 'confirmSignUp') { - this.signInAfterUserConfirmed(authDetails, listenEvent); - } - }; - Hub.listen('auth', listenEvent); - } - - private handleLinkAutoSignIn(authDetails: AuthenticationDetails) { - this._storage.setItem('amplify-polling-started', 'true'); - const start = Date.now(); - const autoSignInPollingIntervalId = setInterval(() => { - if (Date.now() - start > MAX_AUTOSIGNIN_POLLING_MS) { - clearInterval(autoSignInPollingIntervalId); - dispatchAuthEvent( - 'autoSignIn_failure', - null, - 'Please confirm your account and use your credentials to sign in.' - ); - this._storage.removeItem('amplify-auto-sign-in'); - } else { - this.signInAfterUserConfirmed( - authDetails, - null, - autoSignInPollingIntervalId - ); - } - }, 5000); - } - - private async signInAfterUserConfirmed( - authDetails: AuthenticationDetails, - listenEvent?: HubCallback, - autoSignInPollingIntervalId?: ReturnType - ) { - const user = this.createCognitoUser(authDetails.getUsername()); - try { - await user.authenticateUser( - authDetails, - this.authCallbacks( - user, - value => { - dispatchAuthEvent( - 'autoSignIn', - value, - `${authDetails.getUsername()} has signed in successfully` - ); - if (listenEvent) { - Hub.remove('auth', listenEvent); - } - if (autoSignInPollingIntervalId) { - clearInterval(autoSignInPollingIntervalId); - this._storage.removeItem('amplify-polling-started'); - } - this._storage.removeItem('amplify-auto-sign-in'); - }, - error => { - logger.error(error); - this._storage.removeItem('amplify-auto-sign-in'); - } - ) - ); - } catch (error) { - logger.error(error); - } + return super.signUp(params, restOfAttrs); } /** @@ -538,61 +62,7 @@ export class AuthClass { code: string, options?: ConfirmSignUpOptions ): Promise { - if (!this.userPool) { - return this.rejectNoUserPool(); - } - if (!username) { - return this.rejectAuthError(AuthErrorTypes.EmptyUsername); - } - if (!code) { - return this.rejectAuthError(AuthErrorTypes.EmptyCode); - } - - const user = this.createCognitoUser(username); - const forceAliasCreation = - options && typeof options.forceAliasCreation === 'boolean' - ? options.forceAliasCreation - : true; - - let clientMetadata; - if (options && options.clientMetadata) { - clientMetadata = options.clientMetadata; - } else if (this._config.clientMetadata) { - clientMetadata = this._config.clientMetadata; - } - return new Promise((resolve, reject) => { - user.confirmRegistration( - code, - forceAliasCreation, - (err, data) => { - if (err) { - reject(err); - } else { - dispatchAuthEvent( - 'confirmSignUp', - data, - `${username} has been confirmed successfully` - ); - const autoSignIn = this.isTrueStorageValue('amplify-auto-sign-in'); - if (autoSignIn && !this.autoSignInInitiated) { - dispatchAuthEvent( - 'autoSignIn_failure', - null, - AuthErrorTypes.AutoSignInError - ); - this._storage.removeItem('amplify-auto-sign-in'); - } - resolve(data); - } - }, - clientMetadata - ); - }); - } - - private isTrueStorageValue(value: string) { - const item = this._storage.getItem(value); - return item ? item === 'true' : false; + return super.confirmSignUp(username, code, options); } /** @@ -603,25 +73,9 @@ export class AuthClass { */ public resendSignUp( username: string, - clientMetadata: ClientMetaData = this._config.clientMetadata + clientMetadata?: ClientMetaData ): Promise { - if (!this.userPool) { - return this.rejectNoUserPool(); - } - if (!username) { - return this.rejectAuthError(AuthErrorTypes.EmptyUsername); - } - - const user = this.createCognitoUser(username); - return new Promise((resolve, reject) => { - user.resendConfirmationCode((err, data) => { - if (err) { - reject(err); - } else { - resolve(data); - } - }, clientMetadata); - }); + return super.resendSignUp(username, clientMetadata); } /** @@ -634,191 +88,9 @@ export class AuthClass { public signIn( usernameOrSignInOpts: string | SignInOpts, pw?: string, - clientMetadata: ClientMetaData = this._config.clientMetadata - ): Promise { - if (!this.userPool) { - return this.rejectNoUserPool(); - } - - let username = null; - let password = null; - let validationData = {}; - - // for backward compatibility - if (typeof usernameOrSignInOpts === 'string') { - username = usernameOrSignInOpts; - password = pw; - } else if (isUsernamePasswordOpts(usernameOrSignInOpts)) { - if (typeof pw !== 'undefined') { - logger.warn( - 'The password should be defined under the first parameter object!' - ); - } - username = usernameOrSignInOpts.username; - password = usernameOrSignInOpts.password; - validationData = usernameOrSignInOpts.validationData; - } else { - return this.rejectAuthError(AuthErrorTypes.InvalidUsername); - } - if (!username) { - return this.rejectAuthError(AuthErrorTypes.EmptyUsername); - } - const authDetails = new AuthenticationDetails({ - Username: username, - Password: password, - ValidationData: validationData, - ClientMetadata: clientMetadata, - }); - if (password) { - return this.signInWithPassword(authDetails); - } else { - return this.signInWithoutPassword(authDetails); - } - } - - /** - * Return an object with the authentication callbacks - * @param {CognitoUser} 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 - ): IAuthenticationCallback { - const that = this; - return { - onSuccess: async session => { - logger.debug(session); - delete user['challengeName']; - delete user['challengeParam']; - try { - await this.Credentials.clear(); - const cred = await this.Credentials.set(session, 'session'); - logger.debug('succeed to get cognito credentials', cred); - } catch (e) { - logger.debug('cannot get cognito credentials', e); - } finally { - try { - // In order to get user attributes and MFA methods - // We need to trigger currentUserPoolUser again - const currentUser = await this.currentUserPoolUser(); - that.user = currentUser; - dispatchAuthEvent( - 'signIn', - currentUser, - `A user ${user.getUsername()} has been signed in` - ); - resolve(currentUser); - } catch (e) { - logger.error('Failed to get the signed in user', e); - reject(e); - } - } - }, - onFailure: err => { - logger.debug('signIn failure', err); - dispatchAuthEvent( - 'signIn_failure', - err, - `${user.getUsername()} failed to signin` - ); - reject(err); - }, - customChallenge: challengeParam => { - logger.debug('signIn custom challenge answer required'); - user['challengeName'] = 'CUSTOM_CHALLENGE'; - user['challengeParam'] = challengeParam; - resolve(user); - }, - mfaRequired: (challengeName, challengeParam) => { - logger.debug('signIn MFA required'); - user['challengeName'] = challengeName; - user['challengeParam'] = challengeParam; - resolve(user); - }, - mfaSetup: (challengeName, challengeParam) => { - logger.debug('signIn mfa setup', challengeName); - user['challengeName'] = challengeName; - user['challengeParam'] = challengeParam; - resolve(user); - }, - newPasswordRequired: (userAttributes, requiredAttributes) => { - logger.debug('signIn new password'); - user['challengeName'] = 'NEW_PASSWORD_REQUIRED'; - user['challengeParam'] = { - userAttributes, - requiredAttributes, - }; - resolve(user); - }, - totpRequired: (challengeName, challengeParam) => { - logger.debug('signIn totpRequired'); - user['challengeName'] = challengeName; - user['challengeParam'] = challengeParam; - resolve(user); - }, - selectMFAType: (challengeName, challengeParam) => { - logger.debug('signIn selectMFAType', challengeName); - user['challengeName'] = challengeName; - user['challengeParam'] = challengeParam; - resolve(user); - }, - }; - } - - /** - * Sign in with a password - * @private - * @param {AuthenticationDetails} authDetails - the user sign in data - * @return - A promise resolves the CognitoUser object if success or mfa required - */ - private signInWithPassword( - authDetails: AuthenticationDetails - ): Promise { - if (this.pendingSignIn) { - throw new Error('Pending sign-in attempt already in progress'); - } - - const user = this.createCognitoUser(authDetails.getUsername()); - - this.pendingSignIn = new Promise((resolve, reject) => { - user.authenticateUser( - authDetails, - this.authCallbacks( - user, - value => { - this.pendingSignIn = null; - resolve(value); - }, - error => { - this.pendingSignIn = null; - reject(error); - } - ) - ); - }); - - return this.pendingSignIn; - } - - /** - * 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 - */ - private signInWithoutPassword( - authDetails: AuthenticationDetails + clientMetadata?: ClientMetaData ): Promise { - const user = this.createCognitoUser(authDetails.getUsername()); - user.setAuthenticationFlowType('CUSTOM_AUTH'); - - return new Promise((resolve, reject) => { - user.initiateAuth(authDetails, this.authCallbacks(user, resolve, reject)); - }); + return super.signIn(usernameOrSignInOpts, pw, clientMetadata); } /** @@ -830,18 +102,7 @@ export class AuthClass { * @return - A promise resolves the current preferred mfa option if success */ public getMFAOptions(user: CognitoUser | any): Promise { - return new Promise((res, rej) => { - user.getMFAOptions((err, mfaOptions) => { - if (err) { - logger.debug('get MFA Options failed', err); - rej(err); - return; - } - logger.debug('get MFA options success', mfaOptions); - res(mfaOptions); - return; - }); - }); + return super.getMFAOptions(user); } /** @@ -853,99 +114,7 @@ export class AuthClass { user: CognitoUser | any, params?: GetPreferredMFAOpts ): Promise { - 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( - async (err, data) => { - if (err) { - logger.debug('getting preferred mfa 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; - } - } - rej(err); - return; - } - - const mfaType = that._getMfaTypeFromUserData(data); - if (!mfaType) { - rej('invalid MFA Type'); - return; - } else { - res(mfaType); - return; - } - }, - { bypassCache, clientMetadata } - ); - }); - } - - private _getMfaTypeFromUserData(data) { - let ret = null; - const preferredMFA = data.PreferredMfaSetting; - // if the user has used Auth.setPreferredMFA() to setup the mfa type - // then the "PreferredMfaSetting" would exist in the response - if (preferredMFA) { - ret = preferredMFA; - } else { - // if mfaList exists but empty, then its noMFA - const mfaList = data.UserMFASettingList; - if (!mfaList) { - // if SMS was enabled by using Auth.enableSMS(), - // the response would contain MFAOptions - // as for now Cognito only supports for SMS, so we will say it is 'SMS_MFA' - // if it does not exist, then it should be NOMFA - const MFAOptions = data.MFAOptions; - if (MFAOptions) { - ret = 'SMS_MFA'; - } else { - ret = 'NOMFA'; - } - } else if (mfaList.length === 0) { - ret = 'NOMFA'; - } else { - logger.debug('invalid case for getPreferredMFA', data); - } - } - return ret; - } - - private _getUserData(user, params) { - 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; - } - } - rej(err); - return; - } else { - res(data); - } - }, params); - }); + return super.getPreferredMFA(user, params); } /** @@ -954,118 +123,11 @@ export class AuthClass { * @param {string} mfaMethod - preferred mfa method * @return - A promise resolve if success */ - public async setPreferredMFA( + public setPreferredMFA( user: CognitoUser | any, mfaMethod: 'TOTP' | 'SMS' | 'NOMFA' | 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA' ): Promise { - const clientMetadata = this._config.clientMetadata; // TODO: verify behavior if this is override during signIn - - const userData = await this._getUserData(user, { - bypassCache: true, - clientMetadata, - }); - let smsMfaSettings = null; - let totpMfaSettings = null; - - switch (mfaMethod) { - case 'TOTP': - case 'SOFTWARE_TOKEN_MFA': - totpMfaSettings = { - PreferredMfa: true, - Enabled: true, - }; - break; - case 'SMS': - case 'SMS_MFA': - smsMfaSettings = { - PreferredMfa: true, - Enabled: true, - }; - break; - case 'NOMFA': - const mfaList = userData['UserMFASettingList']; - const currentMFAType = await this._getMfaTypeFromUserData(userData); - if (currentMFAType === 'NOMFA') { - return Promise.resolve('No change for mfa type'); - } else if (currentMFAType === 'SMS_MFA') { - smsMfaSettings = { - PreferredMfa: false, - Enabled: false, - }; - } else if (currentMFAType === 'SOFTWARE_TOKEN_MFA') { - totpMfaSettings = { - PreferredMfa: false, - Enabled: false, - }; - } else { - return this.rejectAuthError(AuthErrorTypes.InvalidMFA); - } - // if there is a UserMFASettingList in the response - // we need to disable every mfa type in that list - if (mfaList && mfaList.length !== 0) { - // to disable SMS or TOTP if exists in that list - mfaList.forEach(mfaType => { - if (mfaType === 'SMS_MFA') { - smsMfaSettings = { - PreferredMfa: false, - Enabled: false, - }; - } else if (mfaType === 'SOFTWARE_TOKEN_MFA') { - totpMfaSettings = { - PreferredMfa: false, - Enabled: false, - }; - } - }); - } - break; - default: - logger.debug('no validmfa method provided'); - return this.rejectAuthError(AuthErrorTypes.NoMFA); - } - - const that = this; - return new Promise((res, rej) => { - user.setUserMfaPreference( - smsMfaSettings, - totpMfaSettings, - (err, result) => { - if (err) { - logger.debug('Set user mfa preference error', err); - return rej(err); - } - 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( - 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; - } - } - return rej(err); - } else { - return res(result); - } - }, - { - bypassCache: true, - clientMetadata, - } - ); - } - ); - }); + return super.setPreferredMFA(user, mfaMethod); } /** @@ -1075,18 +137,7 @@ export class AuthClass { * @return - A promise resolves is success */ public disableSMS(user: CognitoUser): Promise { - return new Promise((res, rej) => { - user.disableMFA((err, data) => { - if (err) { - logger.debug('disable mfa failed', err); - rej(err); - return; - } - logger.debug('disable mfa succeed', data); - res(data); - return; - }); - }); + return super.disableSMS(user); } /** @@ -1096,18 +147,7 @@ export class AuthClass { * @return - A promise resolves is success */ public enableSMS(user: CognitoUser): Promise { - return new Promise((res, rej) => { - user.enableMFA((err, data) => { - if (err) { - logger.debug('enable mfa failed', err); - rej(err); - return; - } - logger.debug('enable mfa succeed', data); - res(data); - return; - }); - }); + return super.enableSMS(user); } /** @@ -1116,20 +156,7 @@ export class AuthClass { * @return - A promise resolves with the secret code if success */ public setupTOTP(user: CognitoUser | any): Promise { - 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; - }, - }); - }); + return super.setupTOTP(user); } /** @@ -1142,40 +169,7 @@ export class AuthClass { user: CognitoUser | any, challengeAnswer: string ): Promise { - logger.debug('verification totp token', user, challengeAnswer); - - let signInUserSession; - if (user && typeof user.getSignInUserSession === 'function') { - signInUserSession = (user as CognitoUser).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) { - dispatchAuthEvent( - 'signIn', - user, - `A user ${user.getUsername()} has been signed in` - ); - } - dispatchAuthEvent( - 'verify', - user, - `A user ${user.getUsername()} has been verified` - ); - logger.debug('verifyTotpToken success', data); - res(data); - return; - }, - }); - }); + return super.verifyTotpToken(user, challengeAnswer); } /** @@ -1187,117 +181,23 @@ export class AuthClass { user: CognitoUser | any, code: string, mfaType?: 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA' | null, - clientMetadata: ClientMetaData = this._config.clientMetadata + clientMetadata?: ClientMetaData ): Promise { - if (!code) { - return this.rejectAuthError(AuthErrorTypes.EmptyCode); - } - - const that = this; - return new Promise((resolve, reject) => { - user.sendMFACode( - code, - { - onSuccess: async session => { - logger.debug(session); - try { - await this.Credentials.clear(); - const cred = await this.Credentials.set(session, 'session'); - logger.debug('succeed to get cognito credentials', cred); - } catch (e) { - logger.debug('cannot get cognito credentials', e); - } finally { - that.user = user; - try { - const currentUser = await this.currentUserPoolUser(); - user.attributes = currentUser.attributes; - } catch (e) { - logger.debug('cannot get updated Cognito User', e); - } - dispatchAuthEvent( - 'signIn', - user, - `A user ${user.getUsername()} has been signed in` - ); - resolve(user); - } - }, - onFailure: err => { - logger.debug('confirm signIn failure', err); - reject(err); - }, - }, - mfaType, - clientMetadata - ); - }); + return super.confirmSignIn(user, code, mfaType, clientMetadata); } public completeNewPassword( user: CognitoUser | any, password: string, requiredAttributes: any = {}, - clientMetadata: ClientMetaData = this._config.clientMetadata + clientMetadata?: ClientMetaData ): Promise { - if (!password) { - return this.rejectAuthError(AuthErrorTypes.EmptyPassword); - } - - const that = this; - return new Promise((resolve, reject) => { - user.completeNewPasswordChallenge( - password, - requiredAttributes, - { - onSuccess: async session => { - logger.debug(session); - try { - await this.Credentials.clear(); - const cred = await this.Credentials.set(session, 'session'); - logger.debug('succeed to get cognito credentials', cred); - } catch (e) { - logger.debug('cannot get cognito credentials', e); - } finally { - that.user = user; - dispatchAuthEvent( - 'signIn', - user, - `A user ${user.getUsername()} has been signed in` - ); - resolve(user); - } - }, - onFailure: err => { - logger.debug('completeNewPassword failure', err); - dispatchAuthEvent( - 'completeNewPassword_failure', - err, - `${this.user} failed to complete the new password flow` - ); - reject(err); - }, - mfaRequired: (challengeName, challengeParam) => { - logger.debug('signIn MFA required'); - user['challengeName'] = challengeName; - user['challengeParam'] = challengeParam; - resolve(user); - }, - mfaSetup: (challengeName, challengeParam) => { - logger.debug('signIn mfa setup', challengeName); - user['challengeName'] = challengeName; - user['challengeParam'] = challengeParam; - resolve(user); - }, - totpRequired: (challengeName, challengeParam) => { - logger.debug('signIn mfa setup', challengeName); - user['challengeName'] = challengeName; - user['challengeParam'] = challengeParam; - resolve(user); - }, - }, - clientMetadata - ); - }); + return super.completeNewPassword( + user, + password, + requiredAttributes, + clientMetadata + ); } /** @@ -1308,23 +208,13 @@ export class AuthClass { public sendCustomChallengeAnswer( user: CognitoUser | any, challengeResponses: string, - clientMetadata: ClientMetaData = this._config.clientMetadata + clientMetadata?: ClientMetaData ): Promise { - if (!this.userPool) { - return this.rejectNoUserPool(); - } - if (!challengeResponses) { - return this.rejectAuthError(AuthErrorTypes.EmptyChallengeResponse); - } - - const that = this; - return new Promise((resolve, reject) => { - user.sendCustomChallengeAnswer( - challengeResponses, - this.authCallbacks(user, resolve, reject), - clientMetadata - ); - }); + return super.sendCustomChallengeAnswer( + user, + challengeResponses, + clientMetadata + ); } /** @@ -1336,18 +226,7 @@ export class AuthClass { user: CognitoUser | any, attributeNames: string[] ) { - const that = this; - return new Promise((resolve, reject) => { - that.userSession(user).then(session => { - user.deleteAttributes(attributeNames, (err, result) => { - if (err) { - return reject(err); - } else { - return resolve(result); - } - }); - }); - }); + return super.deleteUserAttributes(user, attributeNames); } /** @@ -1355,81 +234,8 @@ export class AuthClass { * @return {Promise} **/ // TODO: Check return type void - public async deleteUser(): Promise { - try { - await this._storageSync; - } catch (e) { - logger.debug('Failed to sync cache info into memory', e); - throw new Error(e); - } - - const isSignedInHostedUI = - this._oAuthHandler && - this._storage.getItem('amplify-signin-with-hostedUI') === 'true'; - - return new Promise(async (res, rej) => { - if (this.userPool) { - const user = this.userPool.getCurrentUser(); - - if (!user) { - logger.debug('Failed to get user from user pool'); - return rej(new Error('No current user.')); - } else { - user.getSession(async (err, session) => { - if (err) { - logger.debug('Failed to get the user session', 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; - } - } - 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); - } else { - dispatchAuthEvent( - 'signOut', - this.user, - `A user has been signed out` - ); - res(result); - } - } - }); - } - }); - } - } else { - logger.debug('no Congito User pool'); - rej(new Error('Cognito User pool does not exist')); - } - }); + public deleteUser(): Promise { + return super.deleteUser(); } /** @@ -1440,68 +246,9 @@ export class AuthClass { public updateUserAttributes( user: CognitoUser | any, attributes: object, - clientMetadata: ClientMetaData = this._config.clientMetadata + clientMetadata?: ClientMetaData ): Promise { - const attributeList: ICognitoUserAttributeData[] = []; - const that = this; - return new Promise((resolve, reject) => { - that.userSession(user).then(session => { - for (const key in attributes) { - if (key !== 'sub' && key.indexOf('_verified') < 0) { - const attr: ICognitoUserAttributeData = { - Name: key, - Value: attributes[key], - }; - attributeList.push(attr); - } - } - user.updateAttributes( - attributeList, - (err, result, details) => { - if (err) { - dispatchAuthEvent( - 'updateUserAttributes_failure', - err, - 'Failed to update attributes' - ); - return reject(err); - } else { - const attrs = this.createUpdateAttributesResultList( - attributes as Record, - details?.CodeDeliveryDetailsList - ); - dispatchAuthEvent( - 'updateUserAttributes', - attrs, - 'Attributes successfully updated' - ); - return resolve(result); - } - }, - clientMetadata - ); - }); - }); - } - - private createUpdateAttributesResultList( - attributes: Record, - codeDeliveryDetailsList?: CodeDeliveryDetails[] - ): Record { - const attrs = {}; - Object.keys(attributes).forEach(key => { - attrs[key] = { - isUpdated: true, - }; - const codeDeliveryDetails = codeDeliveryDetailsList?.find( - value => value.AttributeName === key - ); - if (codeDeliveryDetails) { - attrs[key].isUpdated = false; - attrs[key].codeDeliveryDetails = codeDeliveryDetails; - } - }); - return attrs; + return super.updateUserAttributes(user, attributes, clientMetadata); } /** @@ -1512,137 +259,11 @@ export class AuthClass { public userAttributes( user: CognitoUser | any ): Promise { - return new Promise((resolve, reject) => { - this.userSession(user).then(session => { - user.getUserAttributes((err, attributes) => { - if (err) { - reject(err); - } else { - resolve(attributes); - } - }); - }); - }); + return super.userAttributes(user); } public verifiedContact(user: CognitoUser | any) { - const that = this; - return this.userAttributes(user).then(attributes => { - const attrs = that.attributesToObject(attributes); - const unverified = {}; - const verified = {}; - if (attrs['email']) { - if (attrs['email_verified']) { - verified['email'] = attrs['email']; - } else { - unverified['email'] = attrs['email']; - } - } - if (attrs['phone_number']) { - if (attrs['phone_number_verified']) { - verified['phone_number'] = attrs['phone_number']; - } else { - unverified['phone_number'] = attrs['phone_number']; - } - } - return { - verified, - unverified, - }; - }); - } - - private isErrorWithMessage(err: any): err is { message: string } { - return ( - typeof err === 'object' && - Object.prototype.hasOwnProperty.call(err, 'message') - ); - } - - // Session revoked by another app - private isTokenRevokedError( - err: any - ): err is { message: 'Access Token has been revoked' } { - return ( - this.isErrorWithMessage(err) && - err.message === 'Access Token has been revoked' - ); - } - - private isRefreshTokenRevokedError( - err: any - ): err is { message: 'Refresh Token has been revoked' } { - return ( - this.isErrorWithMessage(err) && - err.message === 'Refresh Token has been revoked' - ); - } - - private isUserDisabledError( - err: any - ): err is { message: 'User is disabled.' } { - return this.isErrorWithMessage(err) && err.message === 'User is disabled.'; - } - - private isUserDoesNotExistError( - err: any - ): err is { message: 'User does not exist.' } { - return ( - this.isErrorWithMessage(err) && err.message === 'User does not exist.' - ); - } - - private isRefreshTokenExpiredError( - err: any - ): err is { message: 'Refresh Token has expired' } { - return ( - this.isErrorWithMessage(err) && - err.message === 'Refresh Token has expired' - ); - } - - private isPasswordResetRequiredError( - err: any - ): err is { message: 'Password reset required for the user' } { - return ( - this.isErrorWithMessage(err) && - err.message === 'Password reset required for the user' - ); - } - - private isSignedInHostedUI() { - return ( - this._oAuthHandler && - this._storage.getItem('amplify-signin-with-hostedUI') === 'true' - ); - } - - private isSessionInvalid(err: any) { - return ( - this.isUserDisabledError(err) || - this.isUserDoesNotExistError(err) || - this.isTokenRevokedError(err) || - this.isRefreshTokenRevokedError(err) || - this.isRefreshTokenExpiredError(err) || - this.isPasswordResetRequiredError(err) - ); - } - - private async cleanUpInvalidSession(user: CognitoUser) { - user.signOut(); - this.user = null; - try { - await this.cleanCachedItems(); // clean aws credentials - } catch (e) { - logger.debug('failed to clear cached items'); - } - if (this.isSignedInHostedUI()) { - return new Promise((res, rej) => { - this.oAuthSignOutRedirect(res, rej); - }); - } else { - dispatchAuthEvent('signOut', this.user, `A user has been signed out`); - } + return super.verifiedContact(user); } /** @@ -1652,128 +273,7 @@ export class AuthClass { public currentUserPoolUser( params?: CurrentUserOpts ): Promise { - if (!this.userPool) { - return this.rejectNoUserPool(); - } - - return new Promise((res, rej) => { - this._storageSync - .then(async () => { - if (this.isOAuthInProgress()) { - logger.debug('OAuth signIn in progress, waiting for resolution...'); - - await new Promise(res => { - const timeoutId = setTimeout(() => { - logger.debug('OAuth signIn in progress timeout'); - - Hub.remove('auth', hostedUISignCallback); - - res(); - }, OAUTH_FLOW_MS_TIMEOUT); - - Hub.listen('auth', hostedUISignCallback); - - function hostedUISignCallback({ payload }) { - const { event } = payload; - - if ( - event === 'cognitoHostedUI' || - event === 'cognitoHostedUI_failure' - ) { - logger.debug(`OAuth signIn resolved: ${event}`); - clearTimeout(timeoutId); - - Hub.remove('auth', hostedUISignCallback); - - res(); - } - } - }); - } - - const user = this.userPool.getCurrentUser(); - - if (!user) { - logger.debug('Failed to get user from user pool'); - rej('No current user'); - return; - } - - // refresh the session if the session expired. - try { - const session = await this._userSession(user); - - // get user data from Cognito - const bypassCache = params ? params.bypassCache : false; - - if (bypassCache) { - await this.Credentials.clear(); - } - - const clientMetadata = this._config.clientMetadata; - - // validate the token's scope first before calling this function - const { scope = '' } = session.getAccessToken().decodePayload(); - if (scope.split(' ').includes(USER_ADMIN_SCOPE)) { - 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; - } - rej(err); - } else { - res(user); - } - return; - } - const preferredMFA = data.PreferredMfaSetting || 'NOMFA'; - const attributeList = []; - - for (let i = 0; i < data.UserAttributes.length; i++) { - const attribute = { - Name: data.UserAttributes[i].Name, - Value: data.UserAttributes[i].Value, - }; - const userAttribute = new CognitoUserAttribute(attribute); - attributeList.push(userAttribute); - } - - const attributes = this.attributesToObject(attributeList); - Object.assign(user, { attributes, preferredMFA }); - return res(user); - }, - { bypassCache, clientMetadata } - ); - } 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); - } - } catch (err) { - rej(err); - } - }) - .catch(e => { - logger.debug('Failed to sync cache info into memory', e); - return rej(e); - }); - }); - } - - private isOAuthInProgress(): boolean { - return this.oAuthFlowInProgress; + return super.currentUserPoolUser(params); } /** @@ -1781,54 +281,10 @@ export class AuthClass { * @param {CurrentUserOpts} - options for getting the current user * @return - A promise resolves to current authenticated CognitoUser if success */ - public async currentAuthenticatedUser( + public currentAuthenticatedUser( params?: CurrentUserOpts ): Promise { - logger.debug('getting current authenticated user'); - let federatedUser = null; - try { - await this._storageSync; - } catch (e) { - logger.debug('Failed to sync cache info into memory', e); - throw e; - } - - try { - const federatedInfo = JSON.parse( - this._storage.getItem('aws-amplify-federatedInfo') - ); - if (federatedInfo) { - federatedUser = { - ...federatedInfo.user, - token: federatedInfo.token, - }; - } - } catch (e) { - logger.debug('cannot load federated user from auth storage'); - } - - if (federatedUser) { - this.user = federatedUser; - logger.debug('get current authenticated federated user', this.user); - return this.user; - } else { - logger.debug('get current authenticated userpool user'); - let user = null; - try { - user = await this.currentUserPoolUser(params); - } catch (e) { - if (e === 'No userPool') { - logger.error( - 'Cannot get the current user because the user pool is missing. ' + - 'Please make sure the Auth module is configured with a valid Cognito User Pool ID' - ); - } - logger.debug('The user is not authenticated by the error', e); - return Promise.reject('The user is not authenticated'); - } - this.user = user; - return this.user; - } + return super.currentAuthenticatedUser(params); } /** @@ -1836,88 +292,7 @@ export class AuthClass { * @return - A promise resolves to session object if success */ public currentSession(): Promise { - const that = this; - logger.debug('Getting current session'); - // Purposely not calling the reject method here because we don't need a console error - if (!this.userPool) { - return Promise.reject(new Error('No User Pool in the configuration.')); - } - - return new Promise((res, rej) => { - that - .currentUserPoolUser() - .then(user => { - that - .userSession(user) - .then(session => { - res(session); - return; - }) - .catch(e => { - logger.debug('Failed to get the current session', e); - rej(e); - return; - }); - }) - .catch(e => { - logger.debug('Failed to get the current user', e); - rej(e); - return; - }); - }); - } - - private async _userSession(user?: CognitoUser): Promise { - if (!user) { - logger.debug('the user is null'); - return this.rejectAuthError(AuthErrorTypes.NoUserSession); - } - const clientMetadata = this._config.clientMetadata; - // Debouncing the concurrent userSession calls by caching the promise. - // This solution assumes users will always call this function with the same CognitoUser instance. - if (this.inflightSessionPromiseCounter === 0) { - this.inflightSessionPromise = new Promise( - (res, rej) => { - user.getSession( - async (err, session) => { - if (err) { - logger.debug('Failed to get the session from user', user); - 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; - } - } - rej(err); - return; - } else { - logger.debug('Succeed to get the user session', session); - res(session); - return; - } - }, - { clientMetadata } - ); - } - ); - } - this.inflightSessionPromiseCounter++; - - try { - const userSession = await this.inflightSessionPromise; - // Set private member. Avoid user.setSignInUserSession() to prevent excessive localstorage refresh. - // @ts-ignore - user.signInUserSession = userSession; - return userSession!; - } finally { - this.inflightSessionPromiseCounter--; - } + return super.currentSession(); } /** @@ -1926,52 +301,19 @@ export class AuthClass { * @return - A promise resolves to the session */ public userSession(user): Promise { - return this._userSession(user); + return super.userSession(user); } /** * Get authenticated credentials of current user. * @return - A promise resolves to be current user's credentials */ - public async currentUserCredentials(): Promise { - logger.debug('Getting current user credentials'); - - try { - await this._storageSync; - } catch (e) { - logger.debug('Failed to sync cache info into memory', e); - throw e; - } - - // first to check whether there is federation info in the auth storage - let federatedInfo = null; - try { - federatedInfo = JSON.parse( - this._storage.getItem('aws-amplify-federatedInfo') - ); - } catch (e) { - logger.debug('failed to get or parse item aws-amplify-federatedInfo', e); - } - - if (federatedInfo) { - // refresh the jwt token here if necessary - return this.Credentials.refreshFederatedToken(federatedInfo); - } else { - return this.currentSession() - .then(session => { - logger.debug('getting session success', session); - return this.Credentials.set(session, 'session'); - }) - .catch(() => { - logger.debug('getting guest credentials'); - return this.Credentials.set(null, 'guest'); - }); - } + public currentUserCredentials(): Promise { + return super.currentUserCredentials(); } public currentCredentials(): Promise { - logger.debug('getting current credentials'); - return this.Credentials.get(); + return super.currentCredentials(); } /** @@ -1983,22 +325,9 @@ export class AuthClass { public verifyUserAttribute( user: CognitoUser | any, attr: string, - clientMetadata: ClientMetaData = this._config.clientMetadata + clientMetadata?: ClientMetaData ): Promise { - return new Promise((resolve, reject) => { - user.getAttributeVerificationCode( - attr, - { - onSuccess(success) { - return resolve(success); - }, - onFailure(err) { - return reject(err); - }, - }, - clientMetadata - ); - }); + return super.verifyUserAttribute(user, attr, clientMetadata); } /** @@ -2013,29 +342,11 @@ export class AuthClass { attr: string, code: string ): Promise { - if (!code) { - return this.rejectAuthError(AuthErrorTypes.EmptyCode); - } - - return new Promise((resolve, reject) => { - user.verifyAttribute(attr, code, { - onSuccess(data) { - resolve(data); - return; - }, - onFailure(err) { - reject(err); - return; - }, - }); - }); + return super.verifyUserAttributeSubmit(user, attr, code); } public verifyCurrentUserAttribute(attr: string): Promise { - const that = this; - return that - .currentUserPoolUser() - .then(user => that.verifyUserAttribute(user, attr)); + return super.verifyCurrentUserAttribute(attr); } /** @@ -2048,105 +359,7 @@ export class AuthClass { attr: string, code: string ): Promise { - const that = this; - return that - .currentUserPoolUser() - .then(user => that.verifyUserAttributeSubmit(user, attr, code)); - } - - private async cognitoIdentitySignOut( - opts: SignOutOpts, - user: CognitoUser | any - ) { - try { - await this._storageSync; - } catch (e) { - logger.debug('Failed to sync cache info into memory', e); - throw e; - } - - const isSignedInHostedUI = - this._oAuthHandler && - this._storage.getItem('amplify-signin-with-hostedUI') === 'true'; - - return new Promise((res, rej) => { - if (opts && opts.global) { - logger.debug('user global sign out', user); - // 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( - async (err, result) => { - if (err) { - logger.debug('failed to get the user session', 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; - } - } - 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); - }, - }); - }, - { clientMetadata } - ); - } else { - logger.debug('user sign out', user); - user.signOut(() => { - if (isSignedInHostedUI) { - this.oAuthSignOutRedirect(res, rej); - } else { - return res(); - } - }); - } - }); - } - - private oAuthSignOutRedirect( - resolve: () => void, - reject: (reason?: any) => void - ) { - const { isBrowser } = browserOrNode(); - - if (isBrowser) { - this.oAuthSignOutRedirectOrReject(reject); - } else { - this.oAuthSignOutAndResolve(resolve); - } - } - - private oAuthSignOutAndResolve(resolve: () => void) { - this._oAuthHandler.signOut(); - resolve(); - } - - private oAuthSignOutRedirectOrReject(reject: (reason?: any) => void) { - this._oAuthHandler.signOut(); // this method redirects url - - // App should be redirected to another url otherwise it will reject - setTimeout(() => reject(Error('Signout timeout fail')), 3000); + return super.verifyCurrentUserAttributeSubmit(attr, code); } /** @@ -2154,37 +367,8 @@ export class AuthClass { * @ * @return - A promise resolved if success */ - public async signOut(opts?: SignOutOpts): Promise { - try { - await this.cleanCachedItems(); - } catch (e) { - logger.debug('failed to clear cached items'); - } - - if (this.userPool) { - const user = this.userPool.getCurrentUser(); - if (user) { - await this.cognitoIdentitySignOut(opts, user); - } else { - logger.debug('no current Cognito user'); - } - } else { - logger.debug('no Cognito User pool'); - } - - /** - * Note for future refactor - no reliable way to get username with - * Cognito User Pools vs Identity when federating with Social Providers - * This is why we need a well structured session object that can be inspected - * and information passed back in the message below for Hub dispatch - */ - dispatchAuthEvent('signOut', this.user, `A user has been signed out`); - this.user = null; - } - - private async cleanCachedItems() { - // clear cognito cached item - await this.Credentials.clear(); + public signOut(opts?: SignOutOpts): Promise { + return super.signOut(opts); } /** @@ -2198,25 +382,9 @@ export class AuthClass { user: CognitoUser | any, oldPassword: string, newPassword: string, - clientMetadata: ClientMetaData = this._config.clientMetadata + clientMetadata?: ClientMetaData ): Promise<'SUCCESS'> { - return new Promise((resolve, reject) => { - this.userSession(user).then(session => { - user.changePassword( - oldPassword, - newPassword, - (err, data) => { - if (err) { - logger.debug('change password failure', err); - return reject(err); - } else { - return resolve(data); - } - }, - clientMetadata - ); - }); - }); + return super.changePassword(user, oldPassword, newPassword, clientMetadata); } /** @@ -2226,46 +394,9 @@ export class AuthClass { */ public forgotPassword( username: string, - clientMetadata: ClientMetaData = this._config.clientMetadata + clientMetadata?: ClientMetaData ): Promise { - if (!this.userPool) { - return this.rejectNoUserPool(); - } - if (!username) { - return this.rejectAuthError(AuthErrorTypes.EmptyUsername); - } - - const user = this.createCognitoUser(username); - return new Promise((resolve, reject) => { - user.forgotPassword( - { - onSuccess: () => { - resolve(); - return; - }, - onFailure: err => { - logger.debug('forgot password failure', err); - dispatchAuthEvent( - 'forgotPassword_failure', - err, - `${username} forgotPassword failed` - ); - reject(err); - return; - }, - inputVerificationCode: data => { - dispatchAuthEvent( - 'forgotPassword', - user, - `${username} has initiated forgot password flow` - ); - resolve(data); - return; - }, - }, - clientMetadata - ); - }); + return super.forgotPassword(username, clientMetadata); } /** @@ -2279,49 +410,9 @@ export class AuthClass { username: string, code: string, password: string, - clientMetadata: ClientMetaData = this._config.clientMetadata + clientMetadata?: ClientMetaData ): Promise { - if (!this.userPool) { - return this.rejectNoUserPool(); - } - if (!username) { - return this.rejectAuthError(AuthErrorTypes.EmptyUsername); - } - if (!code) { - return this.rejectAuthError(AuthErrorTypes.EmptyCode); - } - if (!password) { - return this.rejectAuthError(AuthErrorTypes.EmptyPassword); - } - - const user = this.createCognitoUser(username); - return new Promise((resolve, reject) => { - user.confirmPassword( - code, - password, - { - onSuccess: success => { - dispatchAuthEvent( - 'forgotPasswordSubmit', - user, - `${username} forgotPasswordSubmit successful` - ); - resolve(success); - return; - }, - onFailure: err => { - dispatchAuthEvent( - 'forgotPasswordSubmit_failure', - err, - `${username} forgotPasswordSubmit failed` - ); - reject(err); - return; - }, - }, - clientMetadata - ); - }); + return super.forgotPasswordSubmit(username, code, password, clientMetadata); } /** @@ -2329,60 +420,22 @@ export class AuthClass { * @async * @return {Object }- current User's information */ - public async currentUserInfo() { - const source = this.Credentials.getCredSource(); - - if (!source || source === 'aws' || source === 'userPool') { - const user = await this.currentUserPoolUser().catch(err => - logger.error(err) - ); - if (!user) { - return null; - } - - try { - const attributes = await this.userAttributes(user); - const userAttrs: object = this.attributesToObject(attributes); - let credentials = null; - try { - credentials = await this.currentCredentials(); - } catch (e) { - logger.debug( - 'Failed to retrieve credentials while getting current user info', - e - ); - } - - const info = { - id: credentials ? credentials.identityId : undefined, - username: user.getUsername(), - attributes: userAttrs, - }; - return info; - } catch (err) { - logger.error('currentUserInfo error', err); - return {}; - } - } - - if (source === 'federated') { - const user = this.user; - return user ? user : {}; - } + public currentUserInfo() { + return super.currentUserInfo(); } - public async federatedSignIn( + public federatedSignIn( options?: FederatedSignInOptions ): Promise; - public async federatedSignIn( + public federatedSignIn( provider: LegacyProvider, response: FederatedResponse, user: FederatedUser ): Promise; - public async federatedSignIn( + public federatedSignIn( options?: FederatedSignInOptionsCustom ): Promise; - public async federatedSignIn( + public federatedSignIn( providerOrOptions: | LegacyProvider | FederatedSignInOptions @@ -2390,230 +443,7 @@ export class AuthClass { response?: FederatedResponse, user?: FederatedUser ): Promise { - if (!this._config.identityPoolId && !this._config.userPoolId) { - throw new Error( - `Federation requires either a User Pool or Identity Pool in config` - ); - } - - // Ensure backwards compatability - if (typeof providerOrOptions === 'undefined') { - if (this._config.identityPoolId && !this._config.userPoolId) { - throw new Error( - `Federation with Identity Pools requires tokens passed as arguments` - ); - } - } - - if ( - isFederatedSignInOptions(providerOrOptions) || - isFederatedSignInOptionsCustom(providerOrOptions) || - hasCustomState(providerOrOptions) || - typeof providerOrOptions === 'undefined' - ) { - const options = providerOrOptions || { - provider: CognitoHostedUIIdentityProvider.Cognito, - }; - const provider = isFederatedSignInOptions(options) - ? options.provider - : (options as FederatedSignInOptionsCustom).customProvider; - - const customState = isFederatedSignInOptions(options) - ? options.customState - : (options as FederatedSignInOptionsCustom).customState; - - if (this._config.userPoolId) { - const client_id = isCognitoHostedOpts(this._config.oauth) - ? this._config.userPoolWebClientId - : this._config.oauth.clientID; - /*Note: Invenstigate automatically adding trailing slash */ - const redirect_uri = isCognitoHostedOpts(this._config.oauth) - ? this._config.oauth.redirectSignIn - : this._config.oauth.redirectUri; - - this._oAuthHandler.oauthSignIn( - this._config.oauth.responseType, - this._config.oauth.domain, - redirect_uri, - client_id, - provider, - customState - ); - } - } else { - const provider = providerOrOptions; - // To check if the user is already logged in - try { - const loggedInUser = JSON.stringify( - JSON.parse(this._storage.getItem('aws-amplify-federatedInfo')).user - ); - if (loggedInUser) { - logger.warn(`There is already a signed in user: ${loggedInUser} in your app. - You should not call Auth.federatedSignIn method again as it may cause unexpected behavior.`); - } - } catch (e) {} - - const { token, identity_id, expires_at } = response; - // Because this.Credentials.set would update the user info with identity id - // So we need to retrieve the user again. - const credentials = await this.Credentials.set( - { provider, token, identity_id, user, expires_at }, - 'federation' - ); - const currentUser = await this.currentAuthenticatedUser(); - dispatchAuthEvent( - 'signIn', - currentUser, - `A user ${currentUser.username} has been signed in` - ); - logger.debug('federated sign in credentials', credentials); - return credentials; - } - } - - /** - * Used to complete the OAuth flow with or without the Cognito Hosted UI - * @param {String} URL - optional parameter for customers to pass in the response URL - */ - private async _handleAuthResponse(URL?: string) { - if (this.oAuthFlowInProgress) { - logger.debug(`Skipping URL ${URL} current flow in progress`); - return; - } - - try { - this.oAuthFlowInProgress = true; - if (!this._config.userPoolId) { - throw new Error( - `OAuth responses require a User Pool defined in config` - ); - } - - dispatchAuthEvent( - 'parsingCallbackUrl', - { url: URL }, - `The callback url is being parsed` - ); - - const currentUrl = - URL || (browserOrNode().isBrowser ? window.location.href : ''); - - const hasCodeOrError = !!(parse(currentUrl).query || '') - .split('&') - .map(entry => entry.split('=')) - .find(([k]) => k === 'code' || k === 'error'); - - const hasTokenOrError = !!(parse(currentUrl).hash || '#') - .substr(1) - .split('&') - .map(entry => entry.split('=')) - .find(([k]) => k === 'access_token' || k === 'error'); - - if (hasCodeOrError || hasTokenOrError) { - this._storage.setItem('amplify-redirected-from-hosted-ui', 'true'); - try { - const { accessToken, idToken, refreshToken, state } = - await this._oAuthHandler.handleAuthResponse(currentUrl); - const session = new CognitoUserSession({ - IdToken: new CognitoIdToken({ IdToken: idToken }), - RefreshToken: new CognitoRefreshToken({ - RefreshToken: refreshToken, - }), - AccessToken: new CognitoAccessToken({ - AccessToken: accessToken, - }), - }); - - let credentials; - // Get AWS Credentials & store if Identity Pool is defined - if (this._config.identityPoolId) { - credentials = await this.Credentials.set(session, 'session'); - logger.debug('AWS credentials', credentials); - } - - /* - Prior to the request we do sign the custom state along with the state we set. This check will verify - if there is a dash indicated when setting custom state from the request. If a dash is contained - then there is custom state present on the state string. - */ - const isCustomStateIncluded = /-/.test(state); - - /* - The following is to create a user for the Cognito Identity SDK to store the tokens - When we remove this SDK later that logic will have to be centralized in our new version - */ - //#region - const currentUser = this.createCognitoUser( - session.getIdToken().decodePayload()['cognito:username'] - ); - - // This calls cacheTokens() in Cognito SDK - currentUser.setSignInUserSession(session); - - if (window && typeof window.history !== 'undefined') { - window.history.replaceState( - {}, - null, - (this._config.oauth as AwsCognitoOAuthOpts).redirectSignIn - ); - } - - dispatchAuthEvent( - 'signIn', - currentUser, - `A user ${currentUser.getUsername()} has been signed in` - ); - dispatchAuthEvent( - 'cognitoHostedUI', - currentUser, - `A user ${currentUser.getUsername()} has been signed in via Cognito Hosted UI` - ); - - if (isCustomStateIncluded) { - const customState = state.split('-').splice(1).join('-'); - - dispatchAuthEvent( - 'customOAuthState', - urlSafeDecode(customState), - `State for user ${currentUser.getUsername()}` - ); - } - //#endregion - - return credentials; - } catch (err) { - logger.debug('Error in cognito hosted auth response', err); - - // Just like a successful handling of `?code`, replace the window history to "dispose" of the `code`. - // Otherwise, reloading the page will throw errors as the `code` has already been spent. - if (window && typeof window.history !== 'undefined') { - window.history.replaceState( - {}, - null, - (this._config.oauth as AwsCognitoOAuthOpts).redirectSignIn - ); - } - - dispatchAuthEvent( - 'signIn_failure', - err, - `The OAuth response flow failed` - ); - dispatchAuthEvent( - 'cognitoHostedUI_failure', - err, - `A failure occurred when returning to the Cognito Hosted UI` - ); - dispatchAuthEvent( - 'customState_failure', - err, - `A failure occurred when returning state` - ); - } - } - } finally { - this.oAuthFlowInProgress = false; - } + return super.federatedSignIn(providerOrOptions, response, user); } /** @@ -2622,185 +452,21 @@ export class AuthClass { * @return {Object} - Credentials */ public essentialCredentials(credentials): ICredentials { - return { - accessKeyId: credentials.accessKeyId, - sessionToken: credentials.sessionToken, - secretAccessKey: credentials.secretAccessKey, - identityId: credentials.identityId, - authenticated: credentials.authenticated, - }; - } - - private attributesToObject(attributes) { - const obj = {}; - if (attributes) { - attributes.map(attribute => { - if ( - attribute.Name === 'email_verified' || - attribute.Name === 'phone_number_verified' - ) { - obj[attribute.Name] = - this.isTruthyString(attribute.Value) || attribute.Value === true; - } else { - obj[attribute.Name] = attribute.Value; - } - }); - } - return obj; - } - - private isTruthyString(value: any): boolean { - return ( - typeof value.toLowerCase === 'function' && value.toLowerCase() === 'true' - ); - } - - private createCognitoUser(username: string): CognitoUser { - const userData: ICognitoUserData = { - Username: username, - Pool: this.userPool, - }; - userData.Storage = this._storage; - - const { authenticationFlowType } = this._config; - - const user = new CognitoUser(userData); - if (authenticationFlowType) { - user.setAuthenticationFlowType(authenticationFlowType); - } - return user; - } - - private _isValidAuthStorage(obj) { - // We need to check if the obj has the functions of Storage - return ( - !!obj && - typeof obj.getItem === 'function' && - typeof obj.setItem === 'function' && - typeof obj.removeItem === 'function' && - typeof obj.clear === 'function' - ); - } - - private noUserPoolErrorHandler(config: AuthOptions): AuthErrorTypes { - if (config) { - if (!config.userPoolId || !config.identityPoolId) { - return AuthErrorTypes.MissingAuthConfig; - } - } - return AuthErrorTypes.NoConfig; + return super.essentialCredentials(credentials); } - private rejectAuthError(type: AuthErrorTypes): Promise { - return Promise.reject(new AuthError(type)); + public rememberDevice(): Promise { + return super.rememberDevice(); } - private rejectNoUserPool(): Promise { - const type = this.noUserPoolErrorHandler(this._config); - return Promise.reject(new NoUserPoolError(type)); + public forgetDevice(): Promise { + return super.forgetDevice(); } - public async rememberDevice(): Promise { - let currUser; - - try { - currUser = await this.currentUserPoolUser(); - } catch (error) { - logger.debug('The user is not authenticated by the error', error); - return Promise.reject('The user is not authenticated'); - } - - currUser.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); - } - }, - }); - }); - } - - public async forgetDevice(): Promise { - let currUser; - - try { - currUser = await this.currentUserPoolUser(); - } catch (error) { - logger.debug('The user is not authenticated by the error', error); - return Promise.reject('The user is not authenticated'); - } - - currUser.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); - } - }, - }); - }); - } - - public async fetchDevices(): Promise { - let currUser; - - try { - currUser = await this.currentUserPoolUser(); - } catch (error) { - logger.debug('The user is not authenticated by the error', error); - throw new Error('The user is not authenticated'); - } - - currUser.getCachedDeviceKeyAndPassword(); - return new Promise((res, rej) => { - const cb = { - onSuccess(data) { - const deviceList: IAuthDevice[] = data.Devices.map(device => { - const deviceName = - device.DeviceAttributes.find( - ({ Name }) => Name === 'device_name' - ) || {}; - - const deviceInfo: IAuthDevice = { - id: device.DeviceKey, - name: deviceName.Value, - }; - return deviceInfo; - }); - res(deviceList); - }, - onFailure: err => { - if (err.code === 'InvalidParameterException') { - rej(new AuthError(AuthErrorTypes.DeviceConfig)); - } else if (err.code === 'NetworkError') { - rej(new AuthError(AuthErrorTypes.NetworkError)); - } else { - rej(err); - } - }, - }; - currUser.listDevices(MAX_DEVICES, null, cb); - }); + public fetchDevices(): Promise { + return super.fetchDevices(); } } export const Auth = new AuthClass(null); - Amplify.register(Auth); diff --git a/packages/auth/src/internals/InternalAuth.ts b/packages/auth/src/internals/InternalAuth.ts new file mode 100644 index 00000000000..b25bfc6a90e --- /dev/null +++ b/packages/auth/src/internals/InternalAuth.ts @@ -0,0 +1,2901 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { + AuthOptions, + FederatedResponse, + SignUpParams, + FederatedUser, + ConfirmSignUpOptions, + SignOutOpts, + CurrentUserOpts, + GetPreferredMFAOpts, + SignInOpts, + isUsernamePasswordOpts, + isCognitoHostedOpts, + isFederatedSignInOptions, + isFederatedSignInOptionsCustom, + hasCustomState, + FederatedSignInOptionsCustom, + LegacyProvider, + FederatedSignInOptions, + AwsCognitoOAuthOpts, + ClientMetaData, +} from '../types'; + +import { + Amplify, + ConsoleLogger as Logger, + Credentials, + CustomUserAgentDetails, + Hub, + StorageHelper, + ICredentials, + Platform, + browserOrNode, + parseAWSExports, + UniversalStorage, + urlSafeDecode, + HubCallback, +} from '@aws-amplify/core'; +import { + CookieStorage, + CognitoUserPool, + AuthenticationDetails, + ICognitoUserPoolData, + ICognitoUserData, + ISignUpResult, + CognitoUser, + MFAOption, + CognitoUserSession, + IAuthenticationCallback, + ICognitoUserAttributeData, + CognitoUserAttribute, + CognitoIdToken, + CognitoRefreshToken, + CognitoAccessToken, + NodeCallback, + CodeDeliveryDetails, +} from 'amazon-cognito-identity-js'; +import { + addAuthCategoryToCognitoUserAgent, + addFrameworkToCognitoUserAgent, +} from 'amazon-cognito-identity-js/internals'; + +import { parse } from 'url'; +import OAuth from '../OAuth/OAuth'; +import { default as urlListener } from '../urlListener'; +import { AuthError, NoUserPoolError } from '../Errors'; +import { + AuthErrorTypes, + AutoSignInOptions, + CognitoHostedUIIdentityProvider, + IAuthDevice, +} from '../types/Auth'; + +const logger = new Logger('AuthClass'); +const USER_ADMIN_SCOPE = 'aws.cognito.signin.user.admin'; + +// 10 sec, following this guide https://www.nngroup.com/articles/response-times-3-important-limits/ +const OAUTH_FLOW_MS_TIMEOUT = 10 * 1000; + +const AMPLIFY_SYMBOL = ( + typeof Symbol !== 'undefined' && typeof Symbol.for === 'function' + ? Symbol.for('amplify_default') + : '@@amplify_default' +) as Symbol; + +const dispatchAuthEvent = (event: string, data: any, message: string) => { + Hub.dispatch('auth', { event, data, message }, 'Auth', AMPLIFY_SYMBOL); +}; + +// Cognito Documentation for max device +// tslint:disable-next-line:max-line-length +// https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ListDevices.html#API_ListDevices_RequestSyntax +const MAX_DEVICES = 60; + +const MAX_AUTOSIGNIN_POLLING_MS = 3 * 60 * 1000; + +/** + * Provide authentication steps + */ +export class InternalAuthClass { + private _config: AuthOptions; + private userPool: CognitoUserPool = null; + private user: any = null; + private _oAuthHandler: OAuth; + private _storage; + private _storageSync; + private oAuthFlowInProgress: boolean = false; + private pendingSignIn: ReturnType< + InternalAuthClass['signInWithPassword'] + > | null; + private autoSignInInitiated: boolean = false; + private inflightSessionPromise: Promise | null = null; + private inflightSessionPromiseCounter: number = 0; + Credentials = Credentials; + + /** + * Initialize Auth with AWS configurations + * @param {Object} config - Configuration of the Auth + */ + constructor(config: AuthOptions) { + this.configure(config); + this.currentCredentials = this.currentCredentials.bind(this); + this.currentUserCredentials = this.currentUserCredentials.bind(this); + + Hub.listen('auth', ({ payload }) => { + const { event } = payload; + switch (event) { + case 'verify': + case 'signIn': + this._storage.setItem('amplify-signin-with-hostedUI', 'false'); + break; + case 'signOut': + this._storage.removeItem('amplify-signin-with-hostedUI'); + break; + case 'cognitoHostedUI': + this._storage.setItem('amplify-signin-with-hostedUI', 'true'); + break; + } + }); + + addAuthCategoryToCognitoUserAgent(); + addFrameworkToCognitoUserAgent(Platform.framework); + Platform.observeFrameworkChanges(() => { + addFrameworkToCognitoUserAgent(Platform.framework); + }); + } + + public getModuleName() { + return 'InternalAuth'; + } + + configure(config?) { + if (!config) return this._config || {}; + logger.debug('configure Auth'); + const conf = Object.assign( + {}, + this._config, + parseAWSExports(config).Auth, + config + ); + this._config = conf; + const { + userPoolId, + userPoolWebClientId, + cookieStorage, + oauth, + region, + identityPoolId, + mandatorySignIn, + refreshHandlers, + identityPoolRegion, + clientMetadata, + endpoint, + storage, + } = this._config; + + if (!storage) { + // backward compatability + if (cookieStorage) this._storage = new CookieStorage(cookieStorage); + else { + this._storage = config.ssr + ? new UniversalStorage() + : new StorageHelper().getStorage(); + } + } else { + if (!this._isValidAuthStorage(storage)) { + logger.error('The storage in the Auth config is not valid!'); + throw new Error('Empty storage object'); + } + this._storage = storage; + } + + this._storageSync = Promise.resolve(); + if (typeof this._storage['sync'] === 'function') { + this._storageSync = this._storage['sync'](); + } + + if (userPoolId) { + const userPoolData: ICognitoUserPoolData = { + UserPoolId: userPoolId, + ClientId: userPoolWebClientId, + endpoint, + }; + userPoolData.Storage = this._storage; + + this.userPool = new CognitoUserPool( + userPoolData, + this.wrapRefreshSessionCallback + ); + } + + this.Credentials.configure({ + mandatorySignIn, + region, + userPoolId, + identityPoolId, + refreshHandlers, + storage: this._storage, + identityPoolRegion, + }); + + // initialize cognitoauth client if hosted ui options provided + // to keep backward compatibility: + const cognitoHostedUIConfig = oauth + ? isCognitoHostedOpts(this._config.oauth) + ? oauth + : (oauth).awsCognito + : undefined; + + if (cognitoHostedUIConfig) { + const cognitoAuthParams = Object.assign( + { + cognitoClientId: userPoolWebClientId, + UserPoolId: userPoolId, + domain: cognitoHostedUIConfig['domain'], + scopes: cognitoHostedUIConfig['scope'], + redirectSignIn: cognitoHostedUIConfig['redirectSignIn'], + redirectSignOut: cognitoHostedUIConfig['redirectSignOut'], + responseType: cognitoHostedUIConfig['responseType'], + Storage: this._storage, + urlOpener: cognitoHostedUIConfig['urlOpener'], + clientMetadata, + }, + cognitoHostedUIConfig['options'] + ); + + this._oAuthHandler = new OAuth({ + scopes: cognitoAuthParams.scopes, + config: cognitoAuthParams, + cognitoClientId: cognitoAuthParams.cognitoClientId, + }); + + // **NOTE** - Remove this in a future major release as it is a breaking change + // Prevents _handleAuthResponse from being called multiple times in Expo + // See https://github.com/aws-amplify/amplify-js/issues/4388 + const usedResponseUrls = {}; + urlListener(({ url }) => { + if (usedResponseUrls[url]) { + return; + } + + usedResponseUrls[url] = true; + this._handleAuthResponse(url); + }); + } + + dispatchAuthEvent( + 'configured', + null, + `The Auth category has been configured successfully` + ); + + if ( + !this.autoSignInInitiated && + typeof this._storage['getItem'] === 'function' + ) { + const pollingInitiated = this.isTrueStorageValue( + 'amplify-polling-started' + ); + if (pollingInitiated) { + dispatchAuthEvent( + 'autoSignIn_failure', + null, + AuthErrorTypes.AutoSignInError + ); + this._storage.removeItem('amplify-auto-sign-in'); + } + this._storage.removeItem('amplify-polling-started'); + } + return this._config; + } + + wrapRefreshSessionCallback = (callback: NodeCallback.Any) => { + const wrapped: NodeCallback.Any = (error, data) => { + if (data) { + dispatchAuthEvent('tokenRefresh', undefined, `New token retrieved`); + } else { + dispatchAuthEvent( + 'tokenRefresh_failure', + error, + `Failed to retrieve new token` + ); + } + return callback(error, data); + }; + return wrapped; + } // prettier-ignore + + /** + * Sign up with username, password and other attributes like phone, email + * @param {String | object} params - The user attributes used for signin + * @param {String[]} restOfAttrs - for the backward compatability + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves callback data if success + */ + public signUp( + params: string | SignUpParams, + ...restOfAttrs: any + ): Promise; + public signUp( + params: string | SignUpParams, + restOfAttrs?: string[], + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + if (!this.userPool) { + return this.rejectNoUserPool(); + } + + let username: string = null; + let password: string = null; + const attributes: CognitoUserAttribute[] = []; + let validationData: CognitoUserAttribute[] = null; + let clientMetadata; + let autoSignIn: AutoSignInOptions = { enabled: false }; + let autoSignInValidationData = {}; + let autoSignInClientMetaData: ClientMetaData = {}; + + if (params && typeof params === 'string') { + username = params; + password = restOfAttrs ? restOfAttrs[0] : null; + const email: string = restOfAttrs ? restOfAttrs[1] : null; + const phone_number: string = restOfAttrs ? restOfAttrs[2] : null; + + if (email) + attributes.push( + new CognitoUserAttribute({ Name: 'email', Value: email }) + ); + + if (phone_number) + attributes.push( + new CognitoUserAttribute({ + Name: 'phone_number', + Value: phone_number, + }) + ); + } else if (params && typeof params === 'object') { + username = params['username']; + password = params['password']; + + if (params && params.clientMetadata) { + clientMetadata = params.clientMetadata; + } else if (this._config.clientMetadata) { + clientMetadata = this._config.clientMetadata; + } + + const attrs = params['attributes']; + if (attrs) { + Object.keys(attrs).map(key => { + attributes.push( + new CognitoUserAttribute({ Name: key, Value: attrs[key] }) + ); + }); + } + + const validationDataObject = params['validationData']; + if (validationDataObject) { + validationData = []; + Object.keys(validationDataObject).map(key => { + validationData.push( + new CognitoUserAttribute({ + Name: key, + Value: validationDataObject[key], + }) + ); + }); + } + + autoSignIn = params.autoSignIn ?? { enabled: false }; + if (autoSignIn.enabled) { + this._storage.setItem('amplify-auto-sign-in', 'true'); + autoSignInValidationData = autoSignIn.validationData ?? {}; + autoSignInClientMetaData = autoSignIn.clientMetaData ?? {}; + } + } else { + return this.rejectAuthError(AuthErrorTypes.SignUpError); + } + + if (!username) { + return this.rejectAuthError(AuthErrorTypes.EmptyUsername); + } + if (!password) { + return this.rejectAuthError(AuthErrorTypes.EmptyPassword); + } + + logger.debug('signUp attrs:', attributes); + logger.debug('signUp validation data:', validationData); + + return new Promise((resolve, reject) => { + this.userPool.signUp( + username, + password, + attributes, + validationData, + (err, data) => { + if (err) { + dispatchAuthEvent( + 'signUp_failure', + err, + `${username} failed to signup` + ); + reject(err); + } else { + dispatchAuthEvent( + 'signUp', + data, + `${username} has signed up successfully` + ); + if (autoSignIn.enabled) { + this.handleAutoSignIn( + username, + password, + autoSignInValidationData, + autoSignInClientMetaData, + data + ); + } + resolve(data); + } + }, + clientMetadata + ); + }); + } + + private handleAutoSignIn( + username: string, + password: string, + validationData: {}, + clientMetadata: any, + data: any + ) { + this.autoSignInInitiated = true; + const authDetails = new AuthenticationDetails({ + Username: username, + Password: password, + ValidationData: validationData, + ClientMetadata: clientMetadata, + }); + if (data.userConfirmed) { + this.signInAfterUserConfirmed(authDetails); + } else if (this._config.signUpVerificationMethod === 'link') { + this.handleLinkAutoSignIn(authDetails); + } else { + this.handleCodeAutoSignIn(authDetails); + } + } + + private handleCodeAutoSignIn(authDetails: AuthenticationDetails) { + const listenEvent = ({ payload }) => { + if (payload.event === 'confirmSignUp') { + this.signInAfterUserConfirmed(authDetails, listenEvent); + } + }; + Hub.listen('auth', listenEvent); + } + + private handleLinkAutoSignIn(authDetails: AuthenticationDetails) { + this._storage.setItem('amplify-polling-started', 'true'); + const start = Date.now(); + const autoSignInPollingIntervalId = setInterval(() => { + if (Date.now() - start > MAX_AUTOSIGNIN_POLLING_MS) { + clearInterval(autoSignInPollingIntervalId); + dispatchAuthEvent( + 'autoSignIn_failure', + null, + 'Please confirm your account and use your credentials to sign in.' + ); + this._storage.removeItem('amplify-auto-sign-in'); + } else { + this.signInAfterUserConfirmed( + authDetails, + null, + autoSignInPollingIntervalId + ); + } + }, 5000); + } + + private async signInAfterUserConfirmed( + authDetails: AuthenticationDetails, + listenEvent?: HubCallback, + autoSignInPollingIntervalId?: ReturnType + ) { + const user = this.createCognitoUser(authDetails.getUsername()); + try { + await user.authenticateUser( + authDetails, + this.authCallbacks( + user, + value => { + dispatchAuthEvent( + 'autoSignIn', + value, + `${authDetails.getUsername()} has signed in successfully` + ); + if (listenEvent) { + Hub.remove('auth', listenEvent); + } + if (autoSignInPollingIntervalId) { + clearInterval(autoSignInPollingIntervalId); + this._storage.removeItem('amplify-polling-started'); + } + this._storage.removeItem('amplify-auto-sign-in'); + }, + error => { + logger.error(error); + this._storage.removeItem('amplify-auto-sign-in'); + } + ) + ); + } catch (error) { + logger.error(error); + } + } + + /** + * Send the verification code to confirm sign up + * @param {String} username - The username to be confirmed + * @param {String} code - The verification code + * @param {ConfirmSignUpOptions} options - other options for confirm signup + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves callback data if success + */ + public confirmSignUp( + username: string, + code: string, + options?: ConfirmSignUpOptions, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + if (!this.userPool) { + return this.rejectNoUserPool(); + } + if (!username) { + return this.rejectAuthError(AuthErrorTypes.EmptyUsername); + } + if (!code) { + return this.rejectAuthError(AuthErrorTypes.EmptyCode); + } + + const user = this.createCognitoUser(username); + const forceAliasCreation = + options && typeof options.forceAliasCreation === 'boolean' + ? options.forceAliasCreation + : true; + + let clientMetadata; + if (options && options.clientMetadata) { + clientMetadata = options.clientMetadata; + } else if (this._config.clientMetadata) { + clientMetadata = this._config.clientMetadata; + } + return new Promise((resolve, reject) => { + user.confirmRegistration( + code, + forceAliasCreation, + (err, data) => { + if (err) { + reject(err); + } else { + dispatchAuthEvent( + 'confirmSignUp', + data, + `${username} has been confirmed successfully` + ); + const autoSignIn = this.isTrueStorageValue('amplify-auto-sign-in'); + if (autoSignIn && !this.autoSignInInitiated) { + dispatchAuthEvent( + 'autoSignIn_failure', + null, + AuthErrorTypes.AutoSignInError + ); + this._storage.removeItem('amplify-auto-sign-in'); + } + resolve(data); + } + }, + clientMetadata + ); + }); + } + + private isTrueStorageValue(value: string) { + const item = this._storage.getItem(value); + return item ? item === 'true' : false; + } + + /** + * Resend the verification code + * @param {String} username - The username to be confirmed + * @param {ClientMetadata} clientMetadata - Metadata to be passed to Cognito Lambda triggers + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves code delivery details if successful + */ + public resendSignUp( + username: string, + clientMetadata: ClientMetaData = this._config.clientMetadata, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + if (!this.userPool) { + return this.rejectNoUserPool(); + } + if (!username) { + return this.rejectAuthError(AuthErrorTypes.EmptyUsername); + } + + const user = this.createCognitoUser(username); + return new Promise((resolve, reject) => { + user.resendConfirmationCode((err, data) => { + if (err) { + reject(err); + } else { + resolve(data); + } + }, clientMetadata); + }); + } + + /** + * Sign in + * @param {String | SignInOpts} usernameOrSignInOpts - The username to be signed in or the sign in options + * @param {String} pw - The password of the username + * @param {ClientMetaData} clientMetadata - Client metadata for custom workflows + * @return - A promise resolves the CognitoUser + */ + public signIn( + usernameOrSignInOpts: string | SignInOpts, + pw?: string, + clientMetadata: ClientMetaData = this._config.clientMetadata, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + if (!this.userPool) { + return this.rejectNoUserPool(); + } + + let username = null; + let password = null; + let validationData = {}; + + // for backward compatibility + if (typeof usernameOrSignInOpts === 'string') { + username = usernameOrSignInOpts; + password = pw; + } else if (isUsernamePasswordOpts(usernameOrSignInOpts)) { + if (typeof pw !== 'undefined') { + logger.warn( + 'The password should be defined under the first parameter object!' + ); + } + username = usernameOrSignInOpts.username; + password = usernameOrSignInOpts.password; + validationData = usernameOrSignInOpts.validationData; + } else { + return this.rejectAuthError(AuthErrorTypes.InvalidUsername); + } + if (!username) { + return this.rejectAuthError(AuthErrorTypes.EmptyUsername); + } + const authDetails = new AuthenticationDetails({ + Username: username, + Password: password, + ValidationData: validationData, + ClientMetadata: clientMetadata, + }); + if (password) { + return this.signInWithPassword(authDetails); + } else { + return this.signInWithoutPassword(authDetails); + } + } + + /** + * Return an object with the authentication callbacks + * @param {CognitoUser} 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 + ): IAuthenticationCallback { + const that = this; + return { + onSuccess: async session => { + logger.debug(session); + delete user['challengeName']; + delete user['challengeParam']; + try { + await this.Credentials.clear(); + const cred = await this.Credentials.set(session, 'session'); + logger.debug('succeed to get cognito credentials', cred); + } catch (e) { + logger.debug('cannot get cognito credentials', e); + } finally { + try { + // In order to get user attributes and MFA methods + // We need to trigger currentUserPoolUser again + const currentUser = await this.currentUserPoolUser(); + that.user = currentUser; + dispatchAuthEvent( + 'signIn', + currentUser, + `A user ${user.getUsername()} has been signed in` + ); + resolve(currentUser); + } catch (e) { + logger.error('Failed to get the signed in user', e); + reject(e); + } + } + }, + onFailure: err => { + logger.debug('signIn failure', err); + dispatchAuthEvent( + 'signIn_failure', + err, + `${user.getUsername()} failed to signin` + ); + reject(err); + }, + customChallenge: challengeParam => { + logger.debug('signIn custom challenge answer required'); + user['challengeName'] = 'CUSTOM_CHALLENGE'; + user['challengeParam'] = challengeParam; + resolve(user); + }, + mfaRequired: (challengeName, challengeParam) => { + logger.debug('signIn MFA required'); + user['challengeName'] = challengeName; + user['challengeParam'] = challengeParam; + resolve(user); + }, + mfaSetup: (challengeName, challengeParam) => { + logger.debug('signIn mfa setup', challengeName); + user['challengeName'] = challengeName; + user['challengeParam'] = challengeParam; + resolve(user); + }, + newPasswordRequired: (userAttributes, requiredAttributes) => { + logger.debug('signIn new password'); + user['challengeName'] = 'NEW_PASSWORD_REQUIRED'; + user['challengeParam'] = { + userAttributes, + requiredAttributes, + }; + resolve(user); + }, + totpRequired: (challengeName, challengeParam) => { + logger.debug('signIn totpRequired'); + user['challengeName'] = challengeName; + user['challengeParam'] = challengeParam; + resolve(user); + }, + selectMFAType: (challengeName, challengeParam) => { + logger.debug('signIn selectMFAType', challengeName); + user['challengeName'] = challengeName; + user['challengeParam'] = challengeParam; + resolve(user); + }, + }; + } + + /** + * Sign in with a password + * @private + * @param {AuthenticationDetails} authDetails - the user sign in data + * @return - A promise resolves the CognitoUser object if success or mfa required + */ + private signInWithPassword( + authDetails: AuthenticationDetails + ): Promise { + if (this.pendingSignIn) { + throw new Error('Pending sign-in attempt already in progress'); + } + + const user = this.createCognitoUser(authDetails.getUsername()); + + this.pendingSignIn = new Promise((resolve, reject) => { + user.authenticateUser( + authDetails, + this.authCallbacks( + user, + value => { + this.pendingSignIn = null; + resolve(value); + }, + error => { + this.pendingSignIn = null; + reject(error); + } + ) + ); + }); + + return this.pendingSignIn; + } + + /** + * 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 + */ + private signInWithoutPassword( + authDetails: AuthenticationDetails + ): Promise { + const user = this.createCognitoUser(authDetails.getUsername()); + user.setAuthenticationFlowType('CUSTOM_AUTH'); + + return new Promise((resolve, reject) => { + user.initiateAuth(authDetails, this.authCallbacks(user, resolve, reject)); + }); + } + + /** + * This was previously used by an authenticated user to get MFAOptions, + * but no longer returns a meaningful response. Refer to the documentation for + * how to setup and use MFA: https://docs.amplify.aws/lib/auth/mfa/q/platform/js + * @deprecated + * @param {CognitoUser} user - the current user + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves the current preferred mfa option if success + */ + public getMFAOptions( + user: CognitoUser | any, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + return new Promise((res, rej) => { + user.getMFAOptions((err, mfaOptions) => { + if (err) { + logger.debug('get MFA Options failed', err); + rej(err); + return; + } + logger.debug('get MFA options success', mfaOptions); + res(mfaOptions); + return; + }); + }); + } + + /** + * get preferred mfa method + * @param {CognitoUser} user - the current cognito user + * @param {GetPreferredMFAOpts} params - options for getting the current user preferred MFA + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + */ + public getPreferredMFA( + user: CognitoUser | any, + params?: GetPreferredMFAOpts, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + 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( + async (err, data) => { + if (err) { + logger.debug('getting preferred mfa 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; + } + } + rej(err); + return; + } + + const mfaType = that._getMfaTypeFromUserData(data); + if (!mfaType) { + rej('invalid MFA Type'); + return; + } else { + res(mfaType); + return; + } + }, + { bypassCache, clientMetadata } + ); + }); + } + + private _getMfaTypeFromUserData(data) { + let ret = null; + const preferredMFA = data.PreferredMfaSetting; + // if the user has used Auth.setPreferredMFA() to setup the mfa type + // then the "PreferredMfaSetting" would exist in the response + if (preferredMFA) { + ret = preferredMFA; + } else { + // if mfaList exists but empty, then its noMFA + const mfaList = data.UserMFASettingList; + if (!mfaList) { + // if SMS was enabled by using Auth.enableSMS(), + // the response would contain MFAOptions + // as for now Cognito only supports for SMS, so we will say it is 'SMS_MFA' + // if it does not exist, then it should be NOMFA + const MFAOptions = data.MFAOptions; + if (MFAOptions) { + ret = 'SMS_MFA'; + } else { + ret = 'NOMFA'; + } + } else if (mfaList.length === 0) { + ret = 'NOMFA'; + } else { + logger.debug('invalid case for getPreferredMFA', data); + } + } + return ret; + } + + private _getUserData(user, params) { + 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; + } + } + rej(err); + return; + } else { + res(data); + } + }, params); + }); + } + + /** + * set preferred MFA method + * @param {CognitoUser} user - the current Cognito user + * @param {string} mfaMethod - preferred mfa method + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolve if success + */ + public async setPreferredMFA( + user: CognitoUser | any, + mfaMethod: 'TOTP' | 'SMS' | 'NOMFA' | 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA', + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + const clientMetadata = this._config.clientMetadata; // TODO: verify behavior if this is override during signIn + + const userData = await this._getUserData(user, { + bypassCache: true, + clientMetadata, + }); + let smsMfaSettings = null; + let totpMfaSettings = null; + + switch (mfaMethod) { + case 'TOTP': + case 'SOFTWARE_TOKEN_MFA': + totpMfaSettings = { + PreferredMfa: true, + Enabled: true, + }; + break; + case 'SMS': + case 'SMS_MFA': + smsMfaSettings = { + PreferredMfa: true, + Enabled: true, + }; + break; + case 'NOMFA': + const mfaList = userData['UserMFASettingList']; + const currentMFAType = await this._getMfaTypeFromUserData(userData); + if (currentMFAType === 'NOMFA') { + return Promise.resolve('No change for mfa type'); + } else if (currentMFAType === 'SMS_MFA') { + smsMfaSettings = { + PreferredMfa: false, + Enabled: false, + }; + } else if (currentMFAType === 'SOFTWARE_TOKEN_MFA') { + totpMfaSettings = { + PreferredMfa: false, + Enabled: false, + }; + } else { + return this.rejectAuthError(AuthErrorTypes.InvalidMFA); + } + // if there is a UserMFASettingList in the response + // we need to disable every mfa type in that list + if (mfaList && mfaList.length !== 0) { + // to disable SMS or TOTP if exists in that list + mfaList.forEach(mfaType => { + if (mfaType === 'SMS_MFA') { + smsMfaSettings = { + PreferredMfa: false, + Enabled: false, + }; + } else if (mfaType === 'SOFTWARE_TOKEN_MFA') { + totpMfaSettings = { + PreferredMfa: false, + Enabled: false, + }; + } + }); + } + break; + default: + logger.debug('no validmfa method provided'); + return this.rejectAuthError(AuthErrorTypes.NoMFA); + } + + const that = this; + return new Promise((res, rej) => { + user.setUserMfaPreference( + smsMfaSettings, + totpMfaSettings, + (err, result) => { + if (err) { + logger.debug('Set user mfa preference error', err); + return rej(err); + } + 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( + 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; + } + } + return rej(err); + } else { + return res(result); + } + }, + { + bypassCache: true, + clientMetadata, + } + ); + } + ); + }); + } + + /** + * disable SMS + * @deprecated + * @param {CognitoUser} user - the current user + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves is success + */ + public disableSMS( + user: CognitoUser, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + return new Promise((res, rej) => { + user.disableMFA((err, data) => { + if (err) { + logger.debug('disable mfa failed', err); + rej(err); + return; + } + logger.debug('disable mfa succeed', data); + res(data); + return; + }); + }); + } + + /** + * enable SMS + * @deprecated + * @param {CognitoUser} user - the current user + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves is success + */ + public enableSMS( + user: CognitoUser, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + return new Promise((res, rej) => { + user.enableMFA((err, data) => { + if (err) { + logger.debug('enable mfa failed', err); + rej(err); + return; + } + logger.debug('enable mfa succeed', data); + res(data); + return; + }); + }); + } + + /** + * Setup TOTP + * @param {CognitoUser} user - the current user + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves with the secret code if success + */ + public setupTOTP( + user: CognitoUser | any, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + 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; + }, + }); + }); + } + + /** + * verify TOTP setup + * @param {CognitoUser} user - the current user + * @param {string} challengeAnswer - challenge answer + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves is success + */ + public verifyTotpToken( + user: CognitoUser | any, + challengeAnswer: string, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + logger.debug('verification totp token', user, challengeAnswer); + + let signInUserSession; + if (user && typeof user.getSignInUserSession === 'function') { + signInUserSession = (user as CognitoUser).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) { + dispatchAuthEvent( + 'signIn', + user, + `A user ${user.getUsername()} has been signed in` + ); + } + dispatchAuthEvent( + 'verify', + user, + `A user ${user.getUsername()} has been verified` + ); + logger.debug('verifyTotpToken success', data); + res(data); + return; + }, + }); + }); + } + + /** + * Send MFA code to confirm sign in + * @param {Object} user - The CognitoUser object + * @param {String} code - The confirmation code + * @param {string} mfaType - optional mfaType: 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA' + * @param {ClientMetaData} clientMetadata - optional client metadata defaults to config + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + */ + public confirmSignIn( + user: CognitoUser | any, + code: string, + mfaType?: 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA' | null, + clientMetadata: ClientMetaData = this._config.clientMetadata, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + if (!code) { + return this.rejectAuthError(AuthErrorTypes.EmptyCode); + } + + const that = this; + return new Promise((resolve, reject) => { + user.sendMFACode( + code, + { + onSuccess: async session => { + logger.debug(session); + try { + await this.Credentials.clear(); + const cred = await this.Credentials.set(session, 'session'); + logger.debug('succeed to get cognito credentials', cred); + } catch (e) { + logger.debug('cannot get cognito credentials', e); + } finally { + that.user = user; + try { + const currentUser = await this.currentUserPoolUser(); + user.attributes = currentUser.attributes; + } catch (e) { + logger.debug('cannot get updated Cognito User', e); + } + dispatchAuthEvent( + 'signIn', + user, + `A user ${user.getUsername()} has been signed in` + ); + resolve(user); + } + }, + onFailure: err => { + logger.debug('confirm signIn failure', err); + reject(err); + }, + }, + mfaType, + clientMetadata + ); + }); + } + + public completeNewPassword( + user: CognitoUser | any, + password: string, + requiredAttributes: any = {}, + clientMetadata: ClientMetaData = this._config.clientMetadata, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + if (!password) { + return this.rejectAuthError(AuthErrorTypes.EmptyPassword); + } + + const that = this; + return new Promise((resolve, reject) => { + user.completeNewPasswordChallenge( + password, + requiredAttributes, + { + onSuccess: async session => { + logger.debug(session); + try { + await this.Credentials.clear(); + const cred = await this.Credentials.set(session, 'session'); + logger.debug('succeed to get cognito credentials', cred); + } catch (e) { + logger.debug('cannot get cognito credentials', e); + } finally { + that.user = user; + dispatchAuthEvent( + 'signIn', + user, + `A user ${user.getUsername()} has been signed in` + ); + resolve(user); + } + }, + onFailure: err => { + logger.debug('completeNewPassword failure', err); + dispatchAuthEvent( + 'completeNewPassword_failure', + err, + `${this.user} failed to complete the new password flow` + ); + reject(err); + }, + mfaRequired: (challengeName, challengeParam) => { + logger.debug('signIn MFA required'); + user['challengeName'] = challengeName; + user['challengeParam'] = challengeParam; + resolve(user); + }, + mfaSetup: (challengeName, challengeParam) => { + logger.debug('signIn mfa setup', challengeName); + user['challengeName'] = challengeName; + user['challengeParam'] = challengeParam; + resolve(user); + }, + totpRequired: (challengeName, challengeParam) => { + logger.debug('signIn mfa setup', challengeName); + user['challengeName'] = challengeName; + user['challengeParam'] = challengeParam; + resolve(user); + }, + }, + clientMetadata + ); + }); + } + + /** + * Send the answer to a custom challenge + * @param {CognitoUser} user - The CognitoUser object + * @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, + challengeResponses: string, + clientMetadata: ClientMetaData = this._config.clientMetadata, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + if (!this.userPool) { + return this.rejectNoUserPool(); + } + if (!challengeResponses) { + return this.rejectAuthError(AuthErrorTypes.EmptyChallengeResponse); + } + + const that = this; + return new Promise((resolve, reject) => { + user.sendCustomChallengeAnswer( + challengeResponses, + this.authCallbacks(user, resolve, reject), + clientMetadata + ); + }); + } + + /** + * Delete an authenticated users' attributes + * @param {CognitoUser} user - The currently logged in user object + * @param {string[]} attributeNames - Attributes to delete + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return {Promise} + **/ + public deleteUserAttributes( + user: CognitoUser | any, + attributeNames: string[], + customUserAgentDetails?: CustomUserAgentDetails + ) { + const that = this; + return new Promise((resolve, reject) => { + that.userSession(user).then(session => { + user.deleteAttributes(attributeNames, (err, result) => { + if (err) { + return reject(err); + } else { + return resolve(result); + } + }); + }); + }); + } + + /** + * Delete the current authenticated user + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return {Promise} + **/ + // TODO: Check return type void + public async deleteUser( + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + try { + await this._storageSync; + } catch (e) { + logger.debug('Failed to sync cache info into memory', e); + throw new Error(e); + } + + const isSignedInHostedUI = + this._oAuthHandler && + this._storage.getItem('amplify-signin-with-hostedUI') === 'true'; + + return new Promise(async (res, rej) => { + if (this.userPool) { + const user = this.userPool.getCurrentUser(); + + if (!user) { + logger.debug('Failed to get user from user pool'); + return rej(new Error('No current user.')); + } else { + user.getSession(async (err, session) => { + if (err) { + logger.debug('Failed to get the user session', 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; + } + } + 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); + } else { + dispatchAuthEvent( + 'signOut', + this.user, + `A user has been signed out` + ); + res(result); + } + } + }); + } + }); + } + } else { + logger.debug('no Congito User pool'); + rej(new Error('Cognito User pool does not exist')); + } + }); + } + + /** + * Update an authenticated users' attributes + * @param {CognitoUser} user - The currently logged in user object + * @param {object} attributes - attributes to update + * @param {ClientMetaData} clientMetadata - optional client metadata, defaults to config + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return {Promise} + **/ + public updateUserAttributes( + user: CognitoUser | any, + attributes: object, + clientMetadata: ClientMetaData = this._config.clientMetadata, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + const attributeList: ICognitoUserAttributeData[] = []; + const that = this; + return new Promise((resolve, reject) => { + that.userSession(user).then(session => { + for (const key in attributes) { + if (key !== 'sub' && key.indexOf('_verified') < 0) { + const attr: ICognitoUserAttributeData = { + Name: key, + Value: attributes[key], + }; + attributeList.push(attr); + } + } + user.updateAttributes( + attributeList, + (err, result, details) => { + if (err) { + dispatchAuthEvent( + 'updateUserAttributes_failure', + err, + 'Failed to update attributes' + ); + return reject(err); + } else { + const attrs = this.createUpdateAttributesResultList( + attributes as Record, + details?.CodeDeliveryDetailsList + ); + dispatchAuthEvent( + 'updateUserAttributes', + attrs, + 'Attributes successfully updated' + ); + return resolve(result); + } + }, + clientMetadata + ); + }); + }); + } + + private createUpdateAttributesResultList( + attributes: Record, + codeDeliveryDetailsList?: CodeDeliveryDetails[] + ): Record { + const attrs = {}; + Object.keys(attributes).forEach(key => { + attrs[key] = { + isUpdated: true, + }; + const codeDeliveryDetails = codeDeliveryDetailsList?.find( + value => value.AttributeName === key + ); + if (codeDeliveryDetails) { + attrs[key].isUpdated = false; + attrs[key].codeDeliveryDetails = codeDeliveryDetails; + } + }); + return attrs; + } + + /** + * Return user attributes + * @param {Object} user - The CognitoUser object + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves to user attributes if success + */ + public userAttributes( + user: CognitoUser | any, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + return new Promise((resolve, reject) => { + this.userSession(user).then(session => { + user.getUserAttributes((err, attributes) => { + if (err) { + reject(err); + } else { + resolve(attributes); + } + }); + }); + }); + } + + public verifiedContact( + user: CognitoUser | any, + customUserAgentDetails?: CustomUserAgentDetails + ) { + const that = this; + return this.userAttributes(user).then(attributes => { + const attrs = that.attributesToObject(attributes); + const unverified = {}; + const verified = {}; + if (attrs['email']) { + if (attrs['email_verified']) { + verified['email'] = attrs['email']; + } else { + unverified['email'] = attrs['email']; + } + } + if (attrs['phone_number']) { + if (attrs['phone_number_verified']) { + verified['phone_number'] = attrs['phone_number']; + } else { + unverified['phone_number'] = attrs['phone_number']; + } + } + return { + verified, + unverified, + }; + }); + } + + private isErrorWithMessage(err: any): err is { message: string } { + return ( + typeof err === 'object' && + Object.prototype.hasOwnProperty.call(err, 'message') + ); + } + + // Session revoked by another app + private isTokenRevokedError( + err: any + ): err is { message: 'Access Token has been revoked' } { + return ( + this.isErrorWithMessage(err) && + err.message === 'Access Token has been revoked' + ); + } + + private isRefreshTokenRevokedError( + err: any + ): err is { message: 'Refresh Token has been revoked' } { + return ( + this.isErrorWithMessage(err) && + err.message === 'Refresh Token has been revoked' + ); + } + + private isUserDisabledError( + err: any + ): err is { message: 'User is disabled.' } { + return this.isErrorWithMessage(err) && err.message === 'User is disabled.'; + } + + private isUserDoesNotExistError( + err: any + ): err is { message: 'User does not exist.' } { + return ( + this.isErrorWithMessage(err) && err.message === 'User does not exist.' + ); + } + + private isRefreshTokenExpiredError( + err: any + ): err is { message: 'Refresh Token has expired' } { + return ( + this.isErrorWithMessage(err) && + err.message === 'Refresh Token has expired' + ); + } + + private isPasswordResetRequiredError( + err: any + ): err is { message: 'Password reset required for the user' } { + return ( + this.isErrorWithMessage(err) && + err.message === 'Password reset required for the user' + ); + } + + private isSignedInHostedUI() { + return ( + this._oAuthHandler && + this._storage.getItem('amplify-signin-with-hostedUI') === 'true' + ); + } + + private isSessionInvalid(err: any) { + return ( + this.isUserDisabledError(err) || + this.isUserDoesNotExistError(err) || + this.isTokenRevokedError(err) || + this.isRefreshTokenRevokedError(err) || + this.isRefreshTokenExpiredError(err) || + this.isPasswordResetRequiredError(err) + ); + } + + private async cleanUpInvalidSession(user: CognitoUser) { + user.signOut(); + this.user = null; + try { + await this.cleanCachedItems(); // clean aws credentials + } catch (e) { + logger.debug('failed to clear cached items'); + } + if (this.isSignedInHostedUI()) { + return new Promise((res, rej) => { + this.oAuthSignOutRedirect(res, rej); + }); + } else { + dispatchAuthEvent('signOut', this.user, `A user has been signed out`); + } + } + + /** + * Get current authenticated user + * @param {CurrentUserOpts} params - options for getting the current user + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves to current authenticated CognitoUser if success + */ + public currentUserPoolUser( + params?: CurrentUserOpts, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + if (!this.userPool) { + return this.rejectNoUserPool(); + } + + return new Promise((res, rej) => { + this._storageSync + .then(async () => { + if (this.isOAuthInProgress()) { + logger.debug('OAuth signIn in progress, waiting for resolution...'); + + await new Promise(res => { + const timeoutId = setTimeout(() => { + logger.debug('OAuth signIn in progress timeout'); + + Hub.remove('auth', hostedUISignCallback); + + res(); + }, OAUTH_FLOW_MS_TIMEOUT); + + Hub.listen('auth', hostedUISignCallback); + + function hostedUISignCallback({ payload }) { + const { event } = payload; + + if ( + event === 'cognitoHostedUI' || + event === 'cognitoHostedUI_failure' + ) { + logger.debug(`OAuth signIn resolved: ${event}`); + clearTimeout(timeoutId); + + Hub.remove('auth', hostedUISignCallback); + + res(); + } + } + }); + } + + const user = this.userPool.getCurrentUser(); + + if (!user) { + logger.debug('Failed to get user from user pool'); + rej('No current user'); + return; + } + + // refresh the session if the session expired. + try { + const session = await this._userSession(user); + + // get user data from Cognito + const bypassCache = params ? params.bypassCache : false; + + if (bypassCache) { + await this.Credentials.clear(); + } + + const clientMetadata = this._config.clientMetadata; + + // validate the token's scope first before calling this function + const { scope = '' } = session.getAccessToken().decodePayload(); + if (scope.split(' ').includes(USER_ADMIN_SCOPE)) { + 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; + } + rej(err); + } else { + res(user); + } + return; + } + const preferredMFA = data.PreferredMfaSetting || 'NOMFA'; + const attributeList = []; + + for (let i = 0; i < data.UserAttributes.length; i++) { + const attribute = { + Name: data.UserAttributes[i].Name, + Value: data.UserAttributes[i].Value, + }; + const userAttribute = new CognitoUserAttribute(attribute); + attributeList.push(userAttribute); + } + + const attributes = this.attributesToObject(attributeList); + Object.assign(user, { attributes, preferredMFA }); + return res(user); + }, + { bypassCache, clientMetadata } + ); + } 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); + } + } catch (err) { + rej(err); + } + }) + .catch(e => { + logger.debug('Failed to sync cache info into memory', e); + return rej(e); + }); + }); + } + + private isOAuthInProgress(): boolean { + return this.oAuthFlowInProgress; + } + + /** + * Get current authenticated user + * @param {CurrentUserOpts} - options for getting the current user + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves to current authenticated CognitoUser if success + */ + public async currentAuthenticatedUser( + params?: CurrentUserOpts, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + logger.debug('getting current authenticated user'); + let federatedUser = null; + try { + await this._storageSync; + } catch (e) { + logger.debug('Failed to sync cache info into memory', e); + throw e; + } + + try { + const federatedInfo = JSON.parse( + this._storage.getItem('aws-amplify-federatedInfo') + ); + if (federatedInfo) { + federatedUser = { + ...federatedInfo.user, + token: federatedInfo.token, + }; + } + } catch (e) { + logger.debug('cannot load federated user from auth storage'); + } + + if (federatedUser) { + this.user = federatedUser; + logger.debug('get current authenticated federated user', this.user); + return this.user; + } else { + logger.debug('get current authenticated userpool user'); + let user = null; + try { + user = await this.currentUserPoolUser(params); + } catch (e) { + if (e === 'No userPool') { + logger.error( + 'Cannot get the current user because the user pool is missing. ' + + 'Please make sure the Auth module is configured with a valid Cognito User Pool ID' + ); + } + logger.debug('The user is not authenticated by the error', e); + return Promise.reject('The user is not authenticated'); + } + this.user = user; + return this.user; + } + } + + /** + * Get current user's session + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves to session object if success + */ + public currentSession( + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + const that = this; + logger.debug('Getting current session'); + // Purposely not calling the reject method here because we don't need a console error + if (!this.userPool) { + return Promise.reject(new Error('No User Pool in the configuration.')); + } + + return new Promise((res, rej) => { + that + .currentUserPoolUser() + .then(user => { + that + .userSession(user) + .then(session => { + res(session); + return; + }) + .catch(e => { + logger.debug('Failed to get the current session', e); + rej(e); + return; + }); + }) + .catch(e => { + logger.debug('Failed to get the current user', e); + rej(e); + return; + }); + }); + } + + private async _userSession(user?: CognitoUser): Promise { + if (!user) { + logger.debug('the user is null'); + return this.rejectAuthError(AuthErrorTypes.NoUserSession); + } + const clientMetadata = this._config.clientMetadata; + // Debouncing the concurrent userSession calls by caching the promise. + // This solution assumes users will always call this function with the same CognitoUser instance. + if (this.inflightSessionPromiseCounter === 0) { + this.inflightSessionPromise = new Promise( + (res, rej) => { + user.getSession( + async (err, session) => { + if (err) { + logger.debug('Failed to get the session from user', user); + 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; + } + } + rej(err); + return; + } else { + logger.debug('Succeed to get the user session', session); + res(session); + return; + } + }, + { clientMetadata } + ); + } + ); + } + this.inflightSessionPromiseCounter++; + + try { + const userSession = await this.inflightSessionPromise; + // Set private member. Avoid user.setSignInUserSession() to prevent excessive localstorage refresh. + // @ts-ignore + user.signInUserSession = userSession; + return userSession!; + } finally { + this.inflightSessionPromiseCounter--; + } + } + + /** + * Get the corresponding user session + * @param {Object} user - The CognitoUser object + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves to the session + */ + public userSession( + user, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + return this._userSession(user); + } + + /** + * Get authenticated credentials of current user. + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves to be current user's credentials + */ + public async currentUserCredentials( + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + logger.debug('Getting current user credentials'); + + try { + await this._storageSync; + } catch (e) { + logger.debug('Failed to sync cache info into memory', e); + throw e; + } + + // first to check whether there is federation info in the auth storage + let federatedInfo = null; + try { + federatedInfo = JSON.parse( + this._storage.getItem('aws-amplify-federatedInfo') + ); + } catch (e) { + logger.debug('failed to get or parse item aws-amplify-federatedInfo', e); + } + + if (federatedInfo) { + // refresh the jwt token here if necessary + return this.Credentials.refreshFederatedToken(federatedInfo); + } else { + return this.currentSession() + .then(session => { + logger.debug('getting session success', session); + return this.Credentials.set(session, 'session'); + }) + .catch(() => { + logger.debug('getting guest credentials'); + return this.Credentials.set(null, 'guest'); + }); + } + } + + public currentCredentials( + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + logger.debug('getting current credentials'); + return this.Credentials.get(); + } + + /** + * Initiate an attribute confirmation request + * @param {Object} user - The CognitoUser + * @param {Object} attr - The attributes to be verified + * @return - A promise resolves to callback data if success + */ + public verifyUserAttribute( + user: CognitoUser | any, + attr: string, + clientMetadata: ClientMetaData = this._config.clientMetadata, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + return new Promise((resolve, reject) => { + user.getAttributeVerificationCode( + attr, + { + onSuccess(success) { + return resolve(success); + }, + onFailure(err) { + return reject(err); + }, + }, + clientMetadata + ); + }); + } + + /** + * Confirm an attribute using a confirmation code + * @param {Object} user - The CognitoUser + * @param {Object} attr - The attribute to be verified + * @param {String} code - The confirmation code + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves to callback data if success + */ + public verifyUserAttributeSubmit( + user: CognitoUser | any, + attr: string, + code: string, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + if (!code) { + return this.rejectAuthError(AuthErrorTypes.EmptyCode); + } + + return new Promise((resolve, reject) => { + user.verifyAttribute(attr, code, { + onSuccess(data) { + resolve(data); + return; + }, + onFailure(err) { + reject(err); + return; + }, + }); + }); + } + + public verifyCurrentUserAttribute( + attr: string, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + const that = this; + return that + .currentUserPoolUser() + .then(user => that.verifyUserAttribute(user, attr)); + } + + /** + * Confirm current user's attribute using a confirmation code + * @param {Object} attr - The attribute to be verified + * @param {String} code - The confirmation code + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves to callback data if success + */ + verifyCurrentUserAttributeSubmit( + attr: string, + code: string, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + const that = this; + return that + .currentUserPoolUser() + .then(user => that.verifyUserAttributeSubmit(user, attr, code)); + } + + private async cognitoIdentitySignOut( + opts: SignOutOpts, + user: CognitoUser | any + ) { + try { + await this._storageSync; + } catch (e) { + logger.debug('Failed to sync cache info into memory', e); + throw e; + } + + const isSignedInHostedUI = + this._oAuthHandler && + this._storage.getItem('amplify-signin-with-hostedUI') === 'true'; + + return new Promise((res, rej) => { + if (opts && opts.global) { + logger.debug('user global sign out', user); + // 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( + async (err, result) => { + if (err) { + logger.debug('failed to get the user session', 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; + } + } + 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); + }, + }); + }, + { clientMetadata } + ); + } else { + logger.debug('user sign out', user); + user.signOut(() => { + if (isSignedInHostedUI) { + this.oAuthSignOutRedirect(res, rej); + } else { + return res(); + } + }); + } + }); + } + + private oAuthSignOutRedirect( + resolve: () => void, + reject: (reason?: any) => void + ) { + const { isBrowser } = browserOrNode(); + + if (isBrowser) { + this.oAuthSignOutRedirectOrReject(reject); + } else { + this.oAuthSignOutAndResolve(resolve); + } + } + + private oAuthSignOutAndResolve(resolve: () => void) { + this._oAuthHandler.signOut(); + resolve(); + } + + private oAuthSignOutRedirectOrReject(reject: (reason?: any) => void) { + this._oAuthHandler.signOut(); // this method redirects url + + // App should be redirected to another url otherwise it will reject + setTimeout(() => reject(Error('Signout timeout fail')), 3000); + } + + /** + * Sign out method + * @param {SignOutOpts} opts - options for sign out + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolved if success + */ + public async signOut( + opts?: SignOutOpts, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + try { + await this.cleanCachedItems(); + } catch (e) { + logger.debug('failed to clear cached items'); + } + + if (this.userPool) { + const user = this.userPool.getCurrentUser(); + if (user) { + await this.cognitoIdentitySignOut(opts, user); + } else { + logger.debug('no current Cognito user'); + } + } else { + logger.debug('no Cognito User pool'); + } + + /** + * Note for future refactor - no reliable way to get username with + * Cognito User Pools vs Identity when federating with Social Providers + * This is why we need a well structured session object that can be inspected + * and information passed back in the message below for Hub dispatch + */ + dispatchAuthEvent('signOut', this.user, `A user has been signed out`); + this.user = null; + } + + private async cleanCachedItems() { + // clear cognito cached item + await this.Credentials.clear(); + } + + /** + * Change a password for an authenticated user + * @param {Object} user - The CognitoUser object + * @param {String} oldPassword - the current password + * @param {String} newPassword - the requested new password + * @param {ClientMetaData} clientMetadata - optional client metadata, defaults to config + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves if success + */ + public changePassword( + user: CognitoUser | any, + oldPassword: string, + newPassword: string, + clientMetadata: ClientMetaData = this._config.clientMetadata, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise<'SUCCESS'> { + return new Promise((resolve, reject) => { + this.userSession(user).then(session => { + user.changePassword( + oldPassword, + newPassword, + (err, data) => { + if (err) { + logger.debug('change password failure', err); + return reject(err); + } else { + return resolve(data); + } + }, + clientMetadata + ); + }); + }); + } + + /** + * Initiate a forgot password request + * @param {String} username - the username to change password + * @param {ClientMetaData} clientMetadata - optional client metadata, defaults to config + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise resolves if success + */ + public forgotPassword( + username: string, + clientMetadata: ClientMetaData = this._config.clientMetadata, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + if (!this.userPool) { + return this.rejectNoUserPool(); + } + if (!username) { + return this.rejectAuthError(AuthErrorTypes.EmptyUsername); + } + + const user = this.createCognitoUser(username); + return new Promise((resolve, reject) => { + user.forgotPassword( + { + onSuccess: () => { + resolve(); + return; + }, + onFailure: err => { + logger.debug('forgot password failure', err); + dispatchAuthEvent( + 'forgotPassword_failure', + err, + `${username} forgotPassword failed` + ); + reject(err); + return; + }, + inputVerificationCode: data => { + dispatchAuthEvent( + 'forgotPassword', + user, + `${username} has initiated forgot password flow` + ); + resolve(data); + return; + }, + }, + clientMetadata + ); + }); + } + + /** + * Confirm a new password using a confirmation Code + * @param {String} username - The username + * @param {String} code - The confirmation code + * @param {String} password - The new password + * @param {ClientMetaData} clientMetadata - optional client metadata, defaults to config + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return - A promise that resolves if success + */ + public forgotPasswordSubmit( + username: string, + code: string, + password: string, + clientMetadata: ClientMetaData = this._config.clientMetadata, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + if (!this.userPool) { + return this.rejectNoUserPool(); + } + if (!username) { + return this.rejectAuthError(AuthErrorTypes.EmptyUsername); + } + if (!code) { + return this.rejectAuthError(AuthErrorTypes.EmptyCode); + } + if (!password) { + return this.rejectAuthError(AuthErrorTypes.EmptyPassword); + } + + const user = this.createCognitoUser(username); + return new Promise((resolve, reject) => { + user.confirmPassword( + code, + password, + { + onSuccess: success => { + dispatchAuthEvent( + 'forgotPasswordSubmit', + user, + `${username} forgotPasswordSubmit successful` + ); + resolve(success); + return; + }, + onFailure: err => { + dispatchAuthEvent( + 'forgotPasswordSubmit_failure', + err, + `${username} forgotPasswordSubmit failed` + ); + reject(err); + return; + }, + }, + clientMetadata + ); + }); + } + + /** + * Get user information + * @async + * @param {CustomUserAgentDetails} customUserAgentDetails - Optional parameter to send user agent details + * @return {Object }- current User's information + */ + public async currentUserInfo( + customUserAgentDetails?: CustomUserAgentDetails + ) { + const source = this.Credentials.getCredSource(); + + if (!source || source === 'aws' || source === 'userPool') { + const user = await this.currentUserPoolUser().catch(err => + logger.error(err) + ); + if (!user) { + return null; + } + + try { + const attributes = await this.userAttributes(user); + const userAttrs: object = this.attributesToObject(attributes); + let credentials = null; + try { + credentials = await this.currentCredentials(); + } catch (e) { + logger.debug( + 'Failed to retrieve credentials while getting current user info', + e + ); + } + + const info = { + id: credentials ? credentials.identityId : undefined, + username: user.getUsername(), + attributes: userAttrs, + }; + return info; + } catch (err) { + logger.error('currentUserInfo error', err); + return {}; + } + } + + if (source === 'federated') { + const user = this.user; + return user ? user : {}; + } + } + + public async federatedSignIn( + providerOrOptions: + | LegacyProvider + | FederatedSignInOptions + | FederatedSignInOptionsCustom, + response?: FederatedResponse, + user?: FederatedUser, + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + if (!this._config.identityPoolId && !this._config.userPoolId) { + throw new Error( + `Federation requires either a User Pool or Identity Pool in config` + ); + } + + // Ensure backwards compatability + if (typeof providerOrOptions === 'undefined') { + if (this._config.identityPoolId && !this._config.userPoolId) { + throw new Error( + `Federation with Identity Pools requires tokens passed as arguments` + ); + } + } + + if ( + isFederatedSignInOptions(providerOrOptions) || + isFederatedSignInOptionsCustom(providerOrOptions) || + hasCustomState(providerOrOptions) || + typeof providerOrOptions === 'undefined' + ) { + const options = providerOrOptions || { + provider: CognitoHostedUIIdentityProvider.Cognito, + }; + const provider = isFederatedSignInOptions(options) + ? options.provider + : (options as FederatedSignInOptionsCustom).customProvider; + + const customState = isFederatedSignInOptions(options) + ? options.customState + : (options as FederatedSignInOptionsCustom).customState; + + if (this._config.userPoolId) { + const client_id = isCognitoHostedOpts(this._config.oauth) + ? this._config.userPoolWebClientId + : this._config.oauth.clientID; + /*Note: Invenstigate automatically adding trailing slash */ + const redirect_uri = isCognitoHostedOpts(this._config.oauth) + ? this._config.oauth.redirectSignIn + : this._config.oauth.redirectUri; + + this._oAuthHandler.oauthSignIn( + this._config.oauth.responseType, + this._config.oauth.domain, + redirect_uri, + client_id, + provider, + customState + ); + } + } else { + const provider = providerOrOptions; + // To check if the user is already logged in + try { + const loggedInUser = JSON.stringify( + JSON.parse(this._storage.getItem('aws-amplify-federatedInfo')).user + ); + if (loggedInUser) { + logger.warn(`There is already a signed in user: ${loggedInUser} in your app. + You should not call Auth.federatedSignIn method again as it may cause unexpected behavior.`); + } + } catch (e) {} + + const { token, identity_id, expires_at } = response; + // Because this.Credentials.set would update the user info with identity id + // So we need to retrieve the user again. + const credentials = await this.Credentials.set( + { provider, token, identity_id, user, expires_at }, + 'federation' + ); + const currentUser = await this.currentAuthenticatedUser(); + dispatchAuthEvent( + 'signIn', + currentUser, + `A user ${currentUser.username} has been signed in` + ); + logger.debug('federated sign in credentials', credentials); + return credentials; + } + } + + /** + * Used to complete the OAuth flow with or without the Cognito Hosted UI + * @param {String} URL - optional parameter for customers to pass in the response URL + */ + private async _handleAuthResponse(URL?: string) { + if (this.oAuthFlowInProgress) { + logger.debug(`Skipping URL ${URL} current flow in progress`); + return; + } + + try { + this.oAuthFlowInProgress = true; + if (!this._config.userPoolId) { + throw new Error( + `OAuth responses require a User Pool defined in config` + ); + } + + dispatchAuthEvent( + 'parsingCallbackUrl', + { url: URL }, + `The callback url is being parsed` + ); + + const currentUrl = + URL || (browserOrNode().isBrowser ? window.location.href : ''); + + const hasCodeOrError = !!(parse(currentUrl).query || '') + .split('&') + .map(entry => entry.split('=')) + .find(([k]) => k === 'code' || k === 'error'); + + const hasTokenOrError = !!(parse(currentUrl).hash || '#') + .substr(1) + .split('&') + .map(entry => entry.split('=')) + .find(([k]) => k === 'access_token' || k === 'error'); + + if (hasCodeOrError || hasTokenOrError) { + this._storage.setItem('amplify-redirected-from-hosted-ui', 'true'); + try { + const { accessToken, idToken, refreshToken, state } = + await this._oAuthHandler.handleAuthResponse(currentUrl); + const session = new CognitoUserSession({ + IdToken: new CognitoIdToken({ IdToken: idToken }), + RefreshToken: new CognitoRefreshToken({ + RefreshToken: refreshToken, + }), + AccessToken: new CognitoAccessToken({ + AccessToken: accessToken, + }), + }); + + let credentials; + // Get AWS Credentials & store if Identity Pool is defined + if (this._config.identityPoolId) { + credentials = await this.Credentials.set(session, 'session'); + logger.debug('AWS credentials', credentials); + } + + /* + Prior to the request we do sign the custom state along with the state we set. This check will verify + if there is a dash indicated when setting custom state from the request. If a dash is contained + then there is custom state present on the state string. + */ + const isCustomStateIncluded = /-/.test(state); + + /* + The following is to create a user for the Cognito Identity SDK to store the tokens + When we remove this SDK later that logic will have to be centralized in our new version + */ + //#region + const currentUser = this.createCognitoUser( + session.getIdToken().decodePayload()['cognito:username'] + ); + + // This calls cacheTokens() in Cognito SDK + currentUser.setSignInUserSession(session); + + if (window && typeof window.history !== 'undefined') { + window.history.replaceState( + {}, + null, + (this._config.oauth as AwsCognitoOAuthOpts).redirectSignIn + ); + } + + dispatchAuthEvent( + 'signIn', + currentUser, + `A user ${currentUser.getUsername()} has been signed in` + ); + dispatchAuthEvent( + 'cognitoHostedUI', + currentUser, + `A user ${currentUser.getUsername()} has been signed in via Cognito Hosted UI` + ); + + if (isCustomStateIncluded) { + const customState = state.split('-').splice(1).join('-'); + + dispatchAuthEvent( + 'customOAuthState', + urlSafeDecode(customState), + `State for user ${currentUser.getUsername()}` + ); + } + //#endregion + + return credentials; + } catch (err) { + logger.debug('Error in cognito hosted auth response', err); + + // Just like a successful handling of `?code`, replace the window history to "dispose" of the `code`. + // Otherwise, reloading the page will throw errors as the `code` has already been spent. + if (window && typeof window.history !== 'undefined') { + window.history.replaceState( + {}, + null, + (this._config.oauth as AwsCognitoOAuthOpts).redirectSignIn + ); + } + + dispatchAuthEvent( + 'signIn_failure', + err, + `The OAuth response flow failed` + ); + dispatchAuthEvent( + 'cognitoHostedUI_failure', + err, + `A failure occurred when returning to the Cognito Hosted UI` + ); + dispatchAuthEvent( + 'customState_failure', + err, + `A failure occurred when returning state` + ); + } + } + } finally { + this.oAuthFlowInProgress = false; + } + } + + /** + * Compact version of credentials + * @param {Object} credentials + * @return {Object} - Credentials + */ + public essentialCredentials(credentials): ICredentials { + return { + accessKeyId: credentials.accessKeyId, + sessionToken: credentials.sessionToken, + secretAccessKey: credentials.secretAccessKey, + identityId: credentials.identityId, + authenticated: credentials.authenticated, + }; + } + + private attributesToObject(attributes) { + const obj = {}; + if (attributes) { + attributes.map(attribute => { + if ( + attribute.Name === 'email_verified' || + attribute.Name === 'phone_number_verified' + ) { + obj[attribute.Name] = + this.isTruthyString(attribute.Value) || attribute.Value === true; + } else { + obj[attribute.Name] = attribute.Value; + } + }); + } + return obj; + } + + private isTruthyString(value: any): boolean { + return ( + typeof value.toLowerCase === 'function' && value.toLowerCase() === 'true' + ); + } + + private createCognitoUser(username: string): CognitoUser { + const userData: ICognitoUserData = { + Username: username, + Pool: this.userPool, + }; + userData.Storage = this._storage; + + const { authenticationFlowType } = this._config; + + const user = new CognitoUser(userData); + if (authenticationFlowType) { + user.setAuthenticationFlowType(authenticationFlowType); + } + return user; + } + + private _isValidAuthStorage(obj) { + // We need to check if the obj has the functions of Storage + return ( + !!obj && + typeof obj.getItem === 'function' && + typeof obj.setItem === 'function' && + typeof obj.removeItem === 'function' && + typeof obj.clear === 'function' + ); + } + + private noUserPoolErrorHandler(config: AuthOptions): AuthErrorTypes { + if (config) { + if (!config.userPoolId || !config.identityPoolId) { + return AuthErrorTypes.MissingAuthConfig; + } + } + return AuthErrorTypes.NoConfig; + } + + private rejectAuthError(type: AuthErrorTypes): Promise { + return Promise.reject(new AuthError(type)); + } + + private rejectNoUserPool(): Promise { + const type = this.noUserPoolErrorHandler(this._config); + return Promise.reject(new NoUserPoolError(type)); + } + + public async rememberDevice( + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + let currUser; + + try { + currUser = await this.currentUserPoolUser(); + } catch (error) { + logger.debug('The user is not authenticated by the error', error); + return Promise.reject('The user is not authenticated'); + } + + currUser.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); + } + }, + }); + }); + } + + public async forgetDevice( + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + let currUser; + + try { + currUser = await this.currentUserPoolUser(); + } catch (error) { + logger.debug('The user is not authenticated by the error', error); + return Promise.reject('The user is not authenticated'); + } + + currUser.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); + } + }, + }); + }); + } + + public async fetchDevices( + customUserAgentDetails?: CustomUserAgentDetails + ): Promise { + let currUser; + + try { + currUser = await this.currentUserPoolUser(); + } catch (error) { + logger.debug('The user is not authenticated by the error', error); + throw new Error('The user is not authenticated'); + } + + currUser.getCachedDeviceKeyAndPassword(); + return new Promise((res, rej) => { + const cb = { + onSuccess(data) { + const deviceList: IAuthDevice[] = data.Devices.map(device => { + const deviceName = + device.DeviceAttributes.find( + ({ Name }) => Name === 'device_name' + ) || {}; + + const deviceInfo: IAuthDevice = { + id: device.DeviceKey, + name: deviceName.Value, + }; + return deviceInfo; + }); + res(deviceList); + }, + onFailure: err => { + if (err.code === 'InvalidParameterException') { + rej(new AuthError(AuthErrorTypes.DeviceConfig)); + } else if (err.code === 'NetworkError') { + rej(new AuthError(AuthErrorTypes.NetworkError)); + } else { + rej(err); + } + }, + }; + currUser.listDevices(MAX_DEVICES, null, cb); + }); + } +} + +export const InternalAuth = new InternalAuthClass(null); +Amplify.register(InternalAuth); diff --git a/packages/auth/src/internals/index.ts b/packages/auth/src/internals/index.ts new file mode 100644 index 00000000000..b153c1d1d92 --- /dev/null +++ b/packages/auth/src/internals/index.ts @@ -0,0 +1,3 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +export { InternalAuth } from './InternalAuth'; diff --git a/packages/auth/tslint.json b/packages/auth/tslint.json index 1bb9e144d24..8eafab1d2b4 100644 --- a/packages/auth/tslint.json +++ b/packages/auth/tslint.json @@ -39,7 +39,12 @@ "allow-snake-case", "allow-leading-underscore" ], - "semicolon": [true, "always", "ignore-interfaces"] + "semicolon": [ + true, + "always", + "ignore-interfaces", + "ignore-bound-class-methods" + ] }, "rulesDirectory": [] } diff --git a/packages/datastore/package.json b/packages/datastore/package.json index 38f08cd9ef8..8bea75a6975 100644 --- a/packages/datastore/package.json +++ b/packages/datastore/package.json @@ -72,7 +72,7 @@ "name": "DataStore (top-level class)", "path": "./lib-esm/index.js", "import": "{ Amplify, DataStore }", - "limit": "137.1 kB" + "limit": "138.27 kB" } ], "jest": { diff --git a/packages/pubsub/package.json b/packages/pubsub/package.json index 112b1312972..27a4add9554 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": "80.2 kB" + "limit": "81.37 kB" }, { "name": "PubSub (Mqtt provider)", "path": "./lib-esm/index.js", "import": "{ Amplify, PubSub, MqttOverWSProvider }", - "limit": "80.1 kB" + "limit": "81.24 kB" } ], "jest": { diff --git a/packages/rtn-push-notification/ios/AmplifyRTNPushNotification.xcodeproj/project.pbxproj b/packages/rtn-push-notification/ios/AmplifyRTNPushNotification.xcodeproj/project.pbxproj new file mode 100644 index 00000000000..b9a51adc9da --- /dev/null +++ b/packages/rtn-push-notification/ios/AmplifyRTNPushNotification.xcodeproj/project.pbxproj @@ -0,0 +1,180 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXFileReference section */ + D31047262A81EA8200CD9A8D /* AmplifyRTNPushNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmplifyRTNPushNotificationManager.swift; sourceTree = ""; }; + D31047272A81EA8200CD9A8D /* AmplifyPushNotificationAppDelegateHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmplifyPushNotificationAppDelegateHelper.swift; sourceTree = ""; }; + D31047282A81EA8200CD9A8D /* AmplifyRTNPushNotification-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AmplifyRTNPushNotification-Bridging-Header.h"; sourceTree = ""; }; + D31047292A81EA8200CD9A8D /* AmplifyRTNPushNotification.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AmplifyRTNPushNotification.m; sourceTree = ""; }; + D310472A2A81EA8200CD9A8D /* AmplifyRTNEventManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmplifyRTNEventManager.swift; sourceTree = ""; }; + D310472B2A81EA8200CD9A8D /* AmplifyPushNotification.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AmplifyPushNotification.h; sourceTree = ""; }; + D310472C2A81EA8200CD9A8D /* AmplifyPushNotification.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AmplifyPushNotification.m; sourceTree = ""; }; + D310472D2A81EA8200CD9A8D /* AmplifyRTNPushNotification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AmplifyRTNPushNotification.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXGroup section */ + 134814211AA4EA7D00B7C361 /* Products */ = { + isa = PBXGroup; + children = ( + ); + name = Products; + sourceTree = ""; + }; + 58B511D21A9E6C8500147676 = { + isa = PBXGroup; + children = ( + D310472B2A81EA8200CD9A8D /* AmplifyPushNotification.h */, + D310472C2A81EA8200CD9A8D /* AmplifyPushNotification.m */, + D31047272A81EA8200CD9A8D /* AmplifyPushNotificationAppDelegateHelper.swift */, + D310472A2A81EA8200CD9A8D /* AmplifyRTNEventManager.swift */, + D31047282A81EA8200CD9A8D /* AmplifyRTNPushNotification-Bridging-Header.h */, + D31047292A81EA8200CD9A8D /* AmplifyRTNPushNotification.m */, + D310472D2A81EA8200CD9A8D /* AmplifyRTNPushNotification.swift */, + D31047262A81EA8200CD9A8D /* AmplifyRTNPushNotificationManager.swift */, + 134814211AA4EA7D00B7C361 /* Products */, + ); + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXProject section */ + 58B511D31A9E6C8500147676 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0920; + ORGANIZATIONNAME = Facebook; + }; + buildConfigurationList = 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "AmplifyRTNPushNotification" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + English, + en, + ); + mainGroup = 58B511D21A9E6C8500147676; + productRefGroup = 58B511D21A9E6C8500147676; + projectDirPath = ""; + projectRoot = ""; + targets = ( + ); + }; +/* End PBXProject section */ + +/* Begin XCBuildConfiguration section */ + 58B511ED1A9E6C8500147676 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + "EXCLUDED_ARCHS[sdk=*]" = arm64; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 58B511EE1A9E6C8500147676 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + "EXCLUDED_ARCHS[sdk=*]" = arm64; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 58B511D61A9E6C8500147676 /* Build configuration list for PBXProject "AmplifyRTNPushNotification" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 58B511ED1A9E6C8500147676 /* Debug */, + 58B511EE1A9E6C8500147676 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 58B511D31A9E6C8500147676 /* Project object */; +} diff --git a/packages/rtn-push-notification/ios/AmplifyRTNPushNotification.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/rtn-push-notification/ios/AmplifyRTNPushNotification.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000000..94b2795e225 --- /dev/null +++ b/packages/rtn-push-notification/ios/AmplifyRTNPushNotification.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,4 @@ + + + diff --git a/packages/rtn-push-notification/ios/AmplifyRTNPushNotification.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/rtn-push-notification/ios/AmplifyRTNPushNotification.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000000..18d981003d6 --- /dev/null +++ b/packages/rtn-push-notification/ios/AmplifyRTNPushNotification.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/yarn.lock b/yarn.lock index cc71f3aa8d5..0f1d70bc807 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4348,9 +4348,9 @@ webpack "^5.88.0" "@smithy/types@^2.0.2": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@smithy/types/-/types-2.0.2.tgz#49d42724c909e845bfd80a2e195740614ce497f3" - integrity sha512-wcymEjIXQ9+NEfE5Yt5TInAqe1o4n+Nh+rh00AwoazppmUt8tdo6URhc5gkDcOYrcvlDVAZE7uG69nDpEGUKxw== + version "2.1.0" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-2.1.0.tgz#67fd47c25bbb0fd818951891bf7bcf19a8ee2fe6" + integrity sha512-KLsCsqxX0j2l99iP8s0f7LBlcsp7a7ceXGn0LPYPyVOsqmIKvSaPQajq0YevlL4T9Bm+DtcyXfBTbtBcLX1I7A== dependencies: tslib "^2.5.0" @@ -4767,18 +4767,18 @@ "@types/react" "^17" "@types/react@*": - version "18.2.18" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.18.tgz#c8b233919eef1bdc294f6f34b37f9727ad677516" - integrity sha512-da4NTSeBv/P34xoZPhtcLkmZuJ+oYaCxHmyHzwaDQo9RQPBeXV+06gEk2FpqEcsX9XrnNLvRpVh6bdavDSjtiQ== + version "18.2.19" + resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.19.tgz#f77cb2c8307368e624d464a25b9675fa35f95a8b" + integrity sha512-e2S8wmY1ePfM517PqCG80CcE48Xs5k0pwJzuDZsfE8IZRRBfOMCF+XqnFxu6mWtyivum1MQm4aco+WIt6Coimw== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" csstype "^3.0.2" "@types/react@^17": - version "17.0.62" - resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.62.tgz#2efe8ddf8533500ec44b1334dd1a97caa2f860e3" - integrity sha512-eANCyz9DG8p/Vdhr0ZKST8JV12PhH2ACCDYlFw6DIO+D+ca+uP4jtEDEpVqXZrh/uZdXQGwk7whJa3ah5DtyLw== + version "17.0.63" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.63.tgz#78ca41a34b1e4fd5ba9398d33bc78a81bfd5c180" + integrity sha512-T+aaG8RlIkgJ4VzWLJYbMW9QX7sIAV8CcuyV6FU6Hm7yu3Bee1YBZQRu2vYEm/dU8kre+/mzl2aGYh5MFgVLaQ== dependencies: "@types/prop-types" "*" "@types/scheduler" "*" @@ -5323,13 +5323,6 @@ acorn-import-assertions@^1.9.0: resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" - integrity sha512-AU7pnZkguthwBjKgCg6998ByQNIMjbuDQZ8bb78QAFZwPfmKia8AIzgY/gWgqCjnht8JLdXmB4YxA0KaV60ncQ== - dependencies: - acorn "^3.0.4" - acorn-jsx@^5.3.1: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -5345,12 +5338,7 @@ acorn-walk@^8.0.0: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== -acorn@^3.0.4: - version "3.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" - integrity sha512-OLUyIIZ7mF5oaAUT1w0TFqQS81q3saT46x8t7ukpPjMNk+nbs4ZHhs7ToV8EWnLYLepjETXd4XaCE4uxkMeqUw== - -acorn@^5.5.0, acorn@^5.5.3: +acorn@^5.5.3: version "5.7.4" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.4.tgz#3e8d8a9947d0599a1796d10225d7432f4a4acf5e" integrity sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg== @@ -5402,24 +5390,11 @@ ajv-errors@^1.0.0: resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== -ajv-keywords@^1.0.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" - integrity sha512-vuBv+fm2s6cqUyey2A7qYcvsik+GMDJsw8BARP2sDE76cqmaZVarsvHf7Vx6VJ0Xk8gLl+u3MoAPf6gKzJefeA== - ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@^4.7.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" - integrity sha512-I/bSHSNEcFFqXLf91nchoNB9D1Kie3QKcWdchYUaoIg1+1bdWDkdfdlvdIOJbi9U8xR0y+MWc5D+won9v95WlQ== - dependencies: - co "^4.6.0" - json-stable-stringify "^1.0.1" - ajv@^6.1.0, ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -5672,11 +5647,6 @@ array-equal@^1.0.0: resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" integrity sha512-H3LU5RLiSsGXPhN+Nipar0iR0IofH+8r89G2y1tBKxQ/agagKyAjhkAFDRBfodP2caPrNKHpAWNIM/c9yeL7uA== -array-find@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-find/-/array-find-1.0.0.tgz#6c8e286d11ed768327f8e62ecee87353ca3e78b8" - integrity sha512-kO/vVCacW9mnpn3WPWbTVlEnOabK2L7LWi2HViURtCM46y1zb6I8UMjx4LgbiqadTgHnLInUronwn3ampNTJtQ== - array-ify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" @@ -5937,7 +5907,7 @@ axobject-query@^3.1.1: dependencies: dequal "^2.0.3" -babel-code-frame@^6.16.0, babel-code-frame@^6.26.0: +babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" integrity sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g== @@ -7015,13 +6985,6 @@ browserify-sign@^4.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" -browserify-zlib@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.1.4.tgz#bb35f8a519f600e0fa6b8485241c979d0141fb2d" - integrity sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ== - dependencies: - pako "~0.2.0" - browserify-zlib@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" @@ -7254,13 +7217,6 @@ caller-callsite@^2.0.0: dependencies: callsites "^2.0.0" -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" - integrity sha512-UJiE1otjXPF5/x+T3zTnSFiTOEmJoGTD9HmBoxnCUwho61a2eSNn/VwtwuIBDAo2SEOv1AJ7ARI5gCmohFLu/g== - dependencies: - callsites "^0.2.0" - caller-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" @@ -7268,11 +7224,6 @@ caller-path@^2.0.0: dependencies: caller-callsite "^2.0.0" -callsites@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" - integrity sha512-Zv4Dns9IbXXmPkgRRUjAaJQgfN4xX5p6+RQFhWUqscdvvK2xK/ZL8b3IXIJsj+4sD+f24NwnWy2BY8AJ82JB0A== - callsites@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" @@ -7337,7 +7288,7 @@ chalk@4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== @@ -7458,11 +7409,6 @@ cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: inherits "^2.0.1" safe-buffer "^5.0.1" -circular-json@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" - integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== - class-utils@^0.3.5: version "0.3.6" resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" @@ -7809,7 +7755,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -concat-stream@^1.4.7, concat-stream@^1.5.0, concat-stream@^1.5.2: +concat-stream@^1.4.7, concat-stream@^1.5.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -8163,14 +8109,6 @@ cyclist@^1.0.1: resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.2.tgz#673b5f233bf34d8e602b949429f8171d9121bea3" integrity sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA== -d@1, d@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== - dependencies: - es5-ext "^0.10.50" - type "^1.0.1" - damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" @@ -8207,7 +8145,7 @@ dayjs@^1.8.15: resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.9.tgz#9ca491933fadd0a60a2c19f6c237c03517d71d1a" integrity sha512-QvzAURSbQ0pKdIye2txOzNaHmxtUBXerpY0FJsFXUMKbIZeFm5ht1LS/jFsrncjnmtv8HsG0W2g6c0zUjZWmpA== -debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: +debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -8440,7 +8378,7 @@ doctrine@0.7.2: esutils "^1.1.6" isarray "0.0.1" -doctrine@^2.0.0, doctrine@^2.1.0: +doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== @@ -8536,9 +8474,9 @@ ejs@^3.1.7: jake "^10.8.5" electron-to-chromium@^1.4.477: - version "1.4.486" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.486.tgz#11bc573ecf245c183f55cfe32ba209a22a96237a" - integrity sha512-9zn9/2lbMGY/mFhoUymD9ODYdLY3zjUW/IW9ihU/sJVeIlD70m2aAb86S35aRGF+iwqLuQP25epruayZjKNjBw== + version "1.4.487" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.487.tgz#e2ef8b15f2791bf68fa6f38f2656f1a551d360ae" + integrity sha512-XbCRs/34l31np/p33m+5tdBrdXu9jJkZxSbNxj5I0H1KtV2ZMSB+i/HYqDiRzHaFx2T5EdytjoBRe8QRJE2vQg== elliptic@^6.5.3: version "6.5.4" @@ -8614,15 +8552,6 @@ enhanced-resolve@^5.0.0, enhanced-resolve@^5.15.0: graceful-fs "^4.2.4" tapable "^2.2.0" -enhanced-resolve@~0.9.0: - version "0.9.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz#4d6e689b3725f86090927ccc86cd9f1635b89e2e" - integrity sha512-kxpoMgrdtkXZ5h0SeraBS1iRntpTpQ3R8ussdb38+UAFnMGX5DDyJXePm+OCHOcoXvHDw7mc2erbJBpDnl7TPw== - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.2.0" - tapable "^0.1.8" - enquirer@^2.3.5: version "2.4.1" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.4.1.tgz#93334b3fbd74fc7097b224ab4a8fb7e40bf4ae56" @@ -8767,66 +8696,6 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.62, es5-ext@~0.10.14: - version "0.10.62" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" - integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== - dependencies: - es6-iterator "^2.0.3" - es6-symbol "^3.1.3" - next-tick "^1.1.0" - -es6-iterator@^2.0.3, es6-iterator@~2.0.1, es6-iterator@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-map@^0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" - integrity sha512-mz3UqCh0uPCIqsw1SSAkB/p0rOzF/M0V++vyN7JqlPtSW/VsYgQBvVvqMLmfBuyMzTpLnNqi6JmcSizs4jy19A== - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-set "~0.1.5" - es6-symbol "~3.1.1" - event-emitter "~0.3.5" - -es6-set@~0.1.5: - version "0.1.6" - resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.6.tgz#5669e3b2aa01d61a50ba79964f733673574983b8" - integrity sha512-TE3LgGLDIBX332jq3ypv6bcOpkLO0AslAQo7p2VqX/1N46YNsvIWgvjojjSEnWEGWMhr1qUbYeTSir5J6mFHOw== - dependencies: - d "^1.0.1" - es5-ext "^0.10.62" - es6-iterator "~2.0.3" - es6-symbol "^3.1.3" - event-emitter "^0.3.5" - type "^2.7.2" - -es6-symbol@^3.1.1, es6-symbol@^3.1.3, es6-symbol@~3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== - dependencies: - d "^1.0.1" - ext "^1.1.2" - -es6-weak-map@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" - integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== - dependencies: - d "1" - es5-ext "^0.10.46" - es6-iterator "^2.0.3" - es6-symbol "^3.1.1" - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -8864,16 +8733,6 @@ escodegen@^1.9.1: optionalDependencies: source-map "~0.6.1" -escope@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" - integrity sha512-75IUQsusDdalQEW/G/2esa87J7raqdJF+Ca0/Xm5C3Q58Nr4yVYjZGp/P1+2xiEVgXRrA39dpRb8LcshajbqDQ== - dependencies: - es6-map "^0.1.3" - es6-weak-map "^2.0.1" - esrecurse "^4.1.0" - estraverse "^4.1.1" - eslint-config-airbnb-base@^14.2.1: version "14.2.1" resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.1.tgz#8a2eb38455dc5a312550193b319cdaeef042cd1e" @@ -8883,11 +8742,6 @@ eslint-config-airbnb-base@^14.2.1: object.assign "^4.1.2" object.entries "^1.1.2" -eslint-config-airbnb-base@^5.0.2: - version "5.0.3" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-5.0.3.tgz#9714ac35ec2cd7fab0d44d148a9f91db2944074d" - integrity sha512-MWuWm4ethl+gy6JwrjVAPmzMHo84cvB2wr9+MczyJ+eWZ/fS1y8rVDRJInXgM+A39hm8QLBY0QAW6ydsRHpdeA== - eslint-config-airbnb-typescript@^14.0.0: version "14.0.2" resolved "https://registry.yarnpkg.com/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-14.0.2.tgz#4dc1583b9eab671bb011dea7d4ff1fc0d88e6e09" @@ -8904,44 +8758,20 @@ eslint-config-airbnb@^18.2.1: object.assign "^4.1.2" object.entries "^1.1.2" -eslint-config-prettier@^6.3.0: - version "6.15.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.15.0.tgz#7f93f6cb7d45a92f1537a70ecc06366e1ac6fed9" - integrity sha512-a1+kOYLR8wMGustcgAjdydMsQ2A/2ipRPwRKUmfYaSxc9ZPcrku080Ctl6zrZzZNs/U82MjSv+qKREkoq3bJaw== - dependencies: - get-stdin "^6.0.0" - eslint-config-prettier@^8.3.0: version "8.10.0" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz#3a06a662130807e2502fc3ff8b4143d8a0658e11" integrity sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg== eslint-import-resolver-node@^0.3.7: - version "0.3.8" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.8.tgz#be719e72f5e96dcef7a60f74147c842db0c74b06" - integrity sha512-tEe+Pok22qIGaK3KoMP+N96GVDS66B/zreoVVmiavLvRUEmGRtvb4B8wO9jwnb8d2lvHtrkhZ7UD73dWBVnf/Q== + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== dependencies: debug "^3.2.7" is-core-module "^2.13.0" resolve "^1.22.4" -eslint-import-resolver-webpack@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-webpack/-/eslint-import-resolver-webpack-0.5.1.tgz#c186d7b42faa9d3352455e841a5748f1909095d0" - integrity sha512-faDyBxVfTx2dcZx1fRQHabe+VtjyYpBGma/3uHvmu0WyN7QM366zMBtAXIPa5pLKpQolkrDhDANAfaJvmtMVWQ== - dependencies: - array-find "^1.0.0" - debug "^2.2.0" - enhanced-resolve "~0.9.0" - find-root "^0.1.1" - interpret "^1.0.0" - is-absolute "^0.2.3" - lodash.get "^3.7.0" - node-libs-browser "^1.0.0" - object-assign "^4.1.0" - resolve "^1.1.7" - semver "^5.3.0" - eslint-module-utils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" @@ -8949,7 +8779,7 @@ eslint-module-utils@^2.8.0: dependencies: debug "^3.2.7" -eslint-plugin-import@^2.24.2, eslint-plugin-import@^2.7.0: +eslint-plugin-import@^2.24.2: version "2.28.0" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.28.0.tgz#8d66d6925117b06c4018d491ae84469eb3cb1005" integrity sha512-B8s/n+ZluN7sxj9eUf7/pRFERX0r5bnFA2dCaLHy2ZeaQEAz0k+ZZkFWRFHJAqxfxQDx6KLv9LeIki7cFdwW+Q== @@ -9002,16 +8832,6 @@ eslint-plugin-jsx-a11y@^6.4.1: object.fromentries "^2.0.6" semver "^6.3.0" -eslint-plugin-node@^5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-5.2.1.tgz#80df3253c4d7901045ec87fa660a284e32bdca29" - integrity sha512-xhPXrh0Vl/b7870uEbaumb2Q+LxaEcOQ3kS1jtIXanBAwpMre1l5q/l2l/hESYJGEFKuI78bp6Uw50hlpr7B+g== - dependencies: - ignore "^3.3.6" - minimatch "^3.0.4" - resolve "^1.3.3" - semver "5.3.0" - eslint-plugin-prettier@^4.0.0: version "4.2.1" resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" @@ -9019,11 +8839,6 @@ eslint-plugin-prettier@^4.0.0: dependencies: prettier-linter-helpers "^1.0.0" -eslint-plugin-promise@^3.6.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.8.0.tgz#65ebf27a845e3c1e9d6f6a5622ddd3801694b621" - integrity sha512-JiFL9UFR15NKpHyGii1ZcvmtIqa3UTwiDAGb8atSffe43qJ3+1czVGN6UtkklpcJ2DVnqvTMzEKRaJdBkAL2aQ== - eslint-plugin-react-hooks@^4.2.0: version "4.6.0" resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" @@ -9050,11 +8865,6 @@ eslint-plugin-react@^7.26.0: semver "^6.3.1" string.prototype.matchall "^4.0.8" -eslint-plugin-standard@^3.0.1: - version "3.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-3.1.0.tgz#2a9e21259ba4c47c02d53b2d0c9135d4b1022d47" - integrity sha512-fVcdyuKRr0EZ4fjWl3c+gp1BANFJD1+RaWa2UPYfMZ6jCtp5RG00kSaXnK/dE5sYzt4kaWJ9qdxqUfc0d9kX0w== - eslint-scope@5.1.1, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -9095,47 +8905,6 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint@^3.19.0: - version "3.19.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" - integrity sha512-x6LJGXWCGB/4YOBhL48yeppZTo+YQUNC37N5qqCpC1b1kkNzydlQHQAtPuUSFoZSxgIadrysQoW2Hq602P+uEA== - dependencies: - babel-code-frame "^6.16.0" - chalk "^1.1.3" - concat-stream "^1.5.2" - debug "^2.1.1" - doctrine "^2.0.0" - escope "^3.6.0" - espree "^3.4.0" - esquery "^1.0.0" - estraverse "^4.2.0" - esutils "^2.0.2" - file-entry-cache "^2.0.0" - glob "^7.0.3" - globals "^9.14.0" - ignore "^3.2.0" - imurmurhash "^0.1.4" - inquirer "^0.12.0" - is-my-json-valid "^2.10.0" - is-resolvable "^1.0.0" - js-yaml "^3.5.1" - json-stable-stringify "^1.0.0" - levn "^0.3.0" - lodash "^4.0.0" - mkdirp "^0.5.0" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.1" - pluralize "^1.2.1" - progress "^1.1.8" - require-uncached "^1.0.2" - shelljs "^0.7.5" - strip-bom "^3.0.0" - strip-json-comments "~2.0.1" - table "^3.7.8" - text-table "~0.2.0" - user-home "^2.0.0" - eslint@^7.32.0: version "7.32.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.32.0.tgz#c6d328a14be3fb08c8d1d21e12c02fdb7a2a812d" @@ -9182,14 +8951,6 @@ eslint@^7.32.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^3.4.0: - version "3.5.4" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" - integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A== - dependencies: - acorn "^5.5.0" - acorn-jsx "^3.0.0" - espree@^7.3.0, espree@^7.3.1: version "7.3.1" resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" @@ -9204,7 +8965,7 @@ esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.0.0, esquery@^1.4.0: +esquery@^1.4.0: version "1.5.0" resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b" integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== @@ -9248,14 +9009,6 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== -event-emitter@^0.3.5, event-emitter@~0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA== - dependencies: - d "1" - es5-ext "~0.10.14" - event-target-shim@^5.0.0, event-target-shim@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" @@ -9266,11 +9019,6 @@ eventemitter3@^4.0.4: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== -events@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" - integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw== - events@^3.0.0, events@^3.1.0, events@^3.2.0, events@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -9419,13 +9167,6 @@ exponential-backoff@^3.1.1: resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== -ext@^1.1.2: - version "1.7.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" - integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== - dependencies: - type "^2.7.2" - extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -9627,14 +9368,6 @@ figures@^1.3.5: escape-string-regexp "^1.0.5" object-assign "^4.1.0" -file-entry-cache@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" - integrity sha512-uXP/zGzxxFvFfcZGgBIwotm+Tdc55ddPAzF7iHshP4YGaXMww7rSF9peD9D1sui5ebONg5UobsZv+FfgEpGv/w== - dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" - file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -9744,11 +9477,6 @@ find-package@^1.0.0: dependencies: parents "^1.0.1" -find-root@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/find-root/-/find-root-0.1.2.tgz#98d2267cff1916ccaf2743b3a0eea81d79d7dcd1" - integrity sha512-GyDxVgA61TZcrgDJPqOqGBpi80Uf2yIstubgizi7AjC9yPdRrqBR+Y0MvK4kXnYlaoz3d+SGxDHMYVkwI/yd2w== - find-up@5.0.0, find-up@~5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" @@ -9794,16 +9522,6 @@ find@^0.2.7: dependencies: traverse-chain "~0.1.0" -flat-cache@^1.2.1: - version "1.3.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.4.tgz#2c2ef77525cc2929007dfffa1dd314aa9c9dee6f" - integrity sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg== - dependencies: - circular-json "^0.3.1" - graceful-fs "^4.1.2" - rimraf "~2.6.2" - write "^0.2.1" - flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -10097,20 +9815,6 @@ gauge@^5.0.0: strip-ansi "^6.0.1" wide-align "^1.1.5" -generate-function@^2.0.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.3.1.tgz#f069617690c10c868e73b8465746764f97c3479f" - integrity sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ== - dependencies: - is-property "^1.0.2" - -generate-object-property@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" - integrity sha512-TuOwZWgJ2VAMEGJvAyPWvpqxSANF0LDpmyHauMjFYzaACvn+QTT/AZomvPCzVBV7yDN3OmwHQ5OvHaeLKre3JQ== - dependencies: - is-property "^1.0.0" - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -10165,11 +9869,6 @@ get-port@5.1.1: resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== -get-stdin@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" - integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== - get-stdin@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-7.0.0.tgz#8d5de98f15171a125c5e516643c7a6d0ea8a96f6" @@ -10353,7 +10052,7 @@ glob@^10.2.2: minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-scurry "^1.10.1" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.2.0: +glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.2.0: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -10398,7 +10097,7 @@ globals@^13.6.0, globals@^13.9.0: dependencies: type-fest "^0.20.2" -globals@^9.14.0, globals@^9.18.0: +globals@^9.18.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== @@ -10737,11 +10436,6 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -https-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82" - integrity sha512-EjDQFbgJr1vDD/175UJeSX3ncQ3+RUnCL5NkthQGHvF4VNHlzTy8ifJfTqz47qiPRqaFH58+CbuG3x51WuB1XQ== - https-browserify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" @@ -10834,7 +10528,7 @@ ignore-walk@^6.0.0: dependencies: minimatch "^9.0.0" -ignore@^3.2.0, ignore@^3.3.6, ignore@^3.3.7: +ignore@^3.3.7: version "3.3.10" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== @@ -10906,11 +10600,6 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - integrity sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg== - infer-owner@^1.0.3, infer-owner@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" @@ -10978,25 +10667,6 @@ inquirer@8.2.4: through "^2.3.6" wrap-ansi "^7.0.0" -inquirer@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e" - integrity sha512-bOetEz5+/WpgaW4D1NYOk1aD+JCqRjqu/FwRFgnIfiP7FC/zinsrfyO1vlS3nyH/R7S0IH3BIHBu4DBIDSqiGQ== - dependencies: - ansi-escapes "^1.1.0" - ansi-regex "^2.0.0" - chalk "^1.0.0" - cli-cursor "^1.0.1" - cli-width "^2.0.0" - figures "^1.3.5" - lodash "^4.3.0" - readline2 "^1.0.1" - run-async "^0.1.0" - rx-lite "^3.1.2" - string-width "^1.0.1" - strip-ansi "^3.0.0" - through "^2.3.6" - inquirer@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-1.2.3.tgz#4dec6f32f37ef7bb0b2ed3f1d1a5c3f545074918" @@ -11089,14 +10759,6 @@ ip@^2.0.0: resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== -is-absolute@^0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-0.2.6.tgz#20de69f3db942ef2d87b9c2da36f172235b1b5eb" - integrity sha512-7Kr05z5LkcOpoMvxHN1PC11WbPabdNFmMYYo0eZvWu3BfVS0T03yoqYDczoCBx17xqk2x1XAZrcKiFVL88jxlQ== - dependencies: - is-relative "^0.2.1" - is-windows "^0.2.0" - is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -11332,22 +10994,6 @@ is-module@^1.0.0: resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g== -is-my-ip-valid@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.1.tgz#f7220d1146257c98672e6fba097a9f3f2d348442" - integrity sha512-jxc8cBcOWbNK2i2aTkCZP6i7wkHF1bqKFrwEHuN5Jtg5BSaZHUZQ/JTOJwoV41YvHnOaRyWWh72T/KvfNz9DJg== - -is-my-json-valid@^2.10.0: - version "2.20.6" - resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.20.6.tgz#a9d89e56a36493c77bda1440d69ae0dc46a08387" - integrity sha512-1JQwulVNjx8UqkPE/bqDaxtH4PXCe/2VRh/y3p99heOV87HG4Id5/VfDswd+YiAfHcRTfDlWgISycnHuhZq1aw== - dependencies: - generate-function "^2.0.0" - generate-object-property "^1.1.0" - is-my-ip-valid "^1.0.0" - jsonpointer "^5.0.0" - xtend "^4.0.0" - is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -11431,11 +11077,6 @@ is-primitive@^2.0.0: resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" integrity sha512-N3w1tFaRfk3UrPfqeRyD+GYDASU3W5VinKhlORy8EWVf/sIdDL9GAcew85XmktCfH+ngG7SRXEVDoO18WMdB/Q== -is-property@^1.0.0, is-property@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" - integrity sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g== - is-regex@^1.0.4, is-regex@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" @@ -11444,18 +11085,6 @@ is-regex@^1.0.4, is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-relative@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-0.2.1.tgz#d27f4c7d516d175fb610db84bbeef23c3bc97aa5" - integrity sha512-9AMzjRmLqcue629b4ezEVSK6kJsYJlUIhMcygmYORUgwUNJiavHcC3HkaGx0XYpyVKQSOqFbMEZmW42cY87sYw== - dependencies: - is-unc-path "^0.1.1" - -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" @@ -11523,13 +11152,6 @@ is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== -is-unc-path@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-0.1.2.tgz#6ab053a72573c10250ff416a3814c35178af39b9" - integrity sha512-HhLc5VDMH4pu3oMtIuunz/DFQUIoR561kMME3U3Afhj8b7vH085vkIkemrz1kLXCEIuoMAmO3yVmafWdSbGW8w== - dependencies: - unc-path-regex "^0.1.0" - is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" @@ -11547,11 +11169,6 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" -is-windows@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" - integrity sha512-n67eJYmXbniZB7RF4I/FTjK1s6RPOCTxhYrVYLRaCt3lF0mpWZPKr3T2LSZAqyjQsxR2qMmGYXXzK0YWwcPM1Q== - is-windows@^1.0.0, is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" @@ -12189,7 +11806,7 @@ js-tokens@^3.0.2: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" integrity sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg== -js-yaml@3.14.1, js-yaml@^3.10.0, js-yaml@^3.13.1, js-yaml@^3.5.1: +js-yaml@3.14.1, js-yaml@^3.10.0, js-yaml@^3.13.1: version "3.14.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== @@ -12321,13 +11938,6 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz#e06f23128e0bbe342dc996ed5a19e28b57b580e0" - integrity sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g== - dependencies: - jsonify "^0.0.1" - json-stringify-nice@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67" @@ -12378,21 +11988,11 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -jsonify@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" - integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== - jsonparse@^1.2.0, jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== -jsonpointer@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" - integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== - jsprim@^1.2.2: version "1.4.2" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" @@ -12580,14 +12180,6 @@ leven@^3.1.0: resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -12596,6 +12188,14 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + libnpmaccess@^6.0.3: version "6.0.4" resolved "https://registry.yarnpkg.com/libnpmaccess/-/libnpmaccess-6.0.4.tgz#2dd158bd8a071817e2207d3b201d37cf1ad6ae6b" @@ -12743,18 +12343,6 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash._baseget@^3.0.0: - version "3.7.2" - resolved "https://registry.yarnpkg.com/lodash._baseget/-/lodash._baseget-3.7.2.tgz#1b6ae1d5facf3c25532350a13c1197cb8bb674f4" - integrity sha512-OgVr3AufoSqlh6eHWOLdLzRdn6TU3LlEGQjtsyLr9XRtPoXUywzquM1uYE+qQAno1kB9CvpI1gsvGwiVwPinog== - -lodash._topath@^3.0.0: - version "3.8.1" - resolved "https://registry.yarnpkg.com/lodash._topath/-/lodash._topath-3.8.1.tgz#3ec5e2606014f4cb97f755fe6914edd8bfc00eac" - integrity sha512-QsF5c8A+Biv0oxuSCd05JqhXHPCjvFT0nMXVevfMgU1pp5iEHVSin2cKXi3lQe5+px285p7kAHVtOnbNE79syw== - dependencies: - lodash.isarray "^3.0.0" - lodash.assign@^4.0.3, lodash.assign@^4.0.6: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" @@ -12765,19 +12353,6 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== -lodash.get@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-3.7.0.tgz#3ce68ae2c91683b281cc5394128303cbf75e691f" - integrity sha512-7iD0aRHu/B8gcCDNx53lJi33R4TzpbOB3Mfk4XpIN7WFUt+W5rI+6CtHhpJ52B6zhhRvogtuNSDFZc3xgcbClQ== - dependencies: - lodash._baseget "^3.0.0" - lodash._topath "^3.0.0" - -lodash.isarray@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55" - integrity sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ== - lodash.ismatch@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz#756cb5150ca3ba6f11085a78849645f188f85f37" @@ -12808,7 +12383,7 @@ lodash.truncate@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== -lodash@^4.0.0, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.7.0: +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4, lodash@^4.3.0, lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -13049,11 +12624,6 @@ md5@^2.3.0: crypt "0.0.2" is-buffer "~1.1.6" -memory-fs@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.2.0.tgz#f2bb25368bc121e391c2520de92969caee0a0290" - integrity sha512-+y4mDxU4rvXXu5UDSGCGNiesFmwCHuefGMoPCO1WYucNYj7DsLqrFaa2fXVI0H+NNiPTwwzKwspn9yTZqUGqng== - memory-fs@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" @@ -13690,7 +13260,7 @@ mkdirp-infer-owner@^2.0.0: infer-owner "^1.0.4" mkdirp "^1.0.3" -mkdirp@0.x, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3: +mkdirp@0.x, mkdirp@^0.5.1, mkdirp@^0.5.3: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== @@ -13765,11 +13335,6 @@ multimatch@^3.0.0: arrify "^1.0.1" minimatch "^3.0.4" -mute-stream@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" - integrity sha512-EbrziT4s8cWPmzr47eYVW3wimS4HsvlnV5ri1xw1aR6JQo/OrJX5rkl32K/QQHdxeabJETtfeaROGhd8W7uBgg== - mute-stream@0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db" @@ -13834,11 +13399,6 @@ neo-async@^2.5.0, neo-async@^2.6.1, neo-async@^2.6.2: resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== -next-tick@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" - integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== - nice-try@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" @@ -13923,35 +13483,6 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-libs-browser@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-1.1.1.tgz#2a38243abedd7dffcd07a97c9aca5668975a6fea" - integrity sha512-wdhhJdVlCB6oj9hy1xmegD7UFfE/nwOdDDmxtZucxhT4sOHJ88MN6NwVQPZiRaNSs/BEol3w3XezKetZsHNnvg== - dependencies: - assert "^1.1.1" - browserify-zlib "^0.1.4" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^1.0.0" - https-browserify "0.0.1" - os-browserify "^0.2.0" - path-browserify "0.0.0" - process "^0.11.0" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.0.5" - stream-browserify "^2.0.1" - stream-http "^2.3.1" - string_decoder "^0.10.25" - timers-browserify "^1.4.2" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.10.3" - vm-browserify "0.0.4" - node-libs-browser@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" @@ -14331,7 +13862,7 @@ ob1@0.67.0: resolved "https://registry.yarnpkg.com/ob1/-/ob1-0.67.0.tgz#91f104c90641b1af8c364fc82a4b2c7d0801072d" integrity sha512-YvZtX8HKYackQ5PwdFIuuNFVsMChRPHvnARRRT0Vk59xsBvL5t9U1Ock3M1sYrKj+Gp73+0q9xcHLAxI+xLi5g== -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: +object-assign@^4.1.0, object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== @@ -14529,7 +14060,7 @@ opener@^1.5.2: resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598" integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== -optionator@^0.8.1, optionator@^0.8.2: +optionator@^0.8.1: version "0.8.3" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== @@ -14580,21 +14111,11 @@ ora@^5.4.1: strip-ansi "^6.0.0" wcwidth "^1.0.1" -os-browserify@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.2.1.tgz#63fc4ccee5d2d7763d26bbf8601078e6c2e0044f" - integrity sha512-vHbnbzdqWJWvGOm7aOMDXHVUykPG0GdhfLkn5ZDmvbRI+wPj/XoB0/CRAkP9v28eZ7REIPPHJa+8ZEYixsWKmQ== - os-browserify@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== - os-locale@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" @@ -14790,11 +14311,6 @@ pako@^2.0.4: resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== -pako@~0.2.0: - version "0.2.9" - resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" - integrity sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA== - pako@~1.0.5: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" @@ -14907,11 +14423,6 @@ pascalcase@^0.1.1: resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== -path-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" - integrity sha512-WA3pxi1olUQcsl82W576vkqhUSGp0uBtr/381pxx5WXLp3NC+AB99hUG3aGW7H0Kg9PFr1D8wv1iJeICe+9Mhw== - path-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" @@ -14944,11 +14455,6 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-is-inside@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== - path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" @@ -15098,11 +14604,6 @@ plist@^3.0.2, plist@^3.0.5: base64-js "^1.5.1" xmlbuilder "^15.1.1" -pluralize@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" - integrity sha512-TH+BeeL6Ct98C7as35JbZLf8lgsRzlNJb5gklRIGHKaPkGl1esOKBc5ALUMd+q08Sr6tiEKM+Icbsxg5vuhMKQ== - pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" @@ -15224,16 +14725,11 @@ process-nextick-args@~2.0.0: resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -process@^0.11.0, process@^0.11.10, process@~0.11.0: +process@^0.11.10: version "0.11.10" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== -progress@^1.1.8: - version "1.1.8" - resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" - integrity sha512-UdA8mJ4weIkUBO224tIarHzuHs4HuYiJvsuGT7j/SPQiUJVjYvNDBIPa0hAorduOfjGohB/qHWRa/lrrWX/mXw== - progress@^2.0.0, progress@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" @@ -15723,7 +15219,7 @@ read@1, read@^1.0.7: dependencies: mute-stream "~0.0.4" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== @@ -15772,15 +15268,6 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" -readline2@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/readline2/-/readline2-1.0.1.tgz#41059608ffc154757b715d9989d199ffbf372e35" - integrity sha512-8/td4MmwUB6PkZUbV25uKz7dfrmjYWxsW8DVfibWdlHRk/l/DfHKn4pU+dfcoGLFgWOdyGCzINRQD7jn+Bv+/g== - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - mute-stream "0.0.5" - readline@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/readline/-/readline-1.3.0.tgz#c580d77ef2cfc8752b132498060dc9793a7ac01c" @@ -16036,14 +15523,6 @@ require-main-filename@^2.0.0: resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== -require-uncached@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" - integrity sha512-Xct+41K3twrbBHdxAgMoOS+cNcoqIjfM2/VxBF4LL2hVph7YsF8VSKyQ3BDFZwEVbok9yeDl2le/qo0S77WG2w== - dependencies: - caller-path "^0.1.0" - resolve-from "^1.0.0" - resolve-cwd@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" @@ -16063,11 +15542,6 @@ resolve-from@5.0.0, resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-from@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" - integrity sha512-kT10v4dhrlLNcnO084hEjvXCI1wUG9qZLoz2RogxqDQQYy7IxjI/iMUkOtQTNEh6rzHxvdQWHsJyel1pKOVCxg== - resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" @@ -16088,7 +15562,7 @@ resolve@1.1.7: resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" integrity sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg== -resolve@1.x, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.3, resolve@^1.22.4, resolve@^1.3.2, resolve@^1.3.3, resolve@^1.9.0: +resolve@1.x, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.3, resolve@^1.22.4, resolve@^1.3.2, resolve@^1.9.0: version "1.22.4" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== @@ -16249,13 +15723,6 @@ rsvp@^4.8.4: resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== -run-async@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-0.1.0.tgz#c8ad4a5e110661e402a7d21b530e009f25f8e389" - integrity sha512-qOX+w+IxFgpUpJfkv2oGN0+ExPs68F4sZHfaRRx4dDexAQkG83atugKVEylyT5ARees3HBbfmuvnjbrd8j9Wjw== - dependencies: - once "^1.3.0" - run-async@^2.2.0, run-async@^2.4.0: version "2.4.1" resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" @@ -16280,11 +15747,6 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rx-lite@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" - integrity sha512-1I1+G2gteLB8Tkt8YI1sJvSIfa0lWuRtC8GjvtyPBcLSF5jBCCJJqKrpER5JU5r6Bhe+i9/pK3VMuUcXu0kdwQ== - rx@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782" @@ -16433,11 +15895,6 @@ semver-compare@^1.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - integrity sha512-mfmm3/H9+67MCVix1h+IXTpDwL6710LyHuk7+cWC9T1mE0qz4iHhh6r4hU2wrIT9iTsAAC2XQRvfblL028cpLw== - semver@7.3.4: version "7.3.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" @@ -16593,15 +16050,6 @@ shell-quote@^1.6.1, shell-quote@^1.7.3: resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== -shelljs@^0.7.5: - version "0.7.8" - resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.7.8.tgz#decbcf874b0d1e5fb72e14b164a9683048e9acb3" - integrity sha512-/YF5Uk8hcwi7ima04ppkbA4RaRMdPMBfwAvAf8sufYOxsJRtbdoBsT8vGvlb+799BrlGdYrd+oczIA2eN2JdWA== - dependencies: - glob "^7.0.0" - interpret "^1.0.0" - rechoir "^0.6.2" - shelljs@^0.8.4: version "0.8.5" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" @@ -16697,11 +16145,6 @@ slash@^2.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== -slice-ansi@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" - integrity sha512-up04hB2hR92PgjpyU3y/eg91yIBILyjVY26NvvciY3EVVPjybkMszMpXQ9QAkcS3I5rtJBDLoTxxg+qvW8c7rw== - slice-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" @@ -17040,7 +16483,7 @@ stream-events@^1.0.5: dependencies: stubs "^3.0.0" -stream-http@^2.3.1, stream-http@^2.7.2: +stream-http@^2.7.2: version "2.8.3" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== @@ -17082,14 +16525,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -17149,11 +16584,6 @@ string.prototype.trimstart@^1.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" -string_decoder@^0.10.25: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== - string_decoder@^1.0.0, string_decoder@^1.1.1, string_decoder@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -17242,11 +16672,6 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== - strnum@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" @@ -17321,18 +16746,6 @@ symbol-tree@^3.2.2: resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== -table@^3.7.8: - version "3.8.3" - resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" - integrity sha512-RZuzIOtzFbprLCE0AXhkI0Xi42ZJLZhCC+qkwuMLf/Vjz3maWpA8gz1qMdbmNoI9cOROT2Am/DxeRyXenrL11g== - dependencies: - ajv "^4.7.0" - ajv-keywords "^1.0.0" - chalk "^1.1.1" - lodash "^4.0.0" - slice-ansi "0.0.4" - string-width "^2.0.0" - table@^6.0.9: version "6.8.1" resolved "https://registry.yarnpkg.com/table/-/table-6.8.1.tgz#ea2b71359fe03b017a5fbc296204471158080bdf" @@ -17344,11 +16757,6 @@ table@^6.0.9: string-width "^4.2.3" strip-ansi "^6.0.1" -tapable@^0.1.8: - version "0.1.10" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.1.10.tgz#29c35707c2b70e50d07482b5d202e8ed446dafd4" - integrity sha512-jX8Et4hHg57mug1/079yitEKWGB3LCwoxByLsNim89LABq8NqgiX+6iYVOsq0vX8uJHkU+DZ5fnq95f800bEsQ== - tapable@^1.0.0, tapable@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" @@ -17515,7 +16923,7 @@ text-hex@1.0.x: resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== -text-table@^0.2.0, text-table@~0.2.0: +text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== @@ -17550,13 +16958,6 @@ through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6: resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== -timers-browserify@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-1.4.2.tgz#c9c58b575be8407375cb5e2462dacee74359f41d" - integrity sha512-PIxwAupJZiYU4JmVZYwXp9FKsHMXb5h0ZEFyuXTAn8WLHOlcij+FEcbrvDsom1o5dr1YggEtFbECvGCW2sT53Q== - dependencies: - process "~0.11.0" - timers-browserify@^2.0.4: version "2.0.12" resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" @@ -17924,16 +17325,6 @@ type-fest@^0.8.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== -type@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== - -type@^2.7.2: - version "2.7.2" - resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" - integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== - typed-array-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" @@ -18085,11 +17476,6 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" -unc-path-regex@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== - unfetch@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" @@ -18283,13 +17669,6 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== -user-home@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-2.0.0.tgz#9c70bfd8169bc1dcbf48604e0f04b8b49cde9e9f" - integrity sha512-KMWqdlOcjCYdtIJpicDSFBQ8nFwS2i9sslAd6f4+CBGcU4gist2REnr2fxj2YocvJFxSF3ZOHLYLVZnUxv4BZQ== - dependencies: - os-homedir "^1.0.0" - util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" @@ -18315,13 +17694,6 @@ util@0.10.3: dependencies: inherits "2.0.1" -util@^0.10.3: - version "0.10.4" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" - integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== - dependencies: - inherits "2.0.3" - util@^0.11.0: version "0.11.1" resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" @@ -18412,13 +17784,6 @@ vlq@^1.0.0: resolved "https://registry.yarnpkg.com/vlq/-/vlq-1.0.1.tgz#c003f6e7c0b4c1edd623fd6ee50bbc0d6a1de468" integrity sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w== -vm-browserify@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" - integrity sha512-NyZNR3WDah+NPkjh/YmhuWSsT4a0mF0BJYgUmvrJ70zxjTXh5Y2Asobxlh0Nfs0PCFB5FVpRJft7NozAWFMwLQ== - dependencies: - indexof "0.0.1" - vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" @@ -18940,13 +18305,6 @@ write-pkg@4.0.0: type-fest "^0.4.1" write-json-file "^3.2.0" -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" - integrity sha512-CJ17OoULEKXpA5pef3qLj5AxTJ6mSt7g84he2WIskKwqFO4T97d5V7Tadl0DYDk7qyUOQD5WlUlOMChaYrhxeA== - dependencies: - mkdirp "^0.5.1" - ws@^5.2.0: version "5.2.3" resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.3.tgz#05541053414921bc29c63bee14b8b0dd50b07b3d"