@aws-amplify/auth 5.6.4-user-agent-backup.c6dfb36.0 → 5.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Auth.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
 
4
4
  import {
5
+ AuthOptions,
5
6
  FederatedResponse,
6
7
  SignUpParams,
7
8
  FederatedUser,
@@ -10,33 +11,300 @@ import {
10
11
  CurrentUserOpts,
11
12
  GetPreferredMFAOpts,
12
13
  SignInOpts,
14
+ isUsernamePasswordOpts,
15
+ isCognitoHostedOpts,
16
+ isFederatedSignInOptions,
17
+ isFederatedSignInOptionsCustom,
18
+ hasCustomState,
13
19
  FederatedSignInOptionsCustom,
14
20
  LegacyProvider,
15
21
  FederatedSignInOptions,
22
+ AwsCognitoOAuthOpts,
16
23
  ClientMetaData,
17
24
  } from './types';
18
25
 
19
- import { Amplify, ICredentials } from '@aws-amplify/core';
20
26
  import {
27
+ Amplify,
28
+ ConsoleLogger as Logger,
29
+ Credentials,
30
+ Hub,
31
+ StorageHelper,
32
+ ICredentials,
33
+ Platform,
34
+ browserOrNode,
35
+ parseAWSExports,
36
+ UniversalStorage,
37
+ urlSafeDecode,
38
+ HubCallback,
39
+ } from '@aws-amplify/core';
40
+ import {
41
+ CookieStorage,
42
+ CognitoUserPool,
43
+ AuthenticationDetails,
44
+ ICognitoUserPoolData,
45
+ ICognitoUserData,
21
46
  ISignUpResult,
22
47
  CognitoUser,
23
48
  MFAOption,
24
49
  CognitoUserSession,
50
+ IAuthenticationCallback,
51
+ ICognitoUserAttributeData,
25
52
  CognitoUserAttribute,
53
+ CognitoIdToken,
54
+ CognitoRefreshToken,
55
+ CognitoAccessToken,
56
+ NodeCallback,
57
+ CodeDeliveryDetails,
26
58
  } from 'amazon-cognito-identity-js';
59
+ import {
60
+ addAuthCategoryToCognitoUserAgent,
61
+ addFrameworkToCognitoUserAgent,
62
+ } from 'amazon-cognito-identity-js/internals';
63
+
64
+ import { parse } from 'url';
65
+ import OAuth from './OAuth/OAuth';
66
+ import { default as urlListener } from './urlListener';
67
+ import { AuthError, NoUserPoolError } from './Errors';
68
+ import {
69
+ AuthErrorTypes,
70
+ AutoSignInOptions,
71
+ CognitoHostedUIIdentityProvider,
72
+ IAuthDevice,
73
+ } from './types/Auth';
74
+
75
+ const logger = new Logger('AuthClass');
76
+ const USER_ADMIN_SCOPE = 'aws.cognito.signin.user.admin';
77
+
78
+ // 10 sec, following this guide https://www.nngroup.com/articles/response-times-3-important-limits/
79
+ const OAUTH_FLOW_MS_TIMEOUT = 10 * 1000;
80
+
81
+ const AMPLIFY_SYMBOL = (
82
+ typeof Symbol !== 'undefined' && typeof Symbol.for === 'function'
83
+ ? Symbol.for('amplify_default')
84
+ : '@@amplify_default'
85
+ ) as Symbol;
86
+
87
+ const dispatchAuthEvent = (event: string, data: any, message: string) => {
88
+ Hub.dispatch('auth', { event, data, message }, 'Auth', AMPLIFY_SYMBOL);
89
+ };
27
90
 
28
- import { AuthError } from './Errors';
29
- import { IAuthDevice } from './types/Auth';
30
- import { InternalAuthClass } from './internals/InternalAuth';
91
+ // Cognito Documentation for max device
92
+ // tslint:disable-next-line:max-line-length
93
+ // https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ListDevices.html#API_ListDevices_RequestSyntax
94
+ const MAX_DEVICES = 60;
95
+
96
+ const MAX_AUTOSIGNIN_POLLING_MS = 3 * 60 * 1000;
31
97
 
32
98
  /**
33
99
  * Provide authentication steps
34
100
  */
35
- export class AuthClass extends InternalAuthClass {
101
+ export class AuthClass {
102
+ private _config: AuthOptions;
103
+ private userPool: CognitoUserPool = null;
104
+ private user: any = null;
105
+ private _oAuthHandler: OAuth;
106
+ private _storage;
107
+ private _storageSync;
108
+ private oAuthFlowInProgress: boolean = false;
109
+ private pendingSignIn: ReturnType<AuthClass['signInWithPassword']> | null;
110
+ private autoSignInInitiated: boolean = false;
111
+ private inflightSessionPromise: Promise<CognitoUserSession> | null = null;
112
+ private inflightSessionPromiseCounter: number = 0;
113
+ Credentials = Credentials;
114
+
115
+ /**
116
+ * Initialize Auth with AWS configurations
117
+ * @param {Object} config - Configuration of the Auth
118
+ */
119
+ constructor(config: AuthOptions) {
120
+ this.configure(config);
121
+ this.currentCredentials = this.currentCredentials.bind(this);
122
+ this.currentUserCredentials = this.currentUserCredentials.bind(this);
123
+
124
+ Hub.listen('auth', ({ payload }) => {
125
+ const { event } = payload;
126
+ switch (event) {
127
+ case 'verify':
128
+ case 'signIn':
129
+ this._storage.setItem('amplify-signin-with-hostedUI', 'false');
130
+ break;
131
+ case 'signOut':
132
+ this._storage.removeItem('amplify-signin-with-hostedUI');
133
+ break;
134
+ case 'cognitoHostedUI':
135
+ this._storage.setItem('amplify-signin-with-hostedUI', 'true');
136
+ break;
137
+ }
138
+ });
139
+
140
+ addAuthCategoryToCognitoUserAgent();
141
+ addFrameworkToCognitoUserAgent(Platform.framework);
142
+ Platform.observeFrameworkChanges(() => {
143
+ addFrameworkToCognitoUserAgent(Platform.framework);
144
+ });
145
+ }
146
+
36
147
  public getModuleName() {
37
148
  return 'Auth';
38
149
  }
39
150
 
151
+ configure(config?) {
152
+ if (!config) return this._config || {};
153
+ logger.debug('configure Auth');
154
+ const conf = Object.assign(
155
+ {},
156
+ this._config,
157
+ parseAWSExports(config).Auth,
158
+ config
159
+ );
160
+ this._config = conf;
161
+ const {
162
+ userPoolId,
163
+ userPoolWebClientId,
164
+ cookieStorage,
165
+ oauth,
166
+ region,
167
+ identityPoolId,
168
+ mandatorySignIn,
169
+ refreshHandlers,
170
+ identityPoolRegion,
171
+ clientMetadata,
172
+ endpoint,
173
+ storage,
174
+ } = this._config;
175
+
176
+ if (!storage) {
177
+ // backward compatability
178
+ if (cookieStorage) this._storage = new CookieStorage(cookieStorage);
179
+ else {
180
+ this._storage = config.ssr
181
+ ? new UniversalStorage()
182
+ : new StorageHelper().getStorage();
183
+ }
184
+ } else {
185
+ if (!this._isValidAuthStorage(storage)) {
186
+ logger.error('The storage in the Auth config is not valid!');
187
+ throw new Error('Empty storage object');
188
+ }
189
+ this._storage = storage;
190
+ }
191
+
192
+ this._storageSync = Promise.resolve();
193
+ if (typeof this._storage['sync'] === 'function') {
194
+ this._storageSync = this._storage['sync']();
195
+ }
196
+
197
+ if (userPoolId) {
198
+ const userPoolData: ICognitoUserPoolData = {
199
+ UserPoolId: userPoolId,
200
+ ClientId: userPoolWebClientId,
201
+ endpoint,
202
+ };
203
+ userPoolData.Storage = this._storage;
204
+
205
+ this.userPool = new CognitoUserPool(
206
+ userPoolData,
207
+ this.wrapRefreshSessionCallback
208
+ );
209
+ }
210
+
211
+ this.Credentials.configure({
212
+ mandatorySignIn,
213
+ region,
214
+ userPoolId,
215
+ identityPoolId,
216
+ refreshHandlers,
217
+ storage: this._storage,
218
+ identityPoolRegion,
219
+ });
220
+
221
+ // initialize cognitoauth client if hosted ui options provided
222
+ // to keep backward compatibility:
223
+ const cognitoHostedUIConfig = oauth
224
+ ? isCognitoHostedOpts(this._config.oauth)
225
+ ? oauth
226
+ : (<any>oauth).awsCognito
227
+ : undefined;
228
+
229
+ if (cognitoHostedUIConfig) {
230
+ const cognitoAuthParams = Object.assign(
231
+ {
232
+ cognitoClientId: userPoolWebClientId,
233
+ UserPoolId: userPoolId,
234
+ domain: cognitoHostedUIConfig['domain'],
235
+ scopes: cognitoHostedUIConfig['scope'],
236
+ redirectSignIn: cognitoHostedUIConfig['redirectSignIn'],
237
+ redirectSignOut: cognitoHostedUIConfig['redirectSignOut'],
238
+ responseType: cognitoHostedUIConfig['responseType'],
239
+ Storage: this._storage,
240
+ urlOpener: cognitoHostedUIConfig['urlOpener'],
241
+ clientMetadata,
242
+ },
243
+ cognitoHostedUIConfig['options']
244
+ );
245
+
246
+ this._oAuthHandler = new OAuth({
247
+ scopes: cognitoAuthParams.scopes,
248
+ config: cognitoAuthParams,
249
+ cognitoClientId: cognitoAuthParams.cognitoClientId,
250
+ });
251
+
252
+ // **NOTE** - Remove this in a future major release as it is a breaking change
253
+ // Prevents _handleAuthResponse from being called multiple times in Expo
254
+ // See https://github.com/aws-amplify/amplify-js/issues/4388
255
+ const usedResponseUrls = {};
256
+ urlListener(({ url }) => {
257
+ if (usedResponseUrls[url]) {
258
+ return;
259
+ }
260
+
261
+ usedResponseUrls[url] = true;
262
+ this._handleAuthResponse(url);
263
+ });
264
+ }
265
+
266
+ dispatchAuthEvent(
267
+ 'configured',
268
+ null,
269
+ `The Auth category has been configured successfully`
270
+ );
271
+
272
+ if (
273
+ !this.autoSignInInitiated &&
274
+ typeof this._storage['getItem'] === 'function'
275
+ ) {
276
+ const pollingInitiated = this.isTrueStorageValue(
277
+ 'amplify-polling-started'
278
+ );
279
+ if (pollingInitiated) {
280
+ dispatchAuthEvent(
281
+ 'autoSignIn_failure',
282
+ null,
283
+ AuthErrorTypes.AutoSignInError
284
+ );
285
+ this._storage.removeItem('amplify-auto-sign-in');
286
+ }
287
+ this._storage.removeItem('amplify-polling-started');
288
+ }
289
+ return this._config;
290
+ }
291
+
292
+ wrapRefreshSessionCallback = (callback: NodeCallback.Any) => {
293
+ const wrapped: NodeCallback.Any = (error, data) => {
294
+ if (data) {
295
+ dispatchAuthEvent('tokenRefresh', undefined, `New token retrieved`);
296
+ } else {
297
+ dispatchAuthEvent(
298
+ 'tokenRefresh_failure',
299
+ error,
300
+ `Failed to retrieve new token`
301
+ );
302
+ }
303
+ return callback(error, data);
304
+ };
305
+ return wrapped;
306
+ } // prettier-ignore
307
+
40
308
  /**
41
309
  * Sign up with username, password and other attributes like phone, email
42
310
  * @param {String | object} params - The user attributes used for signin
@@ -47,7 +315,215 @@ export class AuthClass extends InternalAuthClass {
47
315
  params: string | SignUpParams,
48
316
  ...restOfAttrs: string[]
49
317
  ): Promise<ISignUpResult> {
50
- return super.signUp(params, restOfAttrs);
318
+ if (!this.userPool) {
319
+ return this.rejectNoUserPool();
320
+ }
321
+
322
+ let username: string = null;
323
+ let password: string = null;
324
+ const attributes: CognitoUserAttribute[] = [];
325
+ let validationData: CognitoUserAttribute[] = null;
326
+ let clientMetadata;
327
+ let autoSignIn: AutoSignInOptions = { enabled: false };
328
+ let autoSignInValidationData = {};
329
+ let autoSignInClientMetaData: ClientMetaData = {};
330
+
331
+ if (params && typeof params === 'string') {
332
+ username = params;
333
+ password = restOfAttrs ? restOfAttrs[0] : null;
334
+ const email: string = restOfAttrs ? restOfAttrs[1] : null;
335
+ const phone_number: string = restOfAttrs ? restOfAttrs[2] : null;
336
+
337
+ if (email)
338
+ attributes.push(
339
+ new CognitoUserAttribute({ Name: 'email', Value: email })
340
+ );
341
+
342
+ if (phone_number)
343
+ attributes.push(
344
+ new CognitoUserAttribute({
345
+ Name: 'phone_number',
346
+ Value: phone_number,
347
+ })
348
+ );
349
+ } else if (params && typeof params === 'object') {
350
+ username = params['username'];
351
+ password = params['password'];
352
+
353
+ if (params && params.clientMetadata) {
354
+ clientMetadata = params.clientMetadata;
355
+ } else if (this._config.clientMetadata) {
356
+ clientMetadata = this._config.clientMetadata;
357
+ }
358
+
359
+ const attrs = params['attributes'];
360
+ if (attrs) {
361
+ Object.keys(attrs).map(key => {
362
+ attributes.push(
363
+ new CognitoUserAttribute({ Name: key, Value: attrs[key] })
364
+ );
365
+ });
366
+ }
367
+
368
+ const validationDataObject = params['validationData'];
369
+ if (validationDataObject) {
370
+ validationData = [];
371
+ Object.keys(validationDataObject).map(key => {
372
+ validationData.push(
373
+ new CognitoUserAttribute({
374
+ Name: key,
375
+ Value: validationDataObject[key],
376
+ })
377
+ );
378
+ });
379
+ }
380
+
381
+ autoSignIn = params.autoSignIn ?? { enabled: false };
382
+ if (autoSignIn.enabled) {
383
+ this._storage.setItem('amplify-auto-sign-in', 'true');
384
+ autoSignInValidationData = autoSignIn.validationData ?? {};
385
+ autoSignInClientMetaData = autoSignIn.clientMetaData ?? {};
386
+ }
387
+ } else {
388
+ return this.rejectAuthError(AuthErrorTypes.SignUpError);
389
+ }
390
+
391
+ if (!username) {
392
+ return this.rejectAuthError(AuthErrorTypes.EmptyUsername);
393
+ }
394
+ if (!password) {
395
+ return this.rejectAuthError(AuthErrorTypes.EmptyPassword);
396
+ }
397
+
398
+ logger.debug('signUp attrs:', attributes);
399
+ logger.debug('signUp validation data:', validationData);
400
+
401
+ return new Promise((resolve, reject) => {
402
+ this.userPool.signUp(
403
+ username,
404
+ password,
405
+ attributes,
406
+ validationData,
407
+ (err, data) => {
408
+ if (err) {
409
+ dispatchAuthEvent(
410
+ 'signUp_failure',
411
+ err,
412
+ `${username} failed to signup`
413
+ );
414
+ reject(err);
415
+ } else {
416
+ dispatchAuthEvent(
417
+ 'signUp',
418
+ data,
419
+ `${username} has signed up successfully`
420
+ );
421
+ if (autoSignIn.enabled) {
422
+ this.handleAutoSignIn(
423
+ username,
424
+ password,
425
+ autoSignInValidationData,
426
+ autoSignInClientMetaData,
427
+ data
428
+ );
429
+ }
430
+ resolve(data);
431
+ }
432
+ },
433
+ clientMetadata
434
+ );
435
+ });
436
+ }
437
+
438
+ private handleAutoSignIn(
439
+ username: string,
440
+ password: string,
441
+ validationData: {},
442
+ clientMetadata: any,
443
+ data: any
444
+ ) {
445
+ this.autoSignInInitiated = true;
446
+ const authDetails = new AuthenticationDetails({
447
+ Username: username,
448
+ Password: password,
449
+ ValidationData: validationData,
450
+ ClientMetadata: clientMetadata,
451
+ });
452
+ if (data.userConfirmed) {
453
+ this.signInAfterUserConfirmed(authDetails);
454
+ } else if (this._config.signUpVerificationMethod === 'link') {
455
+ this.handleLinkAutoSignIn(authDetails);
456
+ } else {
457
+ this.handleCodeAutoSignIn(authDetails);
458
+ }
459
+ }
460
+
461
+ private handleCodeAutoSignIn(authDetails: AuthenticationDetails) {
462
+ const listenEvent = ({ payload }) => {
463
+ if (payload.event === 'confirmSignUp') {
464
+ this.signInAfterUserConfirmed(authDetails, listenEvent);
465
+ }
466
+ };
467
+ Hub.listen('auth', listenEvent);
468
+ }
469
+
470
+ private handleLinkAutoSignIn(authDetails: AuthenticationDetails) {
471
+ this._storage.setItem('amplify-polling-started', 'true');
472
+ const start = Date.now();
473
+ const autoSignInPollingIntervalId = setInterval(() => {
474
+ if (Date.now() - start > MAX_AUTOSIGNIN_POLLING_MS) {
475
+ clearInterval(autoSignInPollingIntervalId);
476
+ dispatchAuthEvent(
477
+ 'autoSignIn_failure',
478
+ null,
479
+ 'Please confirm your account and use your credentials to sign in.'
480
+ );
481
+ this._storage.removeItem('amplify-auto-sign-in');
482
+ } else {
483
+ this.signInAfterUserConfirmed(
484
+ authDetails,
485
+ null,
486
+ autoSignInPollingIntervalId
487
+ );
488
+ }
489
+ }, 5000);
490
+ }
491
+
492
+ private async signInAfterUserConfirmed(
493
+ authDetails: AuthenticationDetails,
494
+ listenEvent?: HubCallback,
495
+ autoSignInPollingIntervalId?: ReturnType<typeof setInterval>
496
+ ) {
497
+ const user = this.createCognitoUser(authDetails.getUsername());
498
+ try {
499
+ await user.authenticateUser(
500
+ authDetails,
501
+ this.authCallbacks(
502
+ user,
503
+ value => {
504
+ dispatchAuthEvent(
505
+ 'autoSignIn',
506
+ value,
507
+ `${authDetails.getUsername()} has signed in successfully`
508
+ );
509
+ if (listenEvent) {
510
+ Hub.remove('auth', listenEvent);
511
+ }
512
+ if (autoSignInPollingIntervalId) {
513
+ clearInterval(autoSignInPollingIntervalId);
514
+ this._storage.removeItem('amplify-polling-started');
515
+ }
516
+ this._storage.removeItem('amplify-auto-sign-in');
517
+ },
518
+ error => {
519
+ logger.error(error);
520
+ this._storage.removeItem('amplify-auto-sign-in');
521
+ }
522
+ )
523
+ );
524
+ } catch (error) {
525
+ logger.error(error);
526
+ }
51
527
  }
52
528
 
53
529
  /**
@@ -62,7 +538,61 @@ export class AuthClass extends InternalAuthClass {
62
538
  code: string,
63
539
  options?: ConfirmSignUpOptions
64
540
  ): Promise<any> {
65
- return super.confirmSignUp(username, code, options);
541
+ if (!this.userPool) {
542
+ return this.rejectNoUserPool();
543
+ }
544
+ if (!username) {
545
+ return this.rejectAuthError(AuthErrorTypes.EmptyUsername);
546
+ }
547
+ if (!code) {
548
+ return this.rejectAuthError(AuthErrorTypes.EmptyCode);
549
+ }
550
+
551
+ const user = this.createCognitoUser(username);
552
+ const forceAliasCreation =
553
+ options && typeof options.forceAliasCreation === 'boolean'
554
+ ? options.forceAliasCreation
555
+ : true;
556
+
557
+ let clientMetadata;
558
+ if (options && options.clientMetadata) {
559
+ clientMetadata = options.clientMetadata;
560
+ } else if (this._config.clientMetadata) {
561
+ clientMetadata = this._config.clientMetadata;
562
+ }
563
+ return new Promise((resolve, reject) => {
564
+ user.confirmRegistration(
565
+ code,
566
+ forceAliasCreation,
567
+ (err, data) => {
568
+ if (err) {
569
+ reject(err);
570
+ } else {
571
+ dispatchAuthEvent(
572
+ 'confirmSignUp',
573
+ data,
574
+ `${username} has been confirmed successfully`
575
+ );
576
+ const autoSignIn = this.isTrueStorageValue('amplify-auto-sign-in');
577
+ if (autoSignIn && !this.autoSignInInitiated) {
578
+ dispatchAuthEvent(
579
+ 'autoSignIn_failure',
580
+ null,
581
+ AuthErrorTypes.AutoSignInError
582
+ );
583
+ this._storage.removeItem('amplify-auto-sign-in');
584
+ }
585
+ resolve(data);
586
+ }
587
+ },
588
+ clientMetadata
589
+ );
590
+ });
591
+ }
592
+
593
+ private isTrueStorageValue(value: string) {
594
+ const item = this._storage.getItem(value);
595
+ return item ? item === 'true' : false;
66
596
  }
67
597
 
68
598
  /**
@@ -73,9 +603,25 @@ export class AuthClass extends InternalAuthClass {
73
603
  */
74
604
  public resendSignUp(
75
605
  username: string,
76
- clientMetadata?: ClientMetaData
606
+ clientMetadata: ClientMetaData = this._config.clientMetadata
77
607
  ): Promise<any> {
78
- return super.resendSignUp(username, clientMetadata);
608
+ if (!this.userPool) {
609
+ return this.rejectNoUserPool();
610
+ }
611
+ if (!username) {
612
+ return this.rejectAuthError(AuthErrorTypes.EmptyUsername);
613
+ }
614
+
615
+ const user = this.createCognitoUser(username);
616
+ return new Promise((resolve, reject) => {
617
+ user.resendConfirmationCode((err, data) => {
618
+ if (err) {
619
+ reject(err);
620
+ } else {
621
+ resolve(data);
622
+ }
623
+ }, clientMetadata);
624
+ });
79
625
  }
80
626
 
81
627
  /**
@@ -88,9 +634,191 @@ export class AuthClass extends InternalAuthClass {
88
634
  public signIn(
89
635
  usernameOrSignInOpts: string | SignInOpts,
90
636
  pw?: string,
91
- clientMetadata?: ClientMetaData
637
+ clientMetadata: ClientMetaData = this._config.clientMetadata
638
+ ): Promise<CognitoUser | any> {
639
+ if (!this.userPool) {
640
+ return this.rejectNoUserPool();
641
+ }
642
+
643
+ let username = null;
644
+ let password = null;
645
+ let validationData = {};
646
+
647
+ // for backward compatibility
648
+ if (typeof usernameOrSignInOpts === 'string') {
649
+ username = usernameOrSignInOpts;
650
+ password = pw;
651
+ } else if (isUsernamePasswordOpts(usernameOrSignInOpts)) {
652
+ if (typeof pw !== 'undefined') {
653
+ logger.warn(
654
+ 'The password should be defined under the first parameter object!'
655
+ );
656
+ }
657
+ username = usernameOrSignInOpts.username;
658
+ password = usernameOrSignInOpts.password;
659
+ validationData = usernameOrSignInOpts.validationData;
660
+ } else {
661
+ return this.rejectAuthError(AuthErrorTypes.InvalidUsername);
662
+ }
663
+ if (!username) {
664
+ return this.rejectAuthError(AuthErrorTypes.EmptyUsername);
665
+ }
666
+ const authDetails = new AuthenticationDetails({
667
+ Username: username,
668
+ Password: password,
669
+ ValidationData: validationData,
670
+ ClientMetadata: clientMetadata,
671
+ });
672
+ if (password) {
673
+ return this.signInWithPassword(authDetails);
674
+ } else {
675
+ return this.signInWithoutPassword(authDetails);
676
+ }
677
+ }
678
+
679
+ /**
680
+ * Return an object with the authentication callbacks
681
+ * @param {CognitoUser} user - the cognito user object
682
+ * @param {} resolve - function called when resolving the current step
683
+ * @param {} reject - function called when rejecting the current step
684
+ * @return - an object with the callback methods for user authentication
685
+ */
686
+ private authCallbacks(
687
+ user: CognitoUser,
688
+ resolve: (value?: CognitoUser | any) => void,
689
+ reject: (value?: any) => void
690
+ ): IAuthenticationCallback {
691
+ const that = this;
692
+ return {
693
+ onSuccess: async session => {
694
+ logger.debug(session);
695
+ delete user['challengeName'];
696
+ delete user['challengeParam'];
697
+ try {
698
+ await this.Credentials.clear();
699
+ const cred = await this.Credentials.set(session, 'session');
700
+ logger.debug('succeed to get cognito credentials', cred);
701
+ } catch (e) {
702
+ logger.debug('cannot get cognito credentials', e);
703
+ } finally {
704
+ try {
705
+ // In order to get user attributes and MFA methods
706
+ // We need to trigger currentUserPoolUser again
707
+ const currentUser = await this.currentUserPoolUser();
708
+ that.user = currentUser;
709
+ dispatchAuthEvent(
710
+ 'signIn',
711
+ currentUser,
712
+ `A user ${user.getUsername()} has been signed in`
713
+ );
714
+ resolve(currentUser);
715
+ } catch (e) {
716
+ logger.error('Failed to get the signed in user', e);
717
+ reject(e);
718
+ }
719
+ }
720
+ },
721
+ onFailure: err => {
722
+ logger.debug('signIn failure', err);
723
+ dispatchAuthEvent(
724
+ 'signIn_failure',
725
+ err,
726
+ `${user.getUsername()} failed to signin`
727
+ );
728
+ reject(err);
729
+ },
730
+ customChallenge: challengeParam => {
731
+ logger.debug('signIn custom challenge answer required');
732
+ user['challengeName'] = 'CUSTOM_CHALLENGE';
733
+ user['challengeParam'] = challengeParam;
734
+ resolve(user);
735
+ },
736
+ mfaRequired: (challengeName, challengeParam) => {
737
+ logger.debug('signIn MFA required');
738
+ user['challengeName'] = challengeName;
739
+ user['challengeParam'] = challengeParam;
740
+ resolve(user);
741
+ },
742
+ mfaSetup: (challengeName, challengeParam) => {
743
+ logger.debug('signIn mfa setup', challengeName);
744
+ user['challengeName'] = challengeName;
745
+ user['challengeParam'] = challengeParam;
746
+ resolve(user);
747
+ },
748
+ newPasswordRequired: (userAttributes, requiredAttributes) => {
749
+ logger.debug('signIn new password');
750
+ user['challengeName'] = 'NEW_PASSWORD_REQUIRED';
751
+ user['challengeParam'] = {
752
+ userAttributes,
753
+ requiredAttributes,
754
+ };
755
+ resolve(user);
756
+ },
757
+ totpRequired: (challengeName, challengeParam) => {
758
+ logger.debug('signIn totpRequired');
759
+ user['challengeName'] = challengeName;
760
+ user['challengeParam'] = challengeParam;
761
+ resolve(user);
762
+ },
763
+ selectMFAType: (challengeName, challengeParam) => {
764
+ logger.debug('signIn selectMFAType', challengeName);
765
+ user['challengeName'] = challengeName;
766
+ user['challengeParam'] = challengeParam;
767
+ resolve(user);
768
+ },
769
+ };
770
+ }
771
+
772
+ /**
773
+ * Sign in with a password
774
+ * @private
775
+ * @param {AuthenticationDetails} authDetails - the user sign in data
776
+ * @return - A promise resolves the CognitoUser object if success or mfa required
777
+ */
778
+ private signInWithPassword(
779
+ authDetails: AuthenticationDetails
780
+ ): Promise<CognitoUser | any> {
781
+ if (this.pendingSignIn) {
782
+ throw new Error('Pending sign-in attempt already in progress');
783
+ }
784
+
785
+ const user = this.createCognitoUser(authDetails.getUsername());
786
+
787
+ this.pendingSignIn = new Promise((resolve, reject) => {
788
+ user.authenticateUser(
789
+ authDetails,
790
+ this.authCallbacks(
791
+ user,
792
+ value => {
793
+ this.pendingSignIn = null;
794
+ resolve(value);
795
+ },
796
+ error => {
797
+ this.pendingSignIn = null;
798
+ reject(error);
799
+ }
800
+ )
801
+ );
802
+ });
803
+
804
+ return this.pendingSignIn;
805
+ }
806
+
807
+ /**
808
+ * Sign in without a password
809
+ * @private
810
+ * @param {AuthenticationDetails} authDetails - the user sign in data
811
+ * @return - A promise resolves the CognitoUser object if success or mfa required
812
+ */
813
+ private signInWithoutPassword(
814
+ authDetails: AuthenticationDetails
92
815
  ): Promise<CognitoUser | any> {
93
- return super.signIn(usernameOrSignInOpts, pw, clientMetadata);
816
+ const user = this.createCognitoUser(authDetails.getUsername());
817
+ user.setAuthenticationFlowType('CUSTOM_AUTH');
818
+
819
+ return new Promise((resolve, reject) => {
820
+ user.initiateAuth(authDetails, this.authCallbacks(user, resolve, reject));
821
+ });
94
822
  }
95
823
 
96
824
  /**
@@ -102,7 +830,18 @@ export class AuthClass extends InternalAuthClass {
102
830
  * @return - A promise resolves the current preferred mfa option if success
103
831
  */
104
832
  public getMFAOptions(user: CognitoUser | any): Promise<MFAOption[]> {
105
- return super.getMFAOptions(user);
833
+ return new Promise((res, rej) => {
834
+ user.getMFAOptions((err, mfaOptions) => {
835
+ if (err) {
836
+ logger.debug('get MFA Options failed', err);
837
+ rej(err);
838
+ return;
839
+ }
840
+ logger.debug('get MFA options success', mfaOptions);
841
+ res(mfaOptions);
842
+ return;
843
+ });
844
+ });
106
845
  }
107
846
 
108
847
  /**
@@ -114,7 +853,99 @@ export class AuthClass extends InternalAuthClass {
114
853
  user: CognitoUser | any,
115
854
  params?: GetPreferredMFAOpts
116
855
  ): Promise<string> {
117
- return super.getPreferredMFA(user, params);
856
+ const that = this;
857
+ return new Promise((res, rej) => {
858
+ const clientMetadata = this._config.clientMetadata; // TODO: verify behavior if this is override during signIn
859
+
860
+ const bypassCache = params ? params.bypassCache : false;
861
+ user.getUserData(
862
+ async (err, data) => {
863
+ if (err) {
864
+ logger.debug('getting preferred mfa failed', err);
865
+ if (this.isSessionInvalid(err)) {
866
+ try {
867
+ await this.cleanUpInvalidSession(user);
868
+ } catch (cleanUpError) {
869
+ rej(
870
+ new Error(
871
+ `Session is invalid due to: ${err.message} and failed to clean up invalid session: ${cleanUpError.message}`
872
+ )
873
+ );
874
+ return;
875
+ }
876
+ }
877
+ rej(err);
878
+ return;
879
+ }
880
+
881
+ const mfaType = that._getMfaTypeFromUserData(data);
882
+ if (!mfaType) {
883
+ rej('invalid MFA Type');
884
+ return;
885
+ } else {
886
+ res(mfaType);
887
+ return;
888
+ }
889
+ },
890
+ { bypassCache, clientMetadata }
891
+ );
892
+ });
893
+ }
894
+
895
+ private _getMfaTypeFromUserData(data) {
896
+ let ret = null;
897
+ const preferredMFA = data.PreferredMfaSetting;
898
+ // if the user has used Auth.setPreferredMFA() to setup the mfa type
899
+ // then the "PreferredMfaSetting" would exist in the response
900
+ if (preferredMFA) {
901
+ ret = preferredMFA;
902
+ } else {
903
+ // if mfaList exists but empty, then its noMFA
904
+ const mfaList = data.UserMFASettingList;
905
+ if (!mfaList) {
906
+ // if SMS was enabled by using Auth.enableSMS(),
907
+ // the response would contain MFAOptions
908
+ // as for now Cognito only supports for SMS, so we will say it is 'SMS_MFA'
909
+ // if it does not exist, then it should be NOMFA
910
+ const MFAOptions = data.MFAOptions;
911
+ if (MFAOptions) {
912
+ ret = 'SMS_MFA';
913
+ } else {
914
+ ret = 'NOMFA';
915
+ }
916
+ } else if (mfaList.length === 0) {
917
+ ret = 'NOMFA';
918
+ } else {
919
+ logger.debug('invalid case for getPreferredMFA', data);
920
+ }
921
+ }
922
+ return ret;
923
+ }
924
+
925
+ private _getUserData(user, params) {
926
+ return new Promise((res, rej) => {
927
+ user.getUserData(async (err, data) => {
928
+ if (err) {
929
+ logger.debug('getting user data failed', err);
930
+ if (this.isSessionInvalid(err)) {
931
+ try {
932
+ await this.cleanUpInvalidSession(user);
933
+ } catch (cleanUpError) {
934
+ rej(
935
+ new Error(
936
+ `Session is invalid due to: ${err.message} and failed to clean up invalid session: ${cleanUpError.message}`
937
+ )
938
+ );
939
+ return;
940
+ }
941
+ }
942
+ rej(err);
943
+ return;
944
+ } else {
945
+ res(data);
946
+ }
947
+ }, params);
948
+ });
118
949
  }
119
950
 
120
951
  /**
@@ -123,11 +954,118 @@ export class AuthClass extends InternalAuthClass {
123
954
  * @param {string} mfaMethod - preferred mfa method
124
955
  * @return - A promise resolve if success
125
956
  */
126
- public setPreferredMFA(
957
+ public async setPreferredMFA(
127
958
  user: CognitoUser | any,
128
959
  mfaMethod: 'TOTP' | 'SMS' | 'NOMFA' | 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA'
129
960
  ): Promise<string> {
130
- return super.setPreferredMFA(user, mfaMethod);
961
+ const clientMetadata = this._config.clientMetadata; // TODO: verify behavior if this is override during signIn
962
+
963
+ const userData = await this._getUserData(user, {
964
+ bypassCache: true,
965
+ clientMetadata,
966
+ });
967
+ let smsMfaSettings = null;
968
+ let totpMfaSettings = null;
969
+
970
+ switch (mfaMethod) {
971
+ case 'TOTP':
972
+ case 'SOFTWARE_TOKEN_MFA':
973
+ totpMfaSettings = {
974
+ PreferredMfa: true,
975
+ Enabled: true,
976
+ };
977
+ break;
978
+ case 'SMS':
979
+ case 'SMS_MFA':
980
+ smsMfaSettings = {
981
+ PreferredMfa: true,
982
+ Enabled: true,
983
+ };
984
+ break;
985
+ case 'NOMFA':
986
+ const mfaList = userData['UserMFASettingList'];
987
+ const currentMFAType = await this._getMfaTypeFromUserData(userData);
988
+ if (currentMFAType === 'NOMFA') {
989
+ return Promise.resolve('No change for mfa type');
990
+ } else if (currentMFAType === 'SMS_MFA') {
991
+ smsMfaSettings = {
992
+ PreferredMfa: false,
993
+ Enabled: false,
994
+ };
995
+ } else if (currentMFAType === 'SOFTWARE_TOKEN_MFA') {
996
+ totpMfaSettings = {
997
+ PreferredMfa: false,
998
+ Enabled: false,
999
+ };
1000
+ } else {
1001
+ return this.rejectAuthError(AuthErrorTypes.InvalidMFA);
1002
+ }
1003
+ // if there is a UserMFASettingList in the response
1004
+ // we need to disable every mfa type in that list
1005
+ if (mfaList && mfaList.length !== 0) {
1006
+ // to disable SMS or TOTP if exists in that list
1007
+ mfaList.forEach(mfaType => {
1008
+ if (mfaType === 'SMS_MFA') {
1009
+ smsMfaSettings = {
1010
+ PreferredMfa: false,
1011
+ Enabled: false,
1012
+ };
1013
+ } else if (mfaType === 'SOFTWARE_TOKEN_MFA') {
1014
+ totpMfaSettings = {
1015
+ PreferredMfa: false,
1016
+ Enabled: false,
1017
+ };
1018
+ }
1019
+ });
1020
+ }
1021
+ break;
1022
+ default:
1023
+ logger.debug('no validmfa method provided');
1024
+ return this.rejectAuthError(AuthErrorTypes.NoMFA);
1025
+ }
1026
+
1027
+ const that = this;
1028
+ return new Promise<string>((res, rej) => {
1029
+ user.setUserMfaPreference(
1030
+ smsMfaSettings,
1031
+ totpMfaSettings,
1032
+ (err, result) => {
1033
+ if (err) {
1034
+ logger.debug('Set user mfa preference error', err);
1035
+ return rej(err);
1036
+ }
1037
+ logger.debug('Set user mfa success', result);
1038
+ logger.debug('Caching the latest user data into local');
1039
+ // cache the latest result into user data
1040
+ user.getUserData(
1041
+ async (err, data) => {
1042
+ if (err) {
1043
+ logger.debug('getting user data failed', err);
1044
+ if (this.isSessionInvalid(err)) {
1045
+ try {
1046
+ await this.cleanUpInvalidSession(user);
1047
+ } catch (cleanUpError) {
1048
+ rej(
1049
+ new Error(
1050
+ `Session is invalid due to: ${err.message} and failed to clean up invalid session: ${cleanUpError.message}`
1051
+ )
1052
+ );
1053
+ return;
1054
+ }
1055
+ }
1056
+ return rej(err);
1057
+ } else {
1058
+ return res(result);
1059
+ }
1060
+ },
1061
+ {
1062
+ bypassCache: true,
1063
+ clientMetadata,
1064
+ }
1065
+ );
1066
+ }
1067
+ );
1068
+ });
131
1069
  }
132
1070
 
133
1071
  /**
@@ -137,7 +1075,18 @@ export class AuthClass extends InternalAuthClass {
137
1075
  * @return - A promise resolves is success
138
1076
  */
139
1077
  public disableSMS(user: CognitoUser): Promise<string> {
140
- return super.disableSMS(user);
1078
+ return new Promise((res, rej) => {
1079
+ user.disableMFA((err, data) => {
1080
+ if (err) {
1081
+ logger.debug('disable mfa failed', err);
1082
+ rej(err);
1083
+ return;
1084
+ }
1085
+ logger.debug('disable mfa succeed', data);
1086
+ res(data);
1087
+ return;
1088
+ });
1089
+ });
141
1090
  }
142
1091
 
143
1092
  /**
@@ -147,7 +1096,18 @@ export class AuthClass extends InternalAuthClass {
147
1096
  * @return - A promise resolves is success
148
1097
  */
149
1098
  public enableSMS(user: CognitoUser): Promise<string> {
150
- return super.enableSMS(user);
1099
+ return new Promise((res, rej) => {
1100
+ user.enableMFA((err, data) => {
1101
+ if (err) {
1102
+ logger.debug('enable mfa failed', err);
1103
+ rej(err);
1104
+ return;
1105
+ }
1106
+ logger.debug('enable mfa succeed', data);
1107
+ res(data);
1108
+ return;
1109
+ });
1110
+ });
151
1111
  }
152
1112
 
153
1113
  /**
@@ -156,7 +1116,20 @@ export class AuthClass extends InternalAuthClass {
156
1116
  * @return - A promise resolves with the secret code if success
157
1117
  */
158
1118
  public setupTOTP(user: CognitoUser | any): Promise<string> {
159
- return super.setupTOTP(user);
1119
+ return new Promise((res, rej) => {
1120
+ user.associateSoftwareToken({
1121
+ onFailure: err => {
1122
+ logger.debug('associateSoftwareToken failed', err);
1123
+ rej(err);
1124
+ return;
1125
+ },
1126
+ associateSecretCode: secretCode => {
1127
+ logger.debug('associateSoftwareToken success', secretCode);
1128
+ res(secretCode);
1129
+ return;
1130
+ },
1131
+ });
1132
+ });
160
1133
  }
161
1134
 
162
1135
  /**
@@ -169,7 +1142,40 @@ export class AuthClass extends InternalAuthClass {
169
1142
  user: CognitoUser | any,
170
1143
  challengeAnswer: string
171
1144
  ): Promise<CognitoUserSession> {
172
- return super.verifyTotpToken(user, challengeAnswer);
1145
+ logger.debug('verification totp token', user, challengeAnswer);
1146
+
1147
+ let signInUserSession;
1148
+ if (user && typeof user.getSignInUserSession === 'function') {
1149
+ signInUserSession = (user as CognitoUser).getSignInUserSession();
1150
+ }
1151
+ const isLoggedIn = signInUserSession?.isValid();
1152
+
1153
+ return new Promise((res, rej) => {
1154
+ user.verifySoftwareToken(challengeAnswer, 'My TOTP device', {
1155
+ onFailure: err => {
1156
+ logger.debug('verifyTotpToken failed', err);
1157
+ rej(err);
1158
+ return;
1159
+ },
1160
+ onSuccess: data => {
1161
+ if (!isLoggedIn) {
1162
+ dispatchAuthEvent(
1163
+ 'signIn',
1164
+ user,
1165
+ `A user ${user.getUsername()} has been signed in`
1166
+ );
1167
+ }
1168
+ dispatchAuthEvent(
1169
+ 'verify',
1170
+ user,
1171
+ `A user ${user.getUsername()} has been verified`
1172
+ );
1173
+ logger.debug('verifyTotpToken success', data);
1174
+ res(data);
1175
+ return;
1176
+ },
1177
+ });
1178
+ });
173
1179
  }
174
1180
 
175
1181
  /**
@@ -181,23 +1187,117 @@ export class AuthClass extends InternalAuthClass {
181
1187
  user: CognitoUser | any,
182
1188
  code: string,
183
1189
  mfaType?: 'SMS_MFA' | 'SOFTWARE_TOKEN_MFA' | null,
184
- clientMetadata?: ClientMetaData
1190
+ clientMetadata: ClientMetaData = this._config.clientMetadata
185
1191
  ): Promise<CognitoUser | any> {
186
- return super.confirmSignIn(user, code, mfaType, clientMetadata);
1192
+ if (!code) {
1193
+ return this.rejectAuthError(AuthErrorTypes.EmptyCode);
1194
+ }
1195
+
1196
+ const that = this;
1197
+ return new Promise((resolve, reject) => {
1198
+ user.sendMFACode(
1199
+ code,
1200
+ {
1201
+ onSuccess: async session => {
1202
+ logger.debug(session);
1203
+ try {
1204
+ await this.Credentials.clear();
1205
+ const cred = await this.Credentials.set(session, 'session');
1206
+ logger.debug('succeed to get cognito credentials', cred);
1207
+ } catch (e) {
1208
+ logger.debug('cannot get cognito credentials', e);
1209
+ } finally {
1210
+ that.user = user;
1211
+ try {
1212
+ const currentUser = await this.currentUserPoolUser();
1213
+ user.attributes = currentUser.attributes;
1214
+ } catch (e) {
1215
+ logger.debug('cannot get updated Cognito User', e);
1216
+ }
1217
+ dispatchAuthEvent(
1218
+ 'signIn',
1219
+ user,
1220
+ `A user ${user.getUsername()} has been signed in`
1221
+ );
1222
+ resolve(user);
1223
+ }
1224
+ },
1225
+ onFailure: err => {
1226
+ logger.debug('confirm signIn failure', err);
1227
+ reject(err);
1228
+ },
1229
+ },
1230
+ mfaType,
1231
+ clientMetadata
1232
+ );
1233
+ });
187
1234
  }
188
1235
 
189
1236
  public completeNewPassword(
190
1237
  user: CognitoUser | any,
191
1238
  password: string,
192
1239
  requiredAttributes: any = {},
193
- clientMetadata?: ClientMetaData
1240
+ clientMetadata: ClientMetaData = this._config.clientMetadata
194
1241
  ): Promise<CognitoUser | any> {
195
- return super.completeNewPassword(
196
- user,
197
- password,
198
- requiredAttributes,
199
- clientMetadata
200
- );
1242
+ if (!password) {
1243
+ return this.rejectAuthError(AuthErrorTypes.EmptyPassword);
1244
+ }
1245
+
1246
+ const that = this;
1247
+ return new Promise((resolve, reject) => {
1248
+ user.completeNewPasswordChallenge(
1249
+ password,
1250
+ requiredAttributes,
1251
+ {
1252
+ onSuccess: async session => {
1253
+ logger.debug(session);
1254
+ try {
1255
+ await this.Credentials.clear();
1256
+ const cred = await this.Credentials.set(session, 'session');
1257
+ logger.debug('succeed to get cognito credentials', cred);
1258
+ } catch (e) {
1259
+ logger.debug('cannot get cognito credentials', e);
1260
+ } finally {
1261
+ that.user = user;
1262
+ dispatchAuthEvent(
1263
+ 'signIn',
1264
+ user,
1265
+ `A user ${user.getUsername()} has been signed in`
1266
+ );
1267
+ resolve(user);
1268
+ }
1269
+ },
1270
+ onFailure: err => {
1271
+ logger.debug('completeNewPassword failure', err);
1272
+ dispatchAuthEvent(
1273
+ 'completeNewPassword_failure',
1274
+ err,
1275
+ `${this.user} failed to complete the new password flow`
1276
+ );
1277
+ reject(err);
1278
+ },
1279
+ mfaRequired: (challengeName, challengeParam) => {
1280
+ logger.debug('signIn MFA required');
1281
+ user['challengeName'] = challengeName;
1282
+ user['challengeParam'] = challengeParam;
1283
+ resolve(user);
1284
+ },
1285
+ mfaSetup: (challengeName, challengeParam) => {
1286
+ logger.debug('signIn mfa setup', challengeName);
1287
+ user['challengeName'] = challengeName;
1288
+ user['challengeParam'] = challengeParam;
1289
+ resolve(user);
1290
+ },
1291
+ totpRequired: (challengeName, challengeParam) => {
1292
+ logger.debug('signIn mfa setup', challengeName);
1293
+ user['challengeName'] = challengeName;
1294
+ user['challengeParam'] = challengeParam;
1295
+ resolve(user);
1296
+ },
1297
+ },
1298
+ clientMetadata
1299
+ );
1300
+ });
201
1301
  }
202
1302
 
203
1303
  /**
@@ -208,13 +1308,23 @@ export class AuthClass extends InternalAuthClass {
208
1308
  public sendCustomChallengeAnswer(
209
1309
  user: CognitoUser | any,
210
1310
  challengeResponses: string,
211
- clientMetadata?: ClientMetaData
1311
+ clientMetadata: ClientMetaData = this._config.clientMetadata
212
1312
  ): Promise<CognitoUser | any> {
213
- return super.sendCustomChallengeAnswer(
214
- user,
215
- challengeResponses,
216
- clientMetadata
217
- );
1313
+ if (!this.userPool) {
1314
+ return this.rejectNoUserPool();
1315
+ }
1316
+ if (!challengeResponses) {
1317
+ return this.rejectAuthError(AuthErrorTypes.EmptyChallengeResponse);
1318
+ }
1319
+
1320
+ const that = this;
1321
+ return new Promise((resolve, reject) => {
1322
+ user.sendCustomChallengeAnswer(
1323
+ challengeResponses,
1324
+ this.authCallbacks(user, resolve, reject),
1325
+ clientMetadata
1326
+ );
1327
+ });
218
1328
  }
219
1329
 
220
1330
  /**
@@ -226,7 +1336,18 @@ export class AuthClass extends InternalAuthClass {
226
1336
  user: CognitoUser | any,
227
1337
  attributeNames: string[]
228
1338
  ) {
229
- return super.deleteUserAttributes(user, attributeNames);
1339
+ const that = this;
1340
+ return new Promise((resolve, reject) => {
1341
+ that.userSession(user).then(session => {
1342
+ user.deleteAttributes(attributeNames, (err, result) => {
1343
+ if (err) {
1344
+ return reject(err);
1345
+ } else {
1346
+ return resolve(result);
1347
+ }
1348
+ });
1349
+ });
1350
+ });
230
1351
  }
231
1352
 
232
1353
  /**
@@ -234,8 +1355,81 @@ export class AuthClass extends InternalAuthClass {
234
1355
  * @return {Promise}
235
1356
  **/
236
1357
  // TODO: Check return type void
237
- public deleteUser(): Promise<string | void> {
238
- return super.deleteUser();
1358
+ public async deleteUser(): Promise<string | void> {
1359
+ try {
1360
+ await this._storageSync;
1361
+ } catch (e) {
1362
+ logger.debug('Failed to sync cache info into memory', e);
1363
+ throw new Error(e);
1364
+ }
1365
+
1366
+ const isSignedInHostedUI =
1367
+ this._oAuthHandler &&
1368
+ this._storage.getItem('amplify-signin-with-hostedUI') === 'true';
1369
+
1370
+ return new Promise(async (res, rej) => {
1371
+ if (this.userPool) {
1372
+ const user = this.userPool.getCurrentUser();
1373
+
1374
+ if (!user) {
1375
+ logger.debug('Failed to get user from user pool');
1376
+ return rej(new Error('No current user.'));
1377
+ } else {
1378
+ user.getSession(async (err, session) => {
1379
+ if (err) {
1380
+ logger.debug('Failed to get the user session', err);
1381
+ if (this.isSessionInvalid(err)) {
1382
+ try {
1383
+ await this.cleanUpInvalidSession(user);
1384
+ } catch (cleanUpError) {
1385
+ rej(
1386
+ new Error(
1387
+ `Session is invalid due to: ${err.message} and failed to clean up invalid session: ${cleanUpError.message}`
1388
+ )
1389
+ );
1390
+ return;
1391
+ }
1392
+ }
1393
+ return rej(err);
1394
+ } else {
1395
+ user.deleteUser((err, result: string) => {
1396
+ if (err) {
1397
+ rej(err);
1398
+ } else {
1399
+ dispatchAuthEvent(
1400
+ 'userDeleted',
1401
+ result,
1402
+ 'The authenticated user has been deleted.'
1403
+ );
1404
+ user.signOut();
1405
+ this.user = null;
1406
+ try {
1407
+ this.cleanCachedItems(); // clean aws credentials
1408
+ } catch (e) {
1409
+ // TODO: change to rejects in refactor
1410
+ logger.debug('failed to clear cached items');
1411
+ }
1412
+
1413
+ if (isSignedInHostedUI) {
1414
+ this.oAuthSignOutRedirect(res, rej);
1415
+ } else {
1416
+ dispatchAuthEvent(
1417
+ 'signOut',
1418
+ this.user,
1419
+ `A user has been signed out`
1420
+ );
1421
+ res(result);
1422
+ }
1423
+ }
1424
+ });
1425
+ }
1426
+ });
1427
+ }
1428
+ } else {
1429
+ logger.debug('no Congito User pool');
1430
+ rej(new Error('Cognito User pool does not exist'));
1431
+ }
1432
+ });
239
1433
  }
240
1434
 
241
1435
  /**
@@ -246,9 +1440,68 @@ export class AuthClass extends InternalAuthClass {
246
1440
  public updateUserAttributes(
247
1441
  user: CognitoUser | any,
248
1442
  attributes: object,
249
- clientMetadata?: ClientMetaData
1443
+ clientMetadata: ClientMetaData = this._config.clientMetadata
250
1444
  ): Promise<string> {
251
- return super.updateUserAttributes(user, attributes, clientMetadata);
1445
+ const attributeList: ICognitoUserAttributeData[] = [];
1446
+ const that = this;
1447
+ return new Promise((resolve, reject) => {
1448
+ that.userSession(user).then(session => {
1449
+ for (const key in attributes) {
1450
+ if (key !== 'sub' && key.indexOf('_verified') < 0) {
1451
+ const attr: ICognitoUserAttributeData = {
1452
+ Name: key,
1453
+ Value: attributes[key],
1454
+ };
1455
+ attributeList.push(attr);
1456
+ }
1457
+ }
1458
+ user.updateAttributes(
1459
+ attributeList,
1460
+ (err, result, details) => {
1461
+ if (err) {
1462
+ dispatchAuthEvent(
1463
+ 'updateUserAttributes_failure',
1464
+ err,
1465
+ 'Failed to update attributes'
1466
+ );
1467
+ return reject(err);
1468
+ } else {
1469
+ const attrs = this.createUpdateAttributesResultList(
1470
+ attributes as Record<string, string>,
1471
+ details?.CodeDeliveryDetailsList
1472
+ );
1473
+ dispatchAuthEvent(
1474
+ 'updateUserAttributes',
1475
+ attrs,
1476
+ 'Attributes successfully updated'
1477
+ );
1478
+ return resolve(result);
1479
+ }
1480
+ },
1481
+ clientMetadata
1482
+ );
1483
+ });
1484
+ });
1485
+ }
1486
+
1487
+ private createUpdateAttributesResultList(
1488
+ attributes: Record<string, string>,
1489
+ codeDeliveryDetailsList?: CodeDeliveryDetails[]
1490
+ ): Record<string, string> {
1491
+ const attrs = {};
1492
+ Object.keys(attributes).forEach(key => {
1493
+ attrs[key] = {
1494
+ isUpdated: true,
1495
+ };
1496
+ const codeDeliveryDetails = codeDeliveryDetailsList?.find(
1497
+ value => value.AttributeName === key
1498
+ );
1499
+ if (codeDeliveryDetails) {
1500
+ attrs[key].isUpdated = false;
1501
+ attrs[key].codeDeliveryDetails = codeDeliveryDetails;
1502
+ }
1503
+ });
1504
+ return attrs;
252
1505
  }
253
1506
 
254
1507
  /**
@@ -259,11 +1512,137 @@ export class AuthClass extends InternalAuthClass {
259
1512
  public userAttributes(
260
1513
  user: CognitoUser | any
261
1514
  ): Promise<CognitoUserAttribute[]> {
262
- return super.userAttributes(user);
1515
+ return new Promise((resolve, reject) => {
1516
+ this.userSession(user).then(session => {
1517
+ user.getUserAttributes((err, attributes) => {
1518
+ if (err) {
1519
+ reject(err);
1520
+ } else {
1521
+ resolve(attributes);
1522
+ }
1523
+ });
1524
+ });
1525
+ });
263
1526
  }
264
1527
 
265
1528
  public verifiedContact(user: CognitoUser | any) {
266
- return super.verifiedContact(user);
1529
+ const that = this;
1530
+ return this.userAttributes(user).then(attributes => {
1531
+ const attrs = that.attributesToObject(attributes);
1532
+ const unverified = {};
1533
+ const verified = {};
1534
+ if (attrs['email']) {
1535
+ if (attrs['email_verified']) {
1536
+ verified['email'] = attrs['email'];
1537
+ } else {
1538
+ unverified['email'] = attrs['email'];
1539
+ }
1540
+ }
1541
+ if (attrs['phone_number']) {
1542
+ if (attrs['phone_number_verified']) {
1543
+ verified['phone_number'] = attrs['phone_number'];
1544
+ } else {
1545
+ unverified['phone_number'] = attrs['phone_number'];
1546
+ }
1547
+ }
1548
+ return {
1549
+ verified,
1550
+ unverified,
1551
+ };
1552
+ });
1553
+ }
1554
+
1555
+ private isErrorWithMessage(err: any): err is { message: string } {
1556
+ return (
1557
+ typeof err === 'object' &&
1558
+ Object.prototype.hasOwnProperty.call(err, 'message')
1559
+ );
1560
+ }
1561
+
1562
+ // Session revoked by another app
1563
+ private isTokenRevokedError(
1564
+ err: any
1565
+ ): err is { message: 'Access Token has been revoked' } {
1566
+ return (
1567
+ this.isErrorWithMessage(err) &&
1568
+ err.message === 'Access Token has been revoked'
1569
+ );
1570
+ }
1571
+
1572
+ private isRefreshTokenRevokedError(
1573
+ err: any
1574
+ ): err is { message: 'Refresh Token has been revoked' } {
1575
+ return (
1576
+ this.isErrorWithMessage(err) &&
1577
+ err.message === 'Refresh Token has been revoked'
1578
+ );
1579
+ }
1580
+
1581
+ private isUserDisabledError(
1582
+ err: any
1583
+ ): err is { message: 'User is disabled.' } {
1584
+ return this.isErrorWithMessage(err) && err.message === 'User is disabled.';
1585
+ }
1586
+
1587
+ private isUserDoesNotExistError(
1588
+ err: any
1589
+ ): err is { message: 'User does not exist.' } {
1590
+ return (
1591
+ this.isErrorWithMessage(err) && err.message === 'User does not exist.'
1592
+ );
1593
+ }
1594
+
1595
+ private isRefreshTokenExpiredError(
1596
+ err: any
1597
+ ): err is { message: 'Refresh Token has expired' } {
1598
+ return (
1599
+ this.isErrorWithMessage(err) &&
1600
+ err.message === 'Refresh Token has expired'
1601
+ );
1602
+ }
1603
+
1604
+ private isPasswordResetRequiredError(
1605
+ err: any
1606
+ ): err is { message: 'Password reset required for the user' } {
1607
+ return (
1608
+ this.isErrorWithMessage(err) &&
1609
+ err.message === 'Password reset required for the user'
1610
+ );
1611
+ }
1612
+
1613
+ private isSignedInHostedUI() {
1614
+ return (
1615
+ this._oAuthHandler &&
1616
+ this._storage.getItem('amplify-signin-with-hostedUI') === 'true'
1617
+ );
1618
+ }
1619
+
1620
+ private isSessionInvalid(err: any) {
1621
+ return (
1622
+ this.isUserDisabledError(err) ||
1623
+ this.isUserDoesNotExistError(err) ||
1624
+ this.isTokenRevokedError(err) ||
1625
+ this.isRefreshTokenRevokedError(err) ||
1626
+ this.isRefreshTokenExpiredError(err) ||
1627
+ this.isPasswordResetRequiredError(err)
1628
+ );
1629
+ }
1630
+
1631
+ private async cleanUpInvalidSession(user: CognitoUser) {
1632
+ user.signOut();
1633
+ this.user = null;
1634
+ try {
1635
+ await this.cleanCachedItems(); // clean aws credentials
1636
+ } catch (e) {
1637
+ logger.debug('failed to clear cached items');
1638
+ }
1639
+ if (this.isSignedInHostedUI()) {
1640
+ return new Promise((res, rej) => {
1641
+ this.oAuthSignOutRedirect(res, rej);
1642
+ });
1643
+ } else {
1644
+ dispatchAuthEvent('signOut', this.user, `A user has been signed out`);
1645
+ }
267
1646
  }
268
1647
 
269
1648
  /**
@@ -273,7 +1652,128 @@ export class AuthClass extends InternalAuthClass {
273
1652
  public currentUserPoolUser(
274
1653
  params?: CurrentUserOpts
275
1654
  ): Promise<CognitoUser | any> {
276
- return super.currentUserPoolUser(params);
1655
+ if (!this.userPool) {
1656
+ return this.rejectNoUserPool();
1657
+ }
1658
+
1659
+ return new Promise((res, rej) => {
1660
+ this._storageSync
1661
+ .then(async () => {
1662
+ if (this.isOAuthInProgress()) {
1663
+ logger.debug('OAuth signIn in progress, waiting for resolution...');
1664
+
1665
+ await new Promise(res => {
1666
+ const timeoutId = setTimeout(() => {
1667
+ logger.debug('OAuth signIn in progress timeout');
1668
+
1669
+ Hub.remove('auth', hostedUISignCallback);
1670
+
1671
+ res();
1672
+ }, OAUTH_FLOW_MS_TIMEOUT);
1673
+
1674
+ Hub.listen('auth', hostedUISignCallback);
1675
+
1676
+ function hostedUISignCallback({ payload }) {
1677
+ const { event } = payload;
1678
+
1679
+ if (
1680
+ event === 'cognitoHostedUI' ||
1681
+ event === 'cognitoHostedUI_failure'
1682
+ ) {
1683
+ logger.debug(`OAuth signIn resolved: ${event}`);
1684
+ clearTimeout(timeoutId);
1685
+
1686
+ Hub.remove('auth', hostedUISignCallback);
1687
+
1688
+ res();
1689
+ }
1690
+ }
1691
+ });
1692
+ }
1693
+
1694
+ const user = this.userPool.getCurrentUser();
1695
+
1696
+ if (!user) {
1697
+ logger.debug('Failed to get user from user pool');
1698
+ rej('No current user');
1699
+ return;
1700
+ }
1701
+
1702
+ // refresh the session if the session expired.
1703
+ try {
1704
+ const session = await this._userSession(user);
1705
+
1706
+ // get user data from Cognito
1707
+ const bypassCache = params ? params.bypassCache : false;
1708
+
1709
+ if (bypassCache) {
1710
+ await this.Credentials.clear();
1711
+ }
1712
+
1713
+ const clientMetadata = this._config.clientMetadata;
1714
+
1715
+ // validate the token's scope first before calling this function
1716
+ const { scope = '' } = session.getAccessToken().decodePayload();
1717
+ if (scope.split(' ').includes(USER_ADMIN_SCOPE)) {
1718
+ user.getUserData(
1719
+ async (err, data) => {
1720
+ if (err) {
1721
+ logger.debug('getting user data failed', err);
1722
+ if (this.isSessionInvalid(err)) {
1723
+ try {
1724
+ await this.cleanUpInvalidSession(user);
1725
+ } catch (cleanUpError) {
1726
+ rej(
1727
+ new Error(
1728
+ `Session is invalid due to: ${err.message} and failed to clean up invalid session: ${cleanUpError.message}`
1729
+ )
1730
+ );
1731
+ return;
1732
+ }
1733
+ rej(err);
1734
+ } else {
1735
+ res(user);
1736
+ }
1737
+ return;
1738
+ }
1739
+ const preferredMFA = data.PreferredMfaSetting || 'NOMFA';
1740
+ const attributeList = [];
1741
+
1742
+ for (let i = 0; i < data.UserAttributes.length; i++) {
1743
+ const attribute = {
1744
+ Name: data.UserAttributes[i].Name,
1745
+ Value: data.UserAttributes[i].Value,
1746
+ };
1747
+ const userAttribute = new CognitoUserAttribute(attribute);
1748
+ attributeList.push(userAttribute);
1749
+ }
1750
+
1751
+ const attributes = this.attributesToObject(attributeList);
1752
+ Object.assign(user, { attributes, preferredMFA });
1753
+ return res(user);
1754
+ },
1755
+ { bypassCache, clientMetadata }
1756
+ );
1757
+ } else {
1758
+ logger.debug(
1759
+ `Unable to get the user data because the ${USER_ADMIN_SCOPE} ` +
1760
+ `is not in the scopes of the access token`
1761
+ );
1762
+ return res(user);
1763
+ }
1764
+ } catch (err) {
1765
+ rej(err);
1766
+ }
1767
+ })
1768
+ .catch(e => {
1769
+ logger.debug('Failed to sync cache info into memory', e);
1770
+ return rej(e);
1771
+ });
1772
+ });
1773
+ }
1774
+
1775
+ private isOAuthInProgress(): boolean {
1776
+ return this.oAuthFlowInProgress;
277
1777
  }
278
1778
 
279
1779
  /**
@@ -281,10 +1781,54 @@ export class AuthClass extends InternalAuthClass {
281
1781
  * @param {CurrentUserOpts} - options for getting the current user
282
1782
  * @return - A promise resolves to current authenticated CognitoUser if success
283
1783
  */
284
- public currentAuthenticatedUser(
1784
+ public async currentAuthenticatedUser(
285
1785
  params?: CurrentUserOpts
286
1786
  ): Promise<CognitoUser | any> {
287
- return super.currentAuthenticatedUser(params);
1787
+ logger.debug('getting current authenticated user');
1788
+ let federatedUser = null;
1789
+ try {
1790
+ await this._storageSync;
1791
+ } catch (e) {
1792
+ logger.debug('Failed to sync cache info into memory', e);
1793
+ throw e;
1794
+ }
1795
+
1796
+ try {
1797
+ const federatedInfo = JSON.parse(
1798
+ this._storage.getItem('aws-amplify-federatedInfo')
1799
+ );
1800
+ if (federatedInfo) {
1801
+ federatedUser = {
1802
+ ...federatedInfo.user,
1803
+ token: federatedInfo.token,
1804
+ };
1805
+ }
1806
+ } catch (e) {
1807
+ logger.debug('cannot load federated user from auth storage');
1808
+ }
1809
+
1810
+ if (federatedUser) {
1811
+ this.user = federatedUser;
1812
+ logger.debug('get current authenticated federated user', this.user);
1813
+ return this.user;
1814
+ } else {
1815
+ logger.debug('get current authenticated userpool user');
1816
+ let user = null;
1817
+ try {
1818
+ user = await this.currentUserPoolUser(params);
1819
+ } catch (e) {
1820
+ if (e === 'No userPool') {
1821
+ logger.error(
1822
+ 'Cannot get the current user because the user pool is missing. ' +
1823
+ 'Please make sure the Auth module is configured with a valid Cognito User Pool ID'
1824
+ );
1825
+ }
1826
+ logger.debug('The user is not authenticated by the error', e);
1827
+ return Promise.reject('The user is not authenticated');
1828
+ }
1829
+ this.user = user;
1830
+ return this.user;
1831
+ }
288
1832
  }
289
1833
 
290
1834
  /**
@@ -292,7 +1836,88 @@ export class AuthClass extends InternalAuthClass {
292
1836
  * @return - A promise resolves to session object if success
293
1837
  */
294
1838
  public currentSession(): Promise<CognitoUserSession> {
295
- return super.currentSession();
1839
+ const that = this;
1840
+ logger.debug('Getting current session');
1841
+ // Purposely not calling the reject method here because we don't need a console error
1842
+ if (!this.userPool) {
1843
+ return Promise.reject(new Error('No User Pool in the configuration.'));
1844
+ }
1845
+
1846
+ return new Promise((res, rej) => {
1847
+ that
1848
+ .currentUserPoolUser()
1849
+ .then(user => {
1850
+ that
1851
+ .userSession(user)
1852
+ .then(session => {
1853
+ res(session);
1854
+ return;
1855
+ })
1856
+ .catch(e => {
1857
+ logger.debug('Failed to get the current session', e);
1858
+ rej(e);
1859
+ return;
1860
+ });
1861
+ })
1862
+ .catch(e => {
1863
+ logger.debug('Failed to get the current user', e);
1864
+ rej(e);
1865
+ return;
1866
+ });
1867
+ });
1868
+ }
1869
+
1870
+ private async _userSession(user?: CognitoUser): Promise<CognitoUserSession> {
1871
+ if (!user) {
1872
+ logger.debug('the user is null');
1873
+ return this.rejectAuthError(AuthErrorTypes.NoUserSession);
1874
+ }
1875
+ const clientMetadata = this._config.clientMetadata;
1876
+ // Debouncing the concurrent userSession calls by caching the promise.
1877
+ // This solution assumes users will always call this function with the same CognitoUser instance.
1878
+ if (this.inflightSessionPromiseCounter === 0) {
1879
+ this.inflightSessionPromise = new Promise<CognitoUserSession>(
1880
+ (res, rej) => {
1881
+ user.getSession(
1882
+ async (err, session) => {
1883
+ if (err) {
1884
+ logger.debug('Failed to get the session from user', user);
1885
+ if (this.isSessionInvalid(err)) {
1886
+ try {
1887
+ await this.cleanUpInvalidSession(user);
1888
+ } catch (cleanUpError) {
1889
+ rej(
1890
+ new Error(
1891
+ `Session is invalid due to: ${err.message} and failed to clean up invalid session: ${cleanUpError.message}`
1892
+ )
1893
+ );
1894
+ return;
1895
+ }
1896
+ }
1897
+ rej(err);
1898
+ return;
1899
+ } else {
1900
+ logger.debug('Succeed to get the user session', session);
1901
+ res(session);
1902
+ return;
1903
+ }
1904
+ },
1905
+ { clientMetadata }
1906
+ );
1907
+ }
1908
+ );
1909
+ }
1910
+ this.inflightSessionPromiseCounter++;
1911
+
1912
+ try {
1913
+ const userSession = await this.inflightSessionPromise;
1914
+ // Set private member. Avoid user.setSignInUserSession() to prevent excessive localstorage refresh.
1915
+ // @ts-ignore
1916
+ user.signInUserSession = userSession;
1917
+ return userSession!;
1918
+ } finally {
1919
+ this.inflightSessionPromiseCounter--;
1920
+ }
296
1921
  }
297
1922
 
298
1923
  /**
@@ -301,19 +1926,52 @@ export class AuthClass extends InternalAuthClass {
301
1926
  * @return - A promise resolves to the session
302
1927
  */
303
1928
  public userSession(user): Promise<CognitoUserSession> {
304
- return super.userSession(user);
1929
+ return this._userSession(user);
305
1930
  }
306
1931
 
307
1932
  /**
308
1933
  * Get authenticated credentials of current user.
309
1934
  * @return - A promise resolves to be current user's credentials
310
1935
  */
311
- public currentUserCredentials(): Promise<ICredentials> {
312
- return super.currentUserCredentials();
1936
+ public async currentUserCredentials(): Promise<ICredentials> {
1937
+ logger.debug('Getting current user credentials');
1938
+
1939
+ try {
1940
+ await this._storageSync;
1941
+ } catch (e) {
1942
+ logger.debug('Failed to sync cache info into memory', e);
1943
+ throw e;
1944
+ }
1945
+
1946
+ // first to check whether there is federation info in the auth storage
1947
+ let federatedInfo = null;
1948
+ try {
1949
+ federatedInfo = JSON.parse(
1950
+ this._storage.getItem('aws-amplify-federatedInfo')
1951
+ );
1952
+ } catch (e) {
1953
+ logger.debug('failed to get or parse item aws-amplify-federatedInfo', e);
1954
+ }
1955
+
1956
+ if (federatedInfo) {
1957
+ // refresh the jwt token here if necessary
1958
+ return this.Credentials.refreshFederatedToken(federatedInfo);
1959
+ } else {
1960
+ return this.currentSession()
1961
+ .then(session => {
1962
+ logger.debug('getting session success', session);
1963
+ return this.Credentials.set(session, 'session');
1964
+ })
1965
+ .catch(() => {
1966
+ logger.debug('getting guest credentials');
1967
+ return this.Credentials.set(null, 'guest');
1968
+ });
1969
+ }
313
1970
  }
314
1971
 
315
1972
  public currentCredentials(): Promise<ICredentials> {
316
- return super.currentCredentials();
1973
+ logger.debug('getting current credentials');
1974
+ return this.Credentials.get();
317
1975
  }
318
1976
 
319
1977
  /**
@@ -325,9 +1983,22 @@ export class AuthClass extends InternalAuthClass {
325
1983
  public verifyUserAttribute(
326
1984
  user: CognitoUser | any,
327
1985
  attr: string,
328
- clientMetadata?: ClientMetaData
1986
+ clientMetadata: ClientMetaData = this._config.clientMetadata
329
1987
  ): Promise<void> {
330
- return super.verifyUserAttribute(user, attr, clientMetadata);
1988
+ return new Promise((resolve, reject) => {
1989
+ user.getAttributeVerificationCode(
1990
+ attr,
1991
+ {
1992
+ onSuccess(success) {
1993
+ return resolve(success);
1994
+ },
1995
+ onFailure(err) {
1996
+ return reject(err);
1997
+ },
1998
+ },
1999
+ clientMetadata
2000
+ );
2001
+ });
331
2002
  }
332
2003
 
333
2004
  /**
@@ -342,11 +2013,29 @@ export class AuthClass extends InternalAuthClass {
342
2013
  attr: string,
343
2014
  code: string
344
2015
  ): Promise<string> {
345
- return super.verifyUserAttributeSubmit(user, attr, code);
2016
+ if (!code) {
2017
+ return this.rejectAuthError(AuthErrorTypes.EmptyCode);
2018
+ }
2019
+
2020
+ return new Promise((resolve, reject) => {
2021
+ user.verifyAttribute(attr, code, {
2022
+ onSuccess(data) {
2023
+ resolve(data);
2024
+ return;
2025
+ },
2026
+ onFailure(err) {
2027
+ reject(err);
2028
+ return;
2029
+ },
2030
+ });
2031
+ });
346
2032
  }
347
2033
 
348
2034
  public verifyCurrentUserAttribute(attr: string): Promise<void> {
349
- return super.verifyCurrentUserAttribute(attr);
2035
+ const that = this;
2036
+ return that
2037
+ .currentUserPoolUser()
2038
+ .then(user => that.verifyUserAttribute(user, attr));
350
2039
  }
351
2040
 
352
2041
  /**
@@ -359,7 +2048,105 @@ export class AuthClass extends InternalAuthClass {
359
2048
  attr: string,
360
2049
  code: string
361
2050
  ): Promise<string> {
362
- return super.verifyCurrentUserAttributeSubmit(attr, code);
2051
+ const that = this;
2052
+ return that
2053
+ .currentUserPoolUser()
2054
+ .then(user => that.verifyUserAttributeSubmit(user, attr, code));
2055
+ }
2056
+
2057
+ private async cognitoIdentitySignOut(
2058
+ opts: SignOutOpts,
2059
+ user: CognitoUser | any
2060
+ ) {
2061
+ try {
2062
+ await this._storageSync;
2063
+ } catch (e) {
2064
+ logger.debug('Failed to sync cache info into memory', e);
2065
+ throw e;
2066
+ }
2067
+
2068
+ const isSignedInHostedUI =
2069
+ this._oAuthHandler &&
2070
+ this._storage.getItem('amplify-signin-with-hostedUI') === 'true';
2071
+
2072
+ return new Promise((res, rej) => {
2073
+ if (opts && opts.global) {
2074
+ logger.debug('user global sign out', user);
2075
+ // in order to use global signout
2076
+ // we must validate the user as an authenticated user by using getSession
2077
+ const clientMetadata = this._config.clientMetadata; // TODO: verify behavior if this is override during signIn
2078
+
2079
+ user.getSession(
2080
+ async (err, result) => {
2081
+ if (err) {
2082
+ logger.debug('failed to get the user session', err);
2083
+ if (this.isSessionInvalid(err)) {
2084
+ try {
2085
+ await this.cleanUpInvalidSession(user);
2086
+ } catch (cleanUpError) {
2087
+ rej(
2088
+ new Error(
2089
+ `Session is invalid due to: ${err.message} and failed to clean up invalid session: ${cleanUpError.message}`
2090
+ )
2091
+ );
2092
+ return;
2093
+ }
2094
+ }
2095
+ return rej(err);
2096
+ }
2097
+ user.globalSignOut({
2098
+ onSuccess: data => {
2099
+ logger.debug('global sign out success');
2100
+ if (isSignedInHostedUI) {
2101
+ this.oAuthSignOutRedirect(res, rej);
2102
+ } else {
2103
+ return res();
2104
+ }
2105
+ },
2106
+ onFailure: err => {
2107
+ logger.debug('global sign out failed', err);
2108
+ return rej(err);
2109
+ },
2110
+ });
2111
+ },
2112
+ { clientMetadata }
2113
+ );
2114
+ } else {
2115
+ logger.debug('user sign out', user);
2116
+ user.signOut(() => {
2117
+ if (isSignedInHostedUI) {
2118
+ this.oAuthSignOutRedirect(res, rej);
2119
+ } else {
2120
+ return res();
2121
+ }
2122
+ });
2123
+ }
2124
+ });
2125
+ }
2126
+
2127
+ private oAuthSignOutRedirect(
2128
+ resolve: () => void,
2129
+ reject: (reason?: any) => void
2130
+ ) {
2131
+ const { isBrowser } = browserOrNode();
2132
+
2133
+ if (isBrowser) {
2134
+ this.oAuthSignOutRedirectOrReject(reject);
2135
+ } else {
2136
+ this.oAuthSignOutAndResolve(resolve);
2137
+ }
2138
+ }
2139
+
2140
+ private oAuthSignOutAndResolve(resolve: () => void) {
2141
+ this._oAuthHandler.signOut();
2142
+ resolve();
2143
+ }
2144
+
2145
+ private oAuthSignOutRedirectOrReject(reject: (reason?: any) => void) {
2146
+ this._oAuthHandler.signOut(); // this method redirects url
2147
+
2148
+ // App should be redirected to another url otherwise it will reject
2149
+ setTimeout(() => reject(Error('Signout timeout fail')), 3000);
363
2150
  }
364
2151
 
365
2152
  /**
@@ -367,8 +2154,37 @@ export class AuthClass extends InternalAuthClass {
367
2154
  * @
368
2155
  * @return - A promise resolved if success
369
2156
  */
370
- public signOut(opts?: SignOutOpts): Promise<any> {
371
- return super.signOut(opts);
2157
+ public async signOut(opts?: SignOutOpts): Promise<any> {
2158
+ try {
2159
+ await this.cleanCachedItems();
2160
+ } catch (e) {
2161
+ logger.debug('failed to clear cached items');
2162
+ }
2163
+
2164
+ if (this.userPool) {
2165
+ const user = this.userPool.getCurrentUser();
2166
+ if (user) {
2167
+ await this.cognitoIdentitySignOut(opts, user);
2168
+ } else {
2169
+ logger.debug('no current Cognito user');
2170
+ }
2171
+ } else {
2172
+ logger.debug('no Cognito User pool');
2173
+ }
2174
+
2175
+ /**
2176
+ * Note for future refactor - no reliable way to get username with
2177
+ * Cognito User Pools vs Identity when federating with Social Providers
2178
+ * This is why we need a well structured session object that can be inspected
2179
+ * and information passed back in the message below for Hub dispatch
2180
+ */
2181
+ dispatchAuthEvent('signOut', this.user, `A user has been signed out`);
2182
+ this.user = null;
2183
+ }
2184
+
2185
+ private async cleanCachedItems() {
2186
+ // clear cognito cached item
2187
+ await this.Credentials.clear();
372
2188
  }
373
2189
 
374
2190
  /**
@@ -382,9 +2198,25 @@ export class AuthClass extends InternalAuthClass {
382
2198
  user: CognitoUser | any,
383
2199
  oldPassword: string,
384
2200
  newPassword: string,
385
- clientMetadata?: ClientMetaData
2201
+ clientMetadata: ClientMetaData = this._config.clientMetadata
386
2202
  ): Promise<'SUCCESS'> {
387
- return super.changePassword(user, oldPassword, newPassword, clientMetadata);
2203
+ return new Promise((resolve, reject) => {
2204
+ this.userSession(user).then(session => {
2205
+ user.changePassword(
2206
+ oldPassword,
2207
+ newPassword,
2208
+ (err, data) => {
2209
+ if (err) {
2210
+ logger.debug('change password failure', err);
2211
+ return reject(err);
2212
+ } else {
2213
+ return resolve(data);
2214
+ }
2215
+ },
2216
+ clientMetadata
2217
+ );
2218
+ });
2219
+ });
388
2220
  }
389
2221
 
390
2222
  /**
@@ -394,9 +2226,46 @@ export class AuthClass extends InternalAuthClass {
394
2226
  */
395
2227
  public forgotPassword(
396
2228
  username: string,
397
- clientMetadata?: ClientMetaData
2229
+ clientMetadata: ClientMetaData = this._config.clientMetadata
398
2230
  ): Promise<any> {
399
- return super.forgotPassword(username, clientMetadata);
2231
+ if (!this.userPool) {
2232
+ return this.rejectNoUserPool();
2233
+ }
2234
+ if (!username) {
2235
+ return this.rejectAuthError(AuthErrorTypes.EmptyUsername);
2236
+ }
2237
+
2238
+ const user = this.createCognitoUser(username);
2239
+ return new Promise((resolve, reject) => {
2240
+ user.forgotPassword(
2241
+ {
2242
+ onSuccess: () => {
2243
+ resolve();
2244
+ return;
2245
+ },
2246
+ onFailure: err => {
2247
+ logger.debug('forgot password failure', err);
2248
+ dispatchAuthEvent(
2249
+ 'forgotPassword_failure',
2250
+ err,
2251
+ `${username} forgotPassword failed`
2252
+ );
2253
+ reject(err);
2254
+ return;
2255
+ },
2256
+ inputVerificationCode: data => {
2257
+ dispatchAuthEvent(
2258
+ 'forgotPassword',
2259
+ user,
2260
+ `${username} has initiated forgot password flow`
2261
+ );
2262
+ resolve(data);
2263
+ return;
2264
+ },
2265
+ },
2266
+ clientMetadata
2267
+ );
2268
+ });
400
2269
  }
401
2270
 
402
2271
  /**
@@ -410,9 +2279,49 @@ export class AuthClass extends InternalAuthClass {
410
2279
  username: string,
411
2280
  code: string,
412
2281
  password: string,
413
- clientMetadata?: ClientMetaData
2282
+ clientMetadata: ClientMetaData = this._config.clientMetadata
414
2283
  ): Promise<string> {
415
- return super.forgotPasswordSubmit(username, code, password, clientMetadata);
2284
+ if (!this.userPool) {
2285
+ return this.rejectNoUserPool();
2286
+ }
2287
+ if (!username) {
2288
+ return this.rejectAuthError(AuthErrorTypes.EmptyUsername);
2289
+ }
2290
+ if (!code) {
2291
+ return this.rejectAuthError(AuthErrorTypes.EmptyCode);
2292
+ }
2293
+ if (!password) {
2294
+ return this.rejectAuthError(AuthErrorTypes.EmptyPassword);
2295
+ }
2296
+
2297
+ const user = this.createCognitoUser(username);
2298
+ return new Promise((resolve, reject) => {
2299
+ user.confirmPassword(
2300
+ code,
2301
+ password,
2302
+ {
2303
+ onSuccess: success => {
2304
+ dispatchAuthEvent(
2305
+ 'forgotPasswordSubmit',
2306
+ user,
2307
+ `${username} forgotPasswordSubmit successful`
2308
+ );
2309
+ resolve(success);
2310
+ return;
2311
+ },
2312
+ onFailure: err => {
2313
+ dispatchAuthEvent(
2314
+ 'forgotPasswordSubmit_failure',
2315
+ err,
2316
+ `${username} forgotPasswordSubmit failed`
2317
+ );
2318
+ reject(err);
2319
+ return;
2320
+ },
2321
+ },
2322
+ clientMetadata
2323
+ );
2324
+ });
416
2325
  }
417
2326
 
418
2327
  /**
@@ -420,22 +2329,60 @@ export class AuthClass extends InternalAuthClass {
420
2329
  * @async
421
2330
  * @return {Object }- current User's information
422
2331
  */
423
- public currentUserInfo() {
424
- return super.currentUserInfo();
2332
+ public async currentUserInfo() {
2333
+ const source = this.Credentials.getCredSource();
2334
+
2335
+ if (!source || source === 'aws' || source === 'userPool') {
2336
+ const user = await this.currentUserPoolUser().catch(err =>
2337
+ logger.error(err)
2338
+ );
2339
+ if (!user) {
2340
+ return null;
2341
+ }
2342
+
2343
+ try {
2344
+ const attributes = await this.userAttributes(user);
2345
+ const userAttrs: object = this.attributesToObject(attributes);
2346
+ let credentials = null;
2347
+ try {
2348
+ credentials = await this.currentCredentials();
2349
+ } catch (e) {
2350
+ logger.debug(
2351
+ 'Failed to retrieve credentials while getting current user info',
2352
+ e
2353
+ );
2354
+ }
2355
+
2356
+ const info = {
2357
+ id: credentials ? credentials.identityId : undefined,
2358
+ username: user.getUsername(),
2359
+ attributes: userAttrs,
2360
+ };
2361
+ return info;
2362
+ } catch (err) {
2363
+ logger.error('currentUserInfo error', err);
2364
+ return {};
2365
+ }
2366
+ }
2367
+
2368
+ if (source === 'federated') {
2369
+ const user = this.user;
2370
+ return user ? user : {};
2371
+ }
425
2372
  }
426
2373
 
427
- public federatedSignIn(
2374
+ public async federatedSignIn(
428
2375
  options?: FederatedSignInOptions
429
2376
  ): Promise<ICredentials>;
430
- public federatedSignIn(
2377
+ public async federatedSignIn(
431
2378
  provider: LegacyProvider,
432
2379
  response: FederatedResponse,
433
2380
  user: FederatedUser
434
2381
  ): Promise<ICredentials>;
435
- public federatedSignIn(
2382
+ public async federatedSignIn(
436
2383
  options?: FederatedSignInOptionsCustom
437
2384
  ): Promise<ICredentials>;
438
- public federatedSignIn(
2385
+ public async federatedSignIn(
439
2386
  providerOrOptions:
440
2387
  | LegacyProvider
441
2388
  | FederatedSignInOptions
@@ -443,7 +2390,230 @@ export class AuthClass extends InternalAuthClass {
443
2390
  response?: FederatedResponse,
444
2391
  user?: FederatedUser
445
2392
  ): Promise<ICredentials> {
446
- return super.federatedSignIn(providerOrOptions, response, user);
2393
+ if (!this._config.identityPoolId && !this._config.userPoolId) {
2394
+ throw new Error(
2395
+ `Federation requires either a User Pool or Identity Pool in config`
2396
+ );
2397
+ }
2398
+
2399
+ // Ensure backwards compatability
2400
+ if (typeof providerOrOptions === 'undefined') {
2401
+ if (this._config.identityPoolId && !this._config.userPoolId) {
2402
+ throw new Error(
2403
+ `Federation with Identity Pools requires tokens passed as arguments`
2404
+ );
2405
+ }
2406
+ }
2407
+
2408
+ if (
2409
+ isFederatedSignInOptions(providerOrOptions) ||
2410
+ isFederatedSignInOptionsCustom(providerOrOptions) ||
2411
+ hasCustomState(providerOrOptions) ||
2412
+ typeof providerOrOptions === 'undefined'
2413
+ ) {
2414
+ const options = providerOrOptions || {
2415
+ provider: CognitoHostedUIIdentityProvider.Cognito,
2416
+ };
2417
+ const provider = isFederatedSignInOptions(options)
2418
+ ? options.provider
2419
+ : (options as FederatedSignInOptionsCustom).customProvider;
2420
+
2421
+ const customState = isFederatedSignInOptions(options)
2422
+ ? options.customState
2423
+ : (options as FederatedSignInOptionsCustom).customState;
2424
+
2425
+ if (this._config.userPoolId) {
2426
+ const client_id = isCognitoHostedOpts(this._config.oauth)
2427
+ ? this._config.userPoolWebClientId
2428
+ : this._config.oauth.clientID;
2429
+ /*Note: Invenstigate automatically adding trailing slash */
2430
+ const redirect_uri = isCognitoHostedOpts(this._config.oauth)
2431
+ ? this._config.oauth.redirectSignIn
2432
+ : this._config.oauth.redirectUri;
2433
+
2434
+ this._oAuthHandler.oauthSignIn(
2435
+ this._config.oauth.responseType,
2436
+ this._config.oauth.domain,
2437
+ redirect_uri,
2438
+ client_id,
2439
+ provider,
2440
+ customState
2441
+ );
2442
+ }
2443
+ } else {
2444
+ const provider = providerOrOptions;
2445
+ // To check if the user is already logged in
2446
+ try {
2447
+ const loggedInUser = JSON.stringify(
2448
+ JSON.parse(this._storage.getItem('aws-amplify-federatedInfo')).user
2449
+ );
2450
+ if (loggedInUser) {
2451
+ logger.warn(`There is already a signed in user: ${loggedInUser} in your app.
2452
+ You should not call Auth.federatedSignIn method again as it may cause unexpected behavior.`);
2453
+ }
2454
+ } catch (e) {}
2455
+
2456
+ const { token, identity_id, expires_at } = response;
2457
+ // Because this.Credentials.set would update the user info with identity id
2458
+ // So we need to retrieve the user again.
2459
+ const credentials = await this.Credentials.set(
2460
+ { provider, token, identity_id, user, expires_at },
2461
+ 'federation'
2462
+ );
2463
+ const currentUser = await this.currentAuthenticatedUser();
2464
+ dispatchAuthEvent(
2465
+ 'signIn',
2466
+ currentUser,
2467
+ `A user ${currentUser.username} has been signed in`
2468
+ );
2469
+ logger.debug('federated sign in credentials', credentials);
2470
+ return credentials;
2471
+ }
2472
+ }
2473
+
2474
+ /**
2475
+ * Used to complete the OAuth flow with or without the Cognito Hosted UI
2476
+ * @param {String} URL - optional parameter for customers to pass in the response URL
2477
+ */
2478
+ private async _handleAuthResponse(URL?: string) {
2479
+ if (this.oAuthFlowInProgress) {
2480
+ logger.debug(`Skipping URL ${URL} current flow in progress`);
2481
+ return;
2482
+ }
2483
+
2484
+ try {
2485
+ this.oAuthFlowInProgress = true;
2486
+ if (!this._config.userPoolId) {
2487
+ throw new Error(
2488
+ `OAuth responses require a User Pool defined in config`
2489
+ );
2490
+ }
2491
+
2492
+ dispatchAuthEvent(
2493
+ 'parsingCallbackUrl',
2494
+ { url: URL },
2495
+ `The callback url is being parsed`
2496
+ );
2497
+
2498
+ const currentUrl =
2499
+ URL || (browserOrNode().isBrowser ? window.location.href : '');
2500
+
2501
+ const hasCodeOrError = !!(parse(currentUrl).query || '')
2502
+ .split('&')
2503
+ .map(entry => entry.split('='))
2504
+ .find(([k]) => k === 'code' || k === 'error');
2505
+
2506
+ const hasTokenOrError = !!(parse(currentUrl).hash || '#')
2507
+ .substr(1)
2508
+ .split('&')
2509
+ .map(entry => entry.split('='))
2510
+ .find(([k]) => k === 'access_token' || k === 'error');
2511
+
2512
+ if (hasCodeOrError || hasTokenOrError) {
2513
+ this._storage.setItem('amplify-redirected-from-hosted-ui', 'true');
2514
+ try {
2515
+ const { accessToken, idToken, refreshToken, state } =
2516
+ await this._oAuthHandler.handleAuthResponse(currentUrl);
2517
+ const session = new CognitoUserSession({
2518
+ IdToken: new CognitoIdToken({ IdToken: idToken }),
2519
+ RefreshToken: new CognitoRefreshToken({
2520
+ RefreshToken: refreshToken,
2521
+ }),
2522
+ AccessToken: new CognitoAccessToken({
2523
+ AccessToken: accessToken,
2524
+ }),
2525
+ });
2526
+
2527
+ let credentials;
2528
+ // Get AWS Credentials & store if Identity Pool is defined
2529
+ if (this._config.identityPoolId) {
2530
+ credentials = await this.Credentials.set(session, 'session');
2531
+ logger.debug('AWS credentials', credentials);
2532
+ }
2533
+
2534
+ /*
2535
+ Prior to the request we do sign the custom state along with the state we set. This check will verify
2536
+ if there is a dash indicated when setting custom state from the request. If a dash is contained
2537
+ then there is custom state present on the state string.
2538
+ */
2539
+ const isCustomStateIncluded = /-/.test(state);
2540
+
2541
+ /*
2542
+ The following is to create a user for the Cognito Identity SDK to store the tokens
2543
+ When we remove this SDK later that logic will have to be centralized in our new version
2544
+ */
2545
+ //#region
2546
+ const currentUser = this.createCognitoUser(
2547
+ session.getIdToken().decodePayload()['cognito:username']
2548
+ );
2549
+
2550
+ // This calls cacheTokens() in Cognito SDK
2551
+ currentUser.setSignInUserSession(session);
2552
+
2553
+ if (window && typeof window.history !== 'undefined') {
2554
+ window.history.replaceState(
2555
+ {},
2556
+ null,
2557
+ (this._config.oauth as AwsCognitoOAuthOpts).redirectSignIn
2558
+ );
2559
+ }
2560
+
2561
+ dispatchAuthEvent(
2562
+ 'signIn',
2563
+ currentUser,
2564
+ `A user ${currentUser.getUsername()} has been signed in`
2565
+ );
2566
+ dispatchAuthEvent(
2567
+ 'cognitoHostedUI',
2568
+ currentUser,
2569
+ `A user ${currentUser.getUsername()} has been signed in via Cognito Hosted UI`
2570
+ );
2571
+
2572
+ if (isCustomStateIncluded) {
2573
+ const customState = state.split('-').splice(1).join('-');
2574
+
2575
+ dispatchAuthEvent(
2576
+ 'customOAuthState',
2577
+ urlSafeDecode(customState),
2578
+ `State for user ${currentUser.getUsername()}`
2579
+ );
2580
+ }
2581
+ //#endregion
2582
+
2583
+ return credentials;
2584
+ } catch (err) {
2585
+ logger.debug('Error in cognito hosted auth response', err);
2586
+
2587
+ // Just like a successful handling of `?code`, replace the window history to "dispose" of the `code`.
2588
+ // Otherwise, reloading the page will throw errors as the `code` has already been spent.
2589
+ if (window && typeof window.history !== 'undefined') {
2590
+ window.history.replaceState(
2591
+ {},
2592
+ null,
2593
+ (this._config.oauth as AwsCognitoOAuthOpts).redirectSignIn
2594
+ );
2595
+ }
2596
+
2597
+ dispatchAuthEvent(
2598
+ 'signIn_failure',
2599
+ err,
2600
+ `The OAuth response flow failed`
2601
+ );
2602
+ dispatchAuthEvent(
2603
+ 'cognitoHostedUI_failure',
2604
+ err,
2605
+ `A failure occurred when returning to the Cognito Hosted UI`
2606
+ );
2607
+ dispatchAuthEvent(
2608
+ 'customState_failure',
2609
+ err,
2610
+ `A failure occurred when returning state`
2611
+ );
2612
+ }
2613
+ }
2614
+ } finally {
2615
+ this.oAuthFlowInProgress = false;
2616
+ }
447
2617
  }
448
2618
 
449
2619
  /**
@@ -452,21 +2622,185 @@ export class AuthClass extends InternalAuthClass {
452
2622
  * @return {Object} - Credentials
453
2623
  */
454
2624
  public essentialCredentials(credentials): ICredentials {
455
- return super.essentialCredentials(credentials);
2625
+ return {
2626
+ accessKeyId: credentials.accessKeyId,
2627
+ sessionToken: credentials.sessionToken,
2628
+ secretAccessKey: credentials.secretAccessKey,
2629
+ identityId: credentials.identityId,
2630
+ authenticated: credentials.authenticated,
2631
+ };
2632
+ }
2633
+
2634
+ private attributesToObject(attributes) {
2635
+ const obj = {};
2636
+ if (attributes) {
2637
+ attributes.map(attribute => {
2638
+ if (
2639
+ attribute.Name === 'email_verified' ||
2640
+ attribute.Name === 'phone_number_verified'
2641
+ ) {
2642
+ obj[attribute.Name] =
2643
+ this.isTruthyString(attribute.Value) || attribute.Value === true;
2644
+ } else {
2645
+ obj[attribute.Name] = attribute.Value;
2646
+ }
2647
+ });
2648
+ }
2649
+ return obj;
2650
+ }
2651
+
2652
+ private isTruthyString(value: any): boolean {
2653
+ return (
2654
+ typeof value.toLowerCase === 'function' && value.toLowerCase() === 'true'
2655
+ );
2656
+ }
2657
+
2658
+ private createCognitoUser(username: string): CognitoUser {
2659
+ const userData: ICognitoUserData = {
2660
+ Username: username,
2661
+ Pool: this.userPool,
2662
+ };
2663
+ userData.Storage = this._storage;
2664
+
2665
+ const { authenticationFlowType } = this._config;
2666
+
2667
+ const user = new CognitoUser(userData);
2668
+ if (authenticationFlowType) {
2669
+ user.setAuthenticationFlowType(authenticationFlowType);
2670
+ }
2671
+ return user;
2672
+ }
2673
+
2674
+ private _isValidAuthStorage(obj) {
2675
+ // We need to check if the obj has the functions of Storage
2676
+ return (
2677
+ !!obj &&
2678
+ typeof obj.getItem === 'function' &&
2679
+ typeof obj.setItem === 'function' &&
2680
+ typeof obj.removeItem === 'function' &&
2681
+ typeof obj.clear === 'function'
2682
+ );
2683
+ }
2684
+
2685
+ private noUserPoolErrorHandler(config: AuthOptions): AuthErrorTypes {
2686
+ if (config) {
2687
+ if (!config.userPoolId || !config.identityPoolId) {
2688
+ return AuthErrorTypes.MissingAuthConfig;
2689
+ }
2690
+ }
2691
+ return AuthErrorTypes.NoConfig;
456
2692
  }
457
2693
 
458
- public rememberDevice(): Promise<string | AuthError> {
459
- return super.rememberDevice();
2694
+ private rejectAuthError(type: AuthErrorTypes): Promise<never> {
2695
+ return Promise.reject(new AuthError(type));
460
2696
  }
461
2697
 
462
- public forgetDevice(): Promise<void> {
463
- return super.forgetDevice();
2698
+ private rejectNoUserPool(): Promise<never> {
2699
+ const type = this.noUserPoolErrorHandler(this._config);
2700
+ return Promise.reject(new NoUserPoolError(type));
464
2701
  }
465
2702
 
466
- public fetchDevices(): Promise<IAuthDevice[]> {
467
- return super.fetchDevices();
2703
+ public async rememberDevice(): Promise<string | AuthError> {
2704
+ let currUser;
2705
+
2706
+ try {
2707
+ currUser = await this.currentUserPoolUser();
2708
+ } catch (error) {
2709
+ logger.debug('The user is not authenticated by the error', error);
2710
+ return Promise.reject('The user is not authenticated');
2711
+ }
2712
+
2713
+ currUser.getCachedDeviceKeyAndPassword();
2714
+ return new Promise((res, rej) => {
2715
+ currUser.setDeviceStatusRemembered({
2716
+ onSuccess: data => {
2717
+ res(data);
2718
+ },
2719
+ onFailure: err => {
2720
+ if (err.code === 'InvalidParameterException') {
2721
+ rej(new AuthError(AuthErrorTypes.DeviceConfig));
2722
+ } else if (err.code === 'NetworkError') {
2723
+ rej(new AuthError(AuthErrorTypes.NetworkError));
2724
+ } else {
2725
+ rej(err);
2726
+ }
2727
+ },
2728
+ });
2729
+ });
2730
+ }
2731
+
2732
+ public async forgetDevice(): Promise<void> {
2733
+ let currUser;
2734
+
2735
+ try {
2736
+ currUser = await this.currentUserPoolUser();
2737
+ } catch (error) {
2738
+ logger.debug('The user is not authenticated by the error', error);
2739
+ return Promise.reject('The user is not authenticated');
2740
+ }
2741
+
2742
+ currUser.getCachedDeviceKeyAndPassword();
2743
+ return new Promise((res, rej) => {
2744
+ currUser.forgetDevice({
2745
+ onSuccess: data => {
2746
+ res(data);
2747
+ },
2748
+ onFailure: err => {
2749
+ if (err.code === 'InvalidParameterException') {
2750
+ rej(new AuthError(AuthErrorTypes.DeviceConfig));
2751
+ } else if (err.code === 'NetworkError') {
2752
+ rej(new AuthError(AuthErrorTypes.NetworkError));
2753
+ } else {
2754
+ rej(err);
2755
+ }
2756
+ },
2757
+ });
2758
+ });
2759
+ }
2760
+
2761
+ public async fetchDevices(): Promise<IAuthDevice[]> {
2762
+ let currUser;
2763
+
2764
+ try {
2765
+ currUser = await this.currentUserPoolUser();
2766
+ } catch (error) {
2767
+ logger.debug('The user is not authenticated by the error', error);
2768
+ throw new Error('The user is not authenticated');
2769
+ }
2770
+
2771
+ currUser.getCachedDeviceKeyAndPassword();
2772
+ return new Promise((res, rej) => {
2773
+ const cb = {
2774
+ onSuccess(data) {
2775
+ const deviceList: IAuthDevice[] = data.Devices.map(device => {
2776
+ const deviceName =
2777
+ device.DeviceAttributes.find(
2778
+ ({ Name }) => Name === 'device_name'
2779
+ ) || {};
2780
+
2781
+ const deviceInfo: IAuthDevice = {
2782
+ id: device.DeviceKey,
2783
+ name: deviceName.Value,
2784
+ };
2785
+ return deviceInfo;
2786
+ });
2787
+ res(deviceList);
2788
+ },
2789
+ onFailure: err => {
2790
+ if (err.code === 'InvalidParameterException') {
2791
+ rej(new AuthError(AuthErrorTypes.DeviceConfig));
2792
+ } else if (err.code === 'NetworkError') {
2793
+ rej(new AuthError(AuthErrorTypes.NetworkError));
2794
+ } else {
2795
+ rej(err);
2796
+ }
2797
+ },
2798
+ };
2799
+ currUser.listDevices(MAX_DEVICES, null, cb);
2800
+ });
468
2801
  }
469
2802
  }
470
2803
 
471
2804
  export const Auth = new AuthClass(null);
2805
+
472
2806
  Amplify.register(Auth);