@imtbl/auth 2.10.7-alpha.6 → 2.11.1-alpha.0
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/.eslintrc.cjs +1 -1
- package/dist/browser/index.js +23 -23
- package/dist/node/index.cjs +42 -41
- package/dist/node/index.js +30 -30
- package/dist/types/Auth.d.ts +1 -1
- package/dist/types/errors.d.ts +2 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/types.d.ts +2 -0
- package/jest.config.ts +27 -0
- package/jest.setup.js +4 -0
- package/package.json +14 -6
- package/src/Auth.test.ts +186 -0
- package/src/Auth.ts +22 -53
- package/src/errors.test.ts +21 -0
- package/src/errors.ts +7 -1
- package/src/index.ts +3 -1
- package/src/types.ts +2 -0
- package/tsconfig.eslint.json +6 -0
- package/tsconfig.json +4 -0
package/src/Auth.ts
CHANGED
|
@@ -42,8 +42,6 @@ import { isAccessTokenExpiredOrExpiring } from './utils/token';
|
|
|
42
42
|
import LoginPopupOverlay from './overlay/loginPopupOverlay';
|
|
43
43
|
import { LocalForageAsyncStorage } from './storage/LocalForageAsyncStorage';
|
|
44
44
|
|
|
45
|
-
const LOGIN_POPUP_CLOSED_POLLING_DURATION = 500;
|
|
46
|
-
|
|
47
45
|
const formUrlEncodedHeader = {
|
|
48
46
|
headers: {
|
|
49
47
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
@@ -206,8 +204,7 @@ export class Auth {
|
|
|
206
204
|
|
|
207
205
|
// Emit LOGGED_IN event and identify user if logged in
|
|
208
206
|
if (user) {
|
|
209
|
-
this.
|
|
210
|
-
identify({ passportId: user.profile.sub });
|
|
207
|
+
this.handleSuccessfulLogin(user);
|
|
211
208
|
}
|
|
212
209
|
|
|
213
210
|
return user;
|
|
@@ -233,8 +230,7 @@ export class Auth {
|
|
|
233
230
|
return withMetricsAsync(async () => {
|
|
234
231
|
const user = await this.loginCallbackInternal();
|
|
235
232
|
if (user) {
|
|
236
|
-
this.
|
|
237
|
-
identify({ passportId: user.profile.sub });
|
|
233
|
+
this.handleSuccessfulLogin(user);
|
|
238
234
|
}
|
|
239
235
|
return user;
|
|
240
236
|
}, 'loginCallback');
|
|
@@ -256,7 +252,7 @@ export class Auth {
|
|
|
256
252
|
* @returns Promise that resolves with the user or null if not authenticated
|
|
257
253
|
*/
|
|
258
254
|
async getUser(): Promise<User | null> {
|
|
259
|
-
return
|
|
255
|
+
return this.getUserInternal();
|
|
260
256
|
}
|
|
261
257
|
|
|
262
258
|
/**
|
|
@@ -275,7 +271,9 @@ export class Auth {
|
|
|
275
271
|
return user;
|
|
276
272
|
}
|
|
277
273
|
|
|
278
|
-
|
|
274
|
+
const loggedInUser = await this.loginWithPopup();
|
|
275
|
+
this.handleSuccessfulLogin(loggedInUser);
|
|
276
|
+
return loggedInUser;
|
|
279
277
|
}
|
|
280
278
|
|
|
281
279
|
/**
|
|
@@ -354,8 +352,7 @@ export class Auth {
|
|
|
354
352
|
async loginWithPKCEFlowCallback(authorizationCode: string, state: string): Promise<User> {
|
|
355
353
|
return withMetricsAsync(async () => {
|
|
356
354
|
const user = await this.loginWithPKCEFlowCallbackInternal(authorizationCode, state);
|
|
357
|
-
this.
|
|
358
|
-
identify({ passportId: user.profile.sub });
|
|
355
|
+
this.handleSuccessfulLogin(user);
|
|
359
356
|
return user;
|
|
360
357
|
}, 'loginWithPKCEFlowCallback');
|
|
361
358
|
}
|
|
@@ -368,8 +365,7 @@ export class Auth {
|
|
|
368
365
|
async storeTokens(tokenResponse: DeviceTokenResponse): Promise<User> {
|
|
369
366
|
return withMetricsAsync(async () => {
|
|
370
367
|
const user = await this.storeTokensInternal(tokenResponse);
|
|
371
|
-
this.
|
|
372
|
-
identify({ passportId: user.profile.sub });
|
|
368
|
+
this.handleSuccessfulLogin(user);
|
|
373
369
|
return user;
|
|
374
370
|
}, 'storeTokens');
|
|
375
371
|
}
|
|
@@ -413,6 +409,11 @@ export class Auth {
|
|
|
413
409
|
return this.config.oidcConfiguration.clientId;
|
|
414
410
|
}
|
|
415
411
|
|
|
412
|
+
private handleSuccessfulLogin(user: User): void {
|
|
413
|
+
this.eventEmitter.emit(AuthEvents.LOGGED_IN, user);
|
|
414
|
+
identify({ passportId: user.profile.sub });
|
|
415
|
+
}
|
|
416
|
+
|
|
416
417
|
private buildExtraQueryParams(
|
|
417
418
|
directLoginOptions?: DirectLoginOptions,
|
|
418
419
|
imPassportTraceId?: string,
|
|
@@ -420,7 +421,6 @@ export class Auth {
|
|
|
420
421
|
const params: Record<string, string> = {
|
|
421
422
|
...(this.userManager.settings?.extraQueryParams ?? {}),
|
|
422
423
|
rid: getDetail(Detail.RUNTIME_ID) || '',
|
|
423
|
-
third_party_a_id: '',
|
|
424
424
|
};
|
|
425
425
|
|
|
426
426
|
if (directLoginOptions) {
|
|
@@ -472,7 +472,7 @@ export class Auth {
|
|
|
472
472
|
const signinPopup = async () => {
|
|
473
473
|
const extraQueryParams = this.buildExtraQueryParams(directLoginOptionsToUse, imPassportTraceId);
|
|
474
474
|
|
|
475
|
-
|
|
475
|
+
return this.userManager.signinPopup({
|
|
476
476
|
extraQueryParams,
|
|
477
477
|
popupWindowFeatures: {
|
|
478
478
|
width: 410,
|
|
@@ -480,27 +480,6 @@ export class Auth {
|
|
|
480
480
|
},
|
|
481
481
|
popupWindowTarget,
|
|
482
482
|
});
|
|
483
|
-
|
|
484
|
-
const popupRef = window.open('', popupWindowTarget);
|
|
485
|
-
if (popupRef) {
|
|
486
|
-
const popupClosedPromise = new Promise<never>((_, reject) => {
|
|
487
|
-
const timer = setInterval(() => {
|
|
488
|
-
if (popupRef.closed) {
|
|
489
|
-
clearInterval(timer);
|
|
490
|
-
reject(new Error('Popup closed by user'));
|
|
491
|
-
}
|
|
492
|
-
}, LOGIN_POPUP_CLOSED_POLLING_DURATION);
|
|
493
|
-
|
|
494
|
-
userPromise.finally(() => {
|
|
495
|
-
clearInterval(timer);
|
|
496
|
-
popupRef.close();
|
|
497
|
-
});
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
return Promise.race([userPromise, popupClosedPromise]);
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
return userPromise;
|
|
504
483
|
};
|
|
505
484
|
|
|
506
485
|
return new Promise((resolve, reject) => {
|
|
@@ -542,8 +521,13 @@ export class Auth {
|
|
|
542
521
|
|
|
543
522
|
private static mapOidcUserToDomainModel = (oidcUser: OidcUser): User => {
|
|
544
523
|
let passport: PassportMetadata | undefined;
|
|
524
|
+
let username: string | undefined;
|
|
545
525
|
if (oidcUser.id_token) {
|
|
546
|
-
|
|
526
|
+
const idTokenPayload = jwt_decode<IdTokenPayload>(oidcUser.id_token);
|
|
527
|
+
passport = idTokenPayload?.passport;
|
|
528
|
+
if (idTokenPayload?.username) {
|
|
529
|
+
username = idTokenPayload?.username;
|
|
530
|
+
}
|
|
547
531
|
}
|
|
548
532
|
|
|
549
533
|
const user: User = {
|
|
@@ -555,6 +539,7 @@ export class Auth {
|
|
|
555
539
|
sub: oidcUser.profile.sub,
|
|
556
540
|
email: oidcUser.profile.email,
|
|
557
541
|
nickname: oidcUser.profile.nickname,
|
|
542
|
+
username,
|
|
558
543
|
},
|
|
559
544
|
};
|
|
560
545
|
if (passport?.zkevm_eth_address && passport?.zkevm_user_admin_address) {
|
|
@@ -582,29 +567,13 @@ export class Auth {
|
|
|
582
567
|
email: idTokenPayload.email,
|
|
583
568
|
nickname: idTokenPayload.nickname,
|
|
584
569
|
passport: idTokenPayload.passport,
|
|
570
|
+
...(idTokenPayload.username ? { username: idTokenPayload.username } : {}),
|
|
585
571
|
},
|
|
586
572
|
});
|
|
587
573
|
};
|
|
588
574
|
|
|
589
|
-
private static shouldUseSigninPopupCallback(): boolean {
|
|
590
|
-
try {
|
|
591
|
-
const urlParams = new URLSearchParams(window.location.search);
|
|
592
|
-
const stateParam = urlParams.get('state');
|
|
593
|
-
const localStorageKey = `oidc.${stateParam}`;
|
|
594
|
-
const localStorageValue = localStorage.getItem(localStorageKey);
|
|
595
|
-
const loginState = JSON.parse(localStorageValue || '{}');
|
|
596
|
-
return loginState?.request_type === 'si:p';
|
|
597
|
-
} catch (err) {
|
|
598
|
-
return false;
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
|
|
602
575
|
private async loginCallbackInternal(): Promise<User | undefined> {
|
|
603
576
|
return withPassportError(async () => {
|
|
604
|
-
if (Auth.shouldUseSigninPopupCallback()) {
|
|
605
|
-
await this.userManager.signinPopupCallback(undefined, true);
|
|
606
|
-
return undefined;
|
|
607
|
-
}
|
|
608
577
|
const oidcUser = await this.userManager.signinCallback();
|
|
609
578
|
if (!oidcUser) {
|
|
610
579
|
return undefined;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { isAPIError } from './errors';
|
|
2
|
+
|
|
3
|
+
describe('isAPIError', () => {
|
|
4
|
+
it('returns true when code and message fields exist', () => {
|
|
5
|
+
expect(isAPIError({ code: 'BAD_REQUEST', message: 'Invalid' })).toBe(true);
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
it.each([
|
|
9
|
+
'Not found',
|
|
10
|
+
404,
|
|
11
|
+
null,
|
|
12
|
+
undefined,
|
|
13
|
+
])('returns false for non-object value: %p', (value) => {
|
|
14
|
+
expect(isAPIError(value)).toBe(false);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('returns false when required fields are missing', () => {
|
|
18
|
+
expect(isAPIError({ code: 'BAD_REQUEST' })).toBe(false);
|
|
19
|
+
expect(isAPIError({ message: 'Invalid' })).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
});
|
package/src/errors.ts
CHANGED
|
@@ -23,10 +23,16 @@ export enum PassportErrorType {
|
|
|
23
23
|
LINK_WALLET_DUPLICATE_NONCE_ERROR = 'LINK_WALLET_DUPLICATE_NONCE_ERROR',
|
|
24
24
|
LINK_WALLET_GENERIC_ERROR = 'LINK_WALLET_GENERIC_ERROR',
|
|
25
25
|
SERVICE_UNAVAILABLE_ERROR = 'SERVICE_UNAVAILABLE_ERROR',
|
|
26
|
+
TRANSACTION_REJECTED = 'TRANSACTION_REJECTED',
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
export function isAPIError(error: any): error is imx.APIError {
|
|
29
|
-
return
|
|
30
|
+
return (
|
|
31
|
+
typeof error === 'object'
|
|
32
|
+
&& error !== null
|
|
33
|
+
&& 'code' in error
|
|
34
|
+
&& 'message' in error
|
|
35
|
+
);
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
export class PassportError extends Error {
|
package/src/index.ts
CHANGED
|
@@ -29,4 +29,6 @@ export {
|
|
|
29
29
|
export { default as TypedEventEmitter } from './utils/typedEventEmitter';
|
|
30
30
|
|
|
31
31
|
// Export errors
|
|
32
|
-
export {
|
|
32
|
+
export {
|
|
33
|
+
PassportError, PassportErrorType, withPassportError, isAPIError,
|
|
34
|
+
} from './errors';
|
package/src/types.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type UserProfile = {
|
|
|
9
9
|
email?: string;
|
|
10
10
|
nickname?: string;
|
|
11
11
|
sub: string;
|
|
12
|
+
username?: string;
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
export enum RollupType {
|
|
@@ -91,6 +92,7 @@ export type TokenPayload = {
|
|
|
91
92
|
|
|
92
93
|
export type IdTokenPayload = {
|
|
93
94
|
passport?: PassportMetadata;
|
|
95
|
+
username?: string;
|
|
94
96
|
email: string;
|
|
95
97
|
nickname: string;
|
|
96
98
|
aud: string;
|