@imtbl/auth 2.12.5-alpha.16 → 2.12.5-alpha.18
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/dist/browser/index.js +23 -23
- package/dist/node/index.cjs +27 -26
- package/dist/node/index.js +20 -20
- package/dist/types/index.d.ts +2 -2
- package/dist/types/types.d.ts +26 -34
- package/package.json +6 -6
- package/src/Auth.test.ts +73 -216
- package/src/Auth.ts +16 -15
- package/src/index.ts +7 -2
- package/src/types.ts +28 -37
package/src/Auth.test.ts
CHANGED
|
@@ -137,7 +137,7 @@ describe('Auth', () => {
|
|
|
137
137
|
});
|
|
138
138
|
});
|
|
139
139
|
|
|
140
|
-
describe('
|
|
140
|
+
describe('mapOidcUserToDomainModel', () => {
|
|
141
141
|
it('extracts username from id token when present', () => {
|
|
142
142
|
const mockOidcUser = {
|
|
143
143
|
id_token: 'token',
|
|
@@ -158,256 +158,113 @@ describe('Auth', () => {
|
|
|
158
158
|
expect(result.profile.username).toEqual('username123');
|
|
159
159
|
});
|
|
160
160
|
|
|
161
|
-
it('
|
|
162
|
-
const
|
|
161
|
+
it('extracts zkEvm chain data from passport metadata', () => {
|
|
162
|
+
const mockOidcUser = {
|
|
163
163
|
id_token: 'token',
|
|
164
164
|
access_token: 'access',
|
|
165
165
|
refresh_token: 'refresh',
|
|
166
|
-
|
|
167
|
-
|
|
166
|
+
expired: false,
|
|
167
|
+
profile: { sub: 'user-123', email: 'test@example.com', nickname: 'tester' },
|
|
168
168
|
};
|
|
169
169
|
|
|
170
170
|
(decodeJwtPayload as jest.Mock).mockReturnValue({
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
iat: 0,
|
|
176
|
-
email: 'test@example.com',
|
|
177
|
-
nickname: 'tester',
|
|
178
|
-
username: 'username123',
|
|
179
|
-
passport: undefined,
|
|
171
|
+
passport: {
|
|
172
|
+
zkevm_eth_address: '0xzkevmaddress',
|
|
173
|
+
zkevm_user_admin_address: '0xzkevmadmin',
|
|
174
|
+
},
|
|
180
175
|
});
|
|
181
176
|
|
|
182
|
-
const
|
|
177
|
+
const result = (Auth as any).mapOidcUserToDomainModel(mockOidcUser);
|
|
183
178
|
|
|
184
|
-
expect(
|
|
185
|
-
|
|
179
|
+
expect(result.zkEvm).toEqual({
|
|
180
|
+
ethAddress: '0xzkevmaddress',
|
|
181
|
+
userAdminAddress: '0xzkevmadmin',
|
|
182
|
+
});
|
|
186
183
|
});
|
|
187
|
-
});
|
|
188
184
|
|
|
189
|
-
|
|
190
|
-
it('emits TOKEN_REFRESHED event when signinSilent succeeds', async () => {
|
|
185
|
+
it('extracts arbitrum_one chain data from nested passport metadata', () => {
|
|
191
186
|
const mockOidcUser = {
|
|
192
|
-
id_token: '
|
|
193
|
-
access_token: '
|
|
194
|
-
refresh_token: '
|
|
187
|
+
id_token: 'token',
|
|
188
|
+
access_token: 'access',
|
|
189
|
+
refresh_token: 'refresh',
|
|
195
190
|
expired: false,
|
|
196
191
|
profile: { sub: 'user-123', email: 'test@example.com', nickname: 'tester' },
|
|
197
192
|
};
|
|
198
193
|
|
|
199
194
|
(decodeJwtPayload as jest.Mock).mockReturnValue({
|
|
200
|
-
|
|
201
|
-
|
|
195
|
+
passport: {
|
|
196
|
+
arbitrum_one: {
|
|
197
|
+
eth_address: '0xarbaddress',
|
|
198
|
+
user_admin_address: '0xarbadmin',
|
|
199
|
+
},
|
|
200
|
+
},
|
|
202
201
|
});
|
|
203
202
|
|
|
204
|
-
const
|
|
205
|
-
const mockEventEmitter = { emit: jest.fn() };
|
|
206
|
-
const mockUserManager = {
|
|
207
|
-
signinSilent: jest.fn().mockResolvedValue(mockOidcUser),
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
(auth as any).eventEmitter = mockEventEmitter;
|
|
211
|
-
(auth as any).userManager = mockUserManager;
|
|
212
|
-
(auth as any).refreshingPromise = null;
|
|
213
|
-
|
|
214
|
-
const user = await (auth as any).refreshTokenAndUpdatePromise();
|
|
215
|
-
|
|
216
|
-
expect(user).toBeDefined();
|
|
217
|
-
expect(user.accessToken).toBe('new-access');
|
|
218
|
-
expect(mockEventEmitter.emit).toHaveBeenCalledWith(
|
|
219
|
-
AuthEvents.TOKEN_REFRESHED,
|
|
220
|
-
expect.objectContaining({
|
|
221
|
-
accessToken: 'new-access',
|
|
222
|
-
refreshToken: 'new-refresh',
|
|
223
|
-
}),
|
|
224
|
-
);
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
it('does not emit TOKEN_REFRESHED event when signinSilent returns null', async () => {
|
|
228
|
-
const auth = Object.create(Auth.prototype) as Auth;
|
|
229
|
-
const mockEventEmitter = { emit: jest.fn() };
|
|
230
|
-
const mockUserManager = {
|
|
231
|
-
signinSilent: jest.fn().mockResolvedValue(null),
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
(auth as any).eventEmitter = mockEventEmitter;
|
|
235
|
-
(auth as any).userManager = mockUserManager;
|
|
236
|
-
(auth as any).refreshingPromise = null;
|
|
237
|
-
|
|
238
|
-
const user = await (auth as any).refreshTokenAndUpdatePromise();
|
|
239
|
-
|
|
240
|
-
expect(user).toBeNull();
|
|
241
|
-
expect(mockEventEmitter.emit).not.toHaveBeenCalled();
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
it('emits USER_REMOVED event for invalid_grant error', async () => {
|
|
245
|
-
const auth = Object.create(Auth.prototype) as Auth;
|
|
246
|
-
const mockEventEmitter = { emit: jest.fn() };
|
|
247
|
-
const mockUserManager = {
|
|
248
|
-
signinSilent: jest.fn().mockRejectedValue(
|
|
249
|
-
Object.assign(new Error('invalid_grant'), {
|
|
250
|
-
error: 'invalid_grant',
|
|
251
|
-
error_description: 'Unknown or invalid refresh token',
|
|
252
|
-
}),
|
|
253
|
-
),
|
|
254
|
-
removeUser: jest.fn().mockResolvedValue(undefined),
|
|
255
|
-
};
|
|
203
|
+
const result = (Auth as any).mapOidcUserToDomainModel(mockOidcUser);
|
|
256
204
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
error: 'invalid_grant',
|
|
261
|
-
error_description: 'Unknown or invalid refresh token',
|
|
205
|
+
expect(result.arbitrum_one).toEqual({
|
|
206
|
+
ethAddress: '0xarbaddress',
|
|
207
|
+
userAdminAddress: '0xarbadmin',
|
|
262
208
|
});
|
|
263
|
-
mockUserManager.signinSilent.mockRejectedValue(errorResponse);
|
|
264
|
-
|
|
265
|
-
(auth as any).eventEmitter = mockEventEmitter;
|
|
266
|
-
(auth as any).userManager = mockUserManager;
|
|
267
|
-
(auth as any).refreshingPromise = null;
|
|
268
|
-
|
|
269
|
-
await expect((auth as any).refreshTokenAndUpdatePromise()).rejects.toThrow();
|
|
270
|
-
|
|
271
|
-
expect(mockEventEmitter.emit).toHaveBeenCalledWith(
|
|
272
|
-
AuthEvents.USER_REMOVED,
|
|
273
|
-
expect.objectContaining({
|
|
274
|
-
reason: 'refresh_failed',
|
|
275
|
-
}),
|
|
276
|
-
);
|
|
277
|
-
expect(mockUserManager.removeUser).toHaveBeenCalled();
|
|
278
209
|
});
|
|
279
210
|
|
|
280
|
-
it('
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
211
|
+
it('extracts both zkEvm and arbitrum_one when present', () => {
|
|
212
|
+
const mockOidcUser = {
|
|
213
|
+
id_token: 'token',
|
|
214
|
+
access_token: 'access',
|
|
215
|
+
refresh_token: 'refresh',
|
|
216
|
+
expired: false,
|
|
217
|
+
profile: { sub: 'user-123', email: 'test@example.com', nickname: 'tester' },
|
|
286
218
|
};
|
|
287
219
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
220
|
+
(decodeJwtPayload as jest.Mock).mockReturnValue({
|
|
221
|
+
passport: {
|
|
222
|
+
zkevm_eth_address: '0xzkevmaddress',
|
|
223
|
+
zkevm_user_admin_address: '0xzkevmadmin',
|
|
224
|
+
arbitrum_one: {
|
|
225
|
+
eth_address: '0xarbaddress',
|
|
226
|
+
user_admin_address: '0xarbadmin',
|
|
227
|
+
},
|
|
228
|
+
},
|
|
292
229
|
});
|
|
293
|
-
mockUserManager.signinSilent.mockRejectedValue(errorResponse);
|
|
294
230
|
|
|
295
|
-
(
|
|
296
|
-
(auth as any).userManager = mockUserManager;
|
|
297
|
-
(auth as any).refreshingPromise = null;
|
|
298
|
-
|
|
299
|
-
await expect((auth as any).refreshTokenAndUpdatePromise()).rejects.toThrow();
|
|
300
|
-
|
|
301
|
-
expect(mockEventEmitter.emit).toHaveBeenCalledWith(
|
|
302
|
-
AuthEvents.USER_REMOVED,
|
|
303
|
-
expect.objectContaining({
|
|
304
|
-
reason: 'refresh_failed',
|
|
305
|
-
}),
|
|
306
|
-
);
|
|
307
|
-
expect(mockUserManager.removeUser).toHaveBeenCalled();
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
it('emits USER_REMOVED event for network errors', async () => {
|
|
311
|
-
const auth = Object.create(Auth.prototype) as Auth;
|
|
312
|
-
const mockEventEmitter = { emit: jest.fn() };
|
|
313
|
-
const mockUserManager = {
|
|
314
|
-
signinSilent: jest.fn().mockRejectedValue(new Error('Network error: Failed to fetch')),
|
|
315
|
-
removeUser: jest.fn().mockResolvedValue(undefined),
|
|
316
|
-
};
|
|
317
|
-
|
|
318
|
-
(auth as any).eventEmitter = mockEventEmitter;
|
|
319
|
-
(auth as any).userManager = mockUserManager;
|
|
320
|
-
(auth as any).refreshingPromise = null;
|
|
321
|
-
|
|
322
|
-
await expect((auth as any).refreshTokenAndUpdatePromise()).rejects.toThrow();
|
|
323
|
-
|
|
324
|
-
expect(mockEventEmitter.emit).toHaveBeenCalledWith(
|
|
325
|
-
AuthEvents.USER_REMOVED,
|
|
326
|
-
expect.objectContaining({
|
|
327
|
-
reason: 'refresh_failed',
|
|
328
|
-
}),
|
|
329
|
-
);
|
|
330
|
-
expect(mockUserManager.removeUser).toHaveBeenCalled();
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
it('emits USER_REMOVED event for server_error OAuth error', async () => {
|
|
334
|
-
const auth = Object.create(Auth.prototype) as Auth;
|
|
335
|
-
const mockEventEmitter = { emit: jest.fn() };
|
|
336
|
-
const mockUserManager = {
|
|
337
|
-
signinSilent: jest.fn(),
|
|
338
|
-
removeUser: jest.fn().mockResolvedValue(undefined),
|
|
339
|
-
};
|
|
231
|
+
const result = (Auth as any).mapOidcUserToDomainModel(mockOidcUser);
|
|
340
232
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
233
|
+
expect(result.zkEvm).toEqual({
|
|
234
|
+
ethAddress: '0xzkevmaddress',
|
|
235
|
+
userAdminAddress: '0xzkevmadmin',
|
|
236
|
+
});
|
|
237
|
+
expect(result.arbitrum_one).toEqual({
|
|
238
|
+
ethAddress: '0xarbaddress',
|
|
239
|
+
userAdminAddress: '0xarbadmin',
|
|
345
240
|
});
|
|
346
|
-
mockUserManager.signinSilent.mockRejectedValue(errorResponse);
|
|
347
|
-
|
|
348
|
-
(auth as any).eventEmitter = mockEventEmitter;
|
|
349
|
-
(auth as any).userManager = mockUserManager;
|
|
350
|
-
(auth as any).refreshingPromise = null;
|
|
351
|
-
|
|
352
|
-
await expect((auth as any).refreshTokenAndUpdatePromise()).rejects.toThrow();
|
|
353
|
-
|
|
354
|
-
expect(mockEventEmitter.emit).toHaveBeenCalledWith(
|
|
355
|
-
AuthEvents.USER_REMOVED,
|
|
356
|
-
expect.objectContaining({
|
|
357
|
-
reason: 'refresh_failed',
|
|
358
|
-
}),
|
|
359
|
-
);
|
|
360
|
-
expect(mockUserManager.removeUser).toHaveBeenCalled();
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
it('emits USER_REMOVED event for unknown errors (safer default)', async () => {
|
|
364
|
-
const auth = Object.create(Auth.prototype) as Auth;
|
|
365
|
-
const mockEventEmitter = { emit: jest.fn() };
|
|
366
|
-
const mockUserManager = {
|
|
367
|
-
signinSilent: jest.fn().mockRejectedValue(new Error('Some unknown error')),
|
|
368
|
-
removeUser: jest.fn().mockResolvedValue(undefined),
|
|
369
|
-
};
|
|
370
|
-
|
|
371
|
-
(auth as any).eventEmitter = mockEventEmitter;
|
|
372
|
-
(auth as any).userManager = mockUserManager;
|
|
373
|
-
(auth as any).refreshingPromise = null;
|
|
374
|
-
|
|
375
|
-
await expect((auth as any).refreshTokenAndUpdatePromise()).rejects.toThrow();
|
|
376
|
-
|
|
377
|
-
// Unknown errors should remove user (safer default)
|
|
378
|
-
expect(mockEventEmitter.emit).toHaveBeenCalledWith(
|
|
379
|
-
AuthEvents.USER_REMOVED,
|
|
380
|
-
expect.objectContaining({
|
|
381
|
-
reason: 'refresh_failed',
|
|
382
|
-
}),
|
|
383
|
-
);
|
|
384
|
-
expect(mockUserManager.removeUser).toHaveBeenCalled();
|
|
385
241
|
});
|
|
386
242
|
|
|
387
|
-
it('
|
|
388
|
-
const
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
243
|
+
it('maps username when creating OIDC user from device tokens', () => {
|
|
244
|
+
const tokenResponse = {
|
|
245
|
+
id_token: 'token',
|
|
246
|
+
access_token: 'access',
|
|
247
|
+
refresh_token: 'refresh',
|
|
248
|
+
token_type: 'Bearer',
|
|
249
|
+
expires_in: 3600,
|
|
393
250
|
};
|
|
394
251
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
252
|
+
(decodeJwtPayload as jest.Mock).mockReturnValue({
|
|
253
|
+
sub: 'user-123',
|
|
254
|
+
iss: 'issuer',
|
|
255
|
+
aud: 'audience',
|
|
256
|
+
exp: 1,
|
|
257
|
+
iat: 0,
|
|
258
|
+
email: 'test@example.com',
|
|
259
|
+
nickname: 'tester',
|
|
260
|
+
username: 'username123',
|
|
261
|
+
passport: undefined,
|
|
262
|
+
});
|
|
403
263
|
|
|
404
|
-
|
|
264
|
+
const oidcUser = (Auth as any).mapDeviceTokenResponseToOidcUser(tokenResponse);
|
|
405
265
|
|
|
406
|
-
expect(
|
|
407
|
-
|
|
408
|
-
expect.anything(),
|
|
409
|
-
);
|
|
410
|
-
expect(mockUserManager.removeUser).not.toHaveBeenCalled();
|
|
266
|
+
expect(decodeJwtPayload).toHaveBeenCalledWith('token');
|
|
267
|
+
expect(oidcUser.profile.username).toEqual('username123');
|
|
411
268
|
});
|
|
412
269
|
});
|
|
413
270
|
|
package/src/Auth.ts
CHANGED
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
PassportMetadata,
|
|
30
30
|
IdTokenPayload,
|
|
31
31
|
isUserZkEvm,
|
|
32
|
+
EvmChain,
|
|
32
33
|
} from './types';
|
|
33
34
|
import EmbeddedLoginPrompt from './login/embeddedLoginPrompt';
|
|
34
35
|
import TypedEventEmitter from './utils/typedEventEmitter';
|
|
@@ -570,12 +571,25 @@ export class Auth {
|
|
|
570
571
|
username,
|
|
571
572
|
},
|
|
572
573
|
};
|
|
574
|
+
|
|
573
575
|
if (passport?.zkevm_eth_address && passport?.zkevm_user_admin_address) {
|
|
574
576
|
user.zkEvm = {
|
|
575
577
|
ethAddress: passport.zkevm_eth_address,
|
|
576
578
|
userAdminAddress: passport.zkevm_user_admin_address,
|
|
577
579
|
};
|
|
578
580
|
}
|
|
581
|
+
|
|
582
|
+
const chains = Object.values(EvmChain).filter((chain) => chain !== EvmChain.ZKEVM);
|
|
583
|
+
for (const chain of chains) {
|
|
584
|
+
const chainMetadata = passport?.[chain as Exclude<EvmChain, EvmChain.ZKEVM>];
|
|
585
|
+
if (chainMetadata?.eth_address && chainMetadata?.user_admin_address) {
|
|
586
|
+
user[chain] = {
|
|
587
|
+
ethAddress: chainMetadata.eth_address,
|
|
588
|
+
userAdminAddress: chainMetadata.user_admin_address,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
579
593
|
return user;
|
|
580
594
|
};
|
|
581
595
|
|
|
@@ -765,21 +779,15 @@ export class Auth {
|
|
|
765
779
|
try {
|
|
766
780
|
const newOidcUser = await this.userManager.signinSilent();
|
|
767
781
|
if (newOidcUser) {
|
|
768
|
-
|
|
769
|
-
// Emit TOKEN_REFRESHED event so consumers (e.g., auth-next-client) can sync
|
|
770
|
-
// the new tokens to their session. This is critical for refresh token
|
|
771
|
-
// rotation - without this, the server-side session may have stale tokens.
|
|
772
|
-
this.eventEmitter.emit(AuthEvents.TOKEN_REFRESHED, user);
|
|
773
|
-
resolve(user);
|
|
782
|
+
resolve(Auth.mapOidcUserToDomainModel(newOidcUser));
|
|
774
783
|
return;
|
|
775
784
|
}
|
|
776
785
|
resolve(null);
|
|
777
786
|
} catch (err) {
|
|
778
787
|
let passportErrorType = PassportErrorType.AUTHENTICATION_ERROR;
|
|
779
788
|
let errorMessage = 'Failed to refresh token';
|
|
780
|
-
// Default to REMOVING user - safer to log out on unknown errors
|
|
781
|
-
// Only keep user logged in for explicitly known transient errors
|
|
782
789
|
let removeUser = true;
|
|
790
|
+
|
|
783
791
|
if (err instanceof ErrorTimeout) {
|
|
784
792
|
passportErrorType = PassportErrorType.SILENT_LOGIN_ERROR;
|
|
785
793
|
errorMessage = `${errorMessage}: ${err.message}`;
|
|
@@ -794,13 +802,6 @@ export class Auth {
|
|
|
794
802
|
}
|
|
795
803
|
|
|
796
804
|
if (removeUser) {
|
|
797
|
-
// Emit USER_REMOVED event BEFORE removing user so consumers can react
|
|
798
|
-
// (e.g., auth-next-client can clear the NextAuth session)
|
|
799
|
-
this.eventEmitter.emit(AuthEvents.USER_REMOVED, {
|
|
800
|
-
reason: 'refresh_failed',
|
|
801
|
-
error: errorMessage,
|
|
802
|
-
});
|
|
803
|
-
|
|
804
805
|
try {
|
|
805
806
|
await this.userManager.removeUser();
|
|
806
807
|
} catch (removeUserError) {
|
package/src/index.ts
CHANGED
|
@@ -17,13 +17,18 @@ export type {
|
|
|
17
17
|
AuthModuleConfiguration,
|
|
18
18
|
PopupOverlayOptions,
|
|
19
19
|
PassportMetadata,
|
|
20
|
+
PassportChainMetadata,
|
|
21
|
+
ChainAddress,
|
|
20
22
|
IdTokenPayload,
|
|
21
23
|
PKCEData,
|
|
22
24
|
AuthEventMap,
|
|
23
|
-
UserRemovedReason,
|
|
24
25
|
} from './types';
|
|
25
26
|
export {
|
|
26
|
-
isUserZkEvm,
|
|
27
|
+
isUserZkEvm,
|
|
28
|
+
RollupType,
|
|
29
|
+
EvmChain,
|
|
30
|
+
MarketingConsentStatus,
|
|
31
|
+
AuthEvents,
|
|
27
32
|
} from './types';
|
|
28
33
|
|
|
29
34
|
// Export TypedEventEmitter
|
package/src/types.ts
CHANGED
|
@@ -16,22 +16,45 @@ export enum RollupType {
|
|
|
16
16
|
ZKEVM = 'zkEvm',
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Supported EVM chains for user registration
|
|
21
|
+
* Matches EvmChain from @imtbl/wallet but defined here to avoid circular dependency
|
|
22
|
+
*/
|
|
23
|
+
export enum EvmChain {
|
|
24
|
+
ZKEVM = 'zkevm',
|
|
25
|
+
ARBITRUM_ONE = 'arbitrum_one',
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export type ChainAddress = {
|
|
29
|
+
ethAddress: string;
|
|
30
|
+
userAdminAddress: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
19
33
|
export type User = {
|
|
20
34
|
idToken?: string;
|
|
21
35
|
accessToken: string;
|
|
22
36
|
refreshToken?: string;
|
|
23
37
|
profile: UserProfile;
|
|
24
38
|
expired?: boolean;
|
|
25
|
-
[RollupType.ZKEVM]?:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
39
|
+
[RollupType.ZKEVM]?: ChainAddress;
|
|
40
|
+
} & {
|
|
41
|
+
[K in Exclude<EvmChain, EvmChain.ZKEVM>]?: ChainAddress;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type PassportChainMetadata = {
|
|
45
|
+
eth_address: string;
|
|
46
|
+
user_admin_address: string;
|
|
29
47
|
};
|
|
30
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Passport metadata
|
|
51
|
+
* - zkEVM: flat fields (zkevm_eth_address, zkevm_user_admin_address)
|
|
52
|
+
* - Other chains: nested objects (arbitrum_one: { eth_address, user_admin_address })
|
|
53
|
+
*/
|
|
31
54
|
export type PassportMetadata = {
|
|
32
55
|
zkevm_eth_address?: string;
|
|
33
56
|
zkevm_user_admin_address?: string;
|
|
34
|
-
}
|
|
57
|
+
} & Partial<Record<Exclude<EvmChain, EvmChain.ZKEVM>, PassportChainMetadata>>;
|
|
35
58
|
|
|
36
59
|
export interface OidcConfiguration {
|
|
37
60
|
clientId: string;
|
|
@@ -140,44 +163,12 @@ export type LoginOptions = {
|
|
|
140
163
|
export enum AuthEvents {
|
|
141
164
|
LOGGED_OUT = 'loggedOut',
|
|
142
165
|
LOGGED_IN = 'loggedIn',
|
|
143
|
-
/**
|
|
144
|
-
* Emitted when tokens are refreshed via signinSilent().
|
|
145
|
-
* This is critical for refresh token rotation - when client-side refresh happens,
|
|
146
|
-
* the new tokens must be synced to server-side session to prevent race conditions.
|
|
147
|
-
*/
|
|
148
|
-
TOKEN_REFRESHED = 'tokenRefreshed',
|
|
149
|
-
/**
|
|
150
|
-
* Emitted when the user is removed from local storage due to a permanent auth error.
|
|
151
|
-
* Only emitted for errors where the refresh token is truly invalid:
|
|
152
|
-
* - invalid_grant: refresh token expired, revoked, or already used
|
|
153
|
-
* - login_required: user must re-authenticate
|
|
154
|
-
* - consent_required / interaction_required: user must interact with auth server
|
|
155
|
-
*
|
|
156
|
-
* NOT emitted for transient errors (network, timeout, server errors) - user stays logged in.
|
|
157
|
-
* Consumers should sync this state by clearing their session (e.g., NextAuth signOut).
|
|
158
|
-
*/
|
|
159
|
-
USER_REMOVED = 'userRemoved',
|
|
160
166
|
}
|
|
161
167
|
|
|
162
|
-
/**
|
|
163
|
-
* Error reason for USER_REMOVED event.
|
|
164
|
-
* Note: Network/timeout errors do NOT emit USER_REMOVED (user stays logged in),
|
|
165
|
-
* so 'network_error' is not a valid reason.
|
|
166
|
-
*/
|
|
167
|
-
export type UserRemovedReason =
|
|
168
|
-
// OAuth permanent errors (invalid_grant, login_required, etc.)
|
|
169
|
-
| 'refresh_token_invalid'
|
|
170
|
-
// Unknown non-OAuth errors
|
|
171
|
-
| 'refresh_failed'
|
|
172
|
-
// Fallback for truly unknown error types
|
|
173
|
-
| 'unknown';
|
|
174
|
-
|
|
175
168
|
/**
|
|
176
169
|
* Event map for typed event emitter
|
|
177
170
|
*/
|
|
178
171
|
export interface AuthEventMap extends Record<string, any> {
|
|
179
172
|
[AuthEvents.LOGGED_OUT]: [];
|
|
180
173
|
[AuthEvents.LOGGED_IN]: [User];
|
|
181
|
-
[AuthEvents.TOKEN_REFRESHED]: [User];
|
|
182
|
-
[AuthEvents.USER_REMOVED]: [{ reason: UserRemovedReason; error?: string }];
|
|
183
174
|
}
|