@0xsequence/wallet-wdk 3.0.2 → 3.0.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.
Files changed (47) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/.turbo/turbo-lint.log +1 -1
  3. package/.turbo/turbo-typecheck.log +1 -1
  4. package/CHANGELOG.md +24 -0
  5. package/dist/sequence/handlers/authcode.d.ts.map +1 -1
  6. package/dist/sequence/handlers/authcode.js +6 -0
  7. package/dist/sequence/handlers/identity.d.ts +1 -0
  8. package/dist/sequence/handlers/identity.d.ts.map +1 -1
  9. package/dist/sequence/handlers/identity.js +3 -0
  10. package/dist/sequence/handlers/idtoken.d.ts +33 -0
  11. package/dist/sequence/handlers/idtoken.d.ts.map +1 -0
  12. package/dist/sequence/handlers/idtoken.js +113 -0
  13. package/dist/sequence/handlers/index.d.ts +1 -0
  14. package/dist/sequence/handlers/index.d.ts.map +1 -1
  15. package/dist/sequence/handlers/index.js +1 -0
  16. package/dist/sequence/manager.d.ts +23 -14
  17. package/dist/sequence/manager.d.ts.map +1 -1
  18. package/dist/sequence/manager.js +28 -4
  19. package/dist/sequence/sessions.d.ts.map +1 -1
  20. package/dist/sequence/sessions.js +5 -1
  21. package/dist/sequence/signers.d.ts.map +1 -1
  22. package/dist/sequence/signers.js +4 -0
  23. package/dist/sequence/types/signer.d.ts +1 -1
  24. package/dist/sequence/types/signer.js +1 -1
  25. package/dist/sequence/types/wallet.d.ts +1 -1
  26. package/dist/sequence/wallets.d.ts +7 -1
  27. package/dist/sequence/wallets.d.ts.map +1 -1
  28. package/dist/sequence/wallets.js +73 -7
  29. package/package.json +6 -6
  30. package/src/sequence/handlers/authcode.ts +6 -0
  31. package/src/sequence/handlers/identity.ts +4 -0
  32. package/src/sequence/handlers/idtoken.ts +146 -0
  33. package/src/sequence/handlers/index.ts +1 -0
  34. package/src/sequence/manager.ts +110 -43
  35. package/src/sequence/sessions.ts +7 -1
  36. package/src/sequence/signers.ts +5 -0
  37. package/src/sequence/types/signer.ts +1 -1
  38. package/src/sequence/types/wallet.ts +1 -1
  39. package/src/sequence/wallets.ts +98 -9
  40. package/test/authcode-pkce.test.ts +1 -1
  41. package/test/authcode.test.ts +2 -2
  42. package/test/identity-auth-dbs.test.ts +122 -2
  43. package/test/identity-signer.test.ts +1 -1
  44. package/test/idtoken.test.ts +343 -0
  45. package/test/sessions-idtoken.test.ts +98 -0
  46. package/test/signers-kindof.test.ts +22 -0
  47. package/test/wallets.test.ts +202 -1
@@ -3,6 +3,7 @@ import { Config, Constants, Payload } from '@0xsequence/wallet-primitives'
3
3
  import { Address, Hex, Provider, RpcTransport } from 'ox'
4
4
  import { AuthCommitment } from '../dbs/auth-commitments.js'
5
5
  import { AuthCodeHandler } from './handlers/authcode.js'
6
+ import { IdTokenHandler } from './handlers/idtoken.js'
6
7
  import { MnemonicHandler } from './handlers/mnemonic.js'
7
8
  import { OtpHandler } from './handlers/otp.js'
8
9
  import { Shared } from './manager.js'
@@ -13,6 +14,43 @@ import { Wallet, WalletSelectionUiHandler } from './types/wallet.js'
13
14
  import { PasskeysHandler } from './handlers/passkeys.js'
14
15
  import type { PasskeySigner } from './passkeys-provider.js'
15
16
 
17
+ function getSignupHandlerKey(kind: SignupArgs['kind'] | StartSignUpWithRedirectArgs['kind'] | AuthCommitment['kind']) {
18
+ if (kind === 'google-pkce') {
19
+ return Kinds.LoginGoogle
20
+ }
21
+ if (kind.startsWith('custom-')) {
22
+ return kind
23
+ }
24
+ return 'login-' + kind
25
+ }
26
+
27
+ function getSignerKindForSignup(kind: SignupArgs['kind'] | AuthCommitment['kind']) {
28
+ if (kind === 'google-id-token' || kind === 'google-pkce') {
29
+ return Kinds.LoginGoogle
30
+ }
31
+ if (kind === 'apple-id-token' || kind === 'apple') {
32
+ return Kinds.LoginApple
33
+ }
34
+ if (kind.startsWith('custom-')) {
35
+ return kind
36
+ }
37
+ return ('login-' + kind) as string
38
+ }
39
+
40
+ function getIdTokenSignupHandler(
41
+ shared: Shared,
42
+ kind: typeof Kinds.LoginGoogle | typeof Kinds.LoginApple | `custom-${string}`,
43
+ ): IdTokenHandler {
44
+ const handler = shared.handlers.get(kind)
45
+ if (!handler) {
46
+ throw new Error('handler-not-registered')
47
+ }
48
+ if (!(handler instanceof IdTokenHandler)) {
49
+ throw new Error('handler-does-not-support-id-token')
50
+ }
51
+ return handler
52
+ }
53
+
16
54
  export type StartSignUpWithRedirectArgs = {
17
55
  kind: 'google-pkce' | 'apple' | `custom-${string}`
18
56
  target: string
@@ -49,6 +87,11 @@ export type EmailOtpSignupArgs = CommonSignupArgs & {
49
87
  email: string
50
88
  }
51
89
 
90
+ export type IdTokenSignupArgs = CommonSignupArgs & {
91
+ kind: 'google-id-token' | 'apple-id-token' | `custom-${string}`
92
+ idToken: string
93
+ }
94
+
52
95
  export type CompleteRedirectArgs = CommonSignupArgs & {
53
96
  state: string
54
97
  code: string
@@ -62,7 +105,12 @@ export type AuthCodeSignupArgs = CommonSignupArgs & {
62
105
  isRedirect: boolean
63
106
  }
64
107
 
65
- export type SignupArgs = PasskeySignupArgs | MnemonicSignupArgs | EmailOtpSignupArgs | AuthCodeSignupArgs
108
+ export type SignupArgs =
109
+ | PasskeySignupArgs
110
+ | MnemonicSignupArgs
111
+ | EmailOtpSignupArgs
112
+ | IdTokenSignupArgs
113
+ | AuthCodeSignupArgs
66
114
 
67
115
  export type LoginToWalletArgs = {
68
116
  wallet: Address.Address
@@ -180,6 +228,7 @@ export interface WalletsInterface {
180
228
  * - `kind: 'mnemonic'`: Uses a mnemonic phrase as the login credential.
181
229
  * - `kind: 'passkey'`: Prompts the user to create a WebAuthn passkey.
182
230
  * - `kind: 'email-otp'`: Initiates an OTP flow to the user's email.
231
+ * - `kind: 'google-id-token' | 'apple-id-token'`: Completes an OIDC ID token flow when the provider is configured with `authMethod: 'id-token'`.
183
232
  * - `kind: 'google-pkce' | 'apple'`: Completes an OAuth redirect flow.
184
233
  * Common options like `noGuard` or `noRecovery` can customize the wallet's security features.
185
234
  * @returns A promise that resolves to the address of the newly created wallet, or `undefined` if the sign-up was aborted.
@@ -361,7 +410,11 @@ export function isLoginToPasskeyArgs(args: LoginArgs): args is LoginToPasskeyArg
361
410
  }
362
411
 
363
412
  export function isAuthCodeArgs(args: SignupArgs): args is AuthCodeSignupArgs {
364
- return 'kind' in args && (args.kind === 'google-pkce' || args.kind === 'apple')
413
+ return 'code' in args && 'commitment' in args
414
+ }
415
+
416
+ export function isIdTokenArgs(args: SignupArgs): args is IdTokenSignupArgs {
417
+ return 'idToken' in args
365
418
  }
366
419
 
367
420
  function buildCappedTree(members: { address: Address.Address; imageHash?: Hex.Hex }[]): Config.Topology {
@@ -674,9 +727,28 @@ export class Wallets implements WalletsInterface {
674
727
  }
675
728
  }
676
729
 
730
+ case 'google-id-token':
731
+ case 'apple-id-token': {
732
+ const handler = getIdTokenSignupHandler(
733
+ this.shared,
734
+ args.kind === 'google-id-token' ? Kinds.LoginGoogle : Kinds.LoginApple,
735
+ )
736
+ const [signer, metadata] = await handler.completeAuth(args.idToken)
737
+ const loginEmail = metadata.email
738
+ this.shared.modules.logger.log('Created new id token signer:', signer.address)
739
+
740
+ return {
741
+ signer,
742
+ extra: {
743
+ signerKind: getSignerKindForSignup(args.kind),
744
+ },
745
+ loginEmail,
746
+ }
747
+ }
748
+
677
749
  case 'google-pkce':
678
750
  case 'apple': {
679
- const handler = this.shared.handlers.get('login-' + args.kind) as AuthCodeHandler
751
+ const handler = this.shared.handlers.get(getSignupHandlerKey(args.kind)) as AuthCodeHandler
680
752
  if (!handler) {
681
753
  throw new Error('handler-not-registered')
682
754
  }
@@ -688,7 +760,7 @@ export class Wallets implements WalletsInterface {
688
760
  return {
689
761
  signer,
690
762
  extra: {
691
- signerKind: 'login-' + args.kind,
763
+ signerKind: getSignerKindForSignup(args.kind),
692
764
  },
693
765
  loginEmail,
694
766
  }
@@ -696,7 +768,18 @@ export class Wallets implements WalletsInterface {
696
768
  }
697
769
 
698
770
  if (args.kind.startsWith('custom-')) {
699
- // TODO: support other custom auth methods (e.g. id-token)
771
+ if (isIdTokenArgs(args)) {
772
+ const handler = getIdTokenSignupHandler(this.shared, args.kind)
773
+ const [signer, metadata] = await handler.completeAuth(args.idToken)
774
+ return {
775
+ signer,
776
+ extra: {
777
+ signerKind: args.kind,
778
+ },
779
+ loginEmail: metadata.email,
780
+ }
781
+ }
782
+
700
783
  const handler = this.shared.handlers.get(args.kind) as AuthCodeHandler
701
784
  if (!handler) {
702
785
  throw new Error('handler-not-registered')
@@ -716,11 +799,14 @@ export class Wallets implements WalletsInterface {
716
799
  }
717
800
 
718
801
  async startSignUpWithRedirect(args: StartSignUpWithRedirectArgs) {
719
- const kind = args.kind.startsWith('custom-') ? args.kind : 'login-' + args.kind
720
- const handler = this.shared.handlers.get(kind) as AuthCodeHandler
802
+ const kind = getSignupHandlerKey(args.kind)
803
+ const handler = this.shared.handlers.get(kind)
721
804
  if (!handler) {
722
805
  throw new Error('handler-not-registered')
723
806
  }
807
+ if (!(handler instanceof AuthCodeHandler)) {
808
+ throw new Error('handler-does-not-support-redirect')
809
+ }
724
810
  return handler.commitAuth(args.target, true)
725
811
  }
726
812
 
@@ -742,11 +828,14 @@ export class Wallets implements WalletsInterface {
742
828
  use4337: args.use4337,
743
829
  })
744
830
  } else {
745
- const kind = commitment.kind.startsWith('custom-') ? commitment.kind : 'login-' + commitment.kind
746
- const handler = this.shared.handlers.get(kind) as AuthCodeHandler
831
+ const handlerKind = getSignupHandlerKey(commitment.kind)
832
+ const handler = this.shared.handlers.get(handlerKind)
747
833
  if (!handler) {
748
834
  throw new Error('handler-not-registered')
749
835
  }
836
+ if (!(handler instanceof AuthCodeHandler)) {
837
+ throw new Error('handler-does-not-support-redirect')
838
+ }
750
839
 
751
840
  await handler.completeAuth(commitment, args.code)
752
841
  }
@@ -326,7 +326,7 @@ describe('AuthCodePkceHandler', () => {
326
326
 
327
327
  describe('Integration and Edge Cases', () => {
328
328
  it('Should have correct kind property', () => {
329
- expect(handler.kind).toBe('login-google-pkce')
329
+ expect(handler.kind).toBe('login-google')
330
330
  })
331
331
 
332
332
  it('Should handle redirect URI configuration', () => {
@@ -188,7 +188,7 @@ describe('AuthCodeHandler', () => {
188
188
  // === KIND GETTER ===
189
189
 
190
190
  describe('kind getter', () => {
191
- it('Should return login-google-pkce for Google PKCE handler', () => {
191
+ it('Should return login-google for Google PKCE handler', () => {
192
192
  const googleHandler = new AuthCodeHandler(
193
193
  'google-pkce',
194
194
  'https://accounts.google.com',
@@ -200,7 +200,7 @@ describe('AuthCodeHandler', () => {
200
200
  mockAuthKeys,
201
201
  )
202
202
 
203
- expect(googleHandler.kind).toBe('login-google-pkce')
203
+ expect(googleHandler.kind).toBe('login-google')
204
204
  })
205
205
 
206
206
  it('Should return login-apple for Apple handler', () => {
@@ -351,9 +351,84 @@ describe('Identity Authentication Databases', () => {
351
351
  },
352
352
  })
353
353
 
354
- // Verify that Google handler is registered and uses our databases
354
+ // Verify that Google is registered under the canonical signer kind while
355
+ // still using the PKCE flow by default.
355
356
  const handlers = (manager as any).shared.handlers
356
- expect(handlers.has('login-google-pkce')).toBe(true)
357
+ expect(handlers.has('login-google')).toBe(true)
358
+ expect(handlers.has('login-google-pkce')).toBe(false)
359
+ })
360
+
361
+ it('Should register the Google ID token handler when configured explicitly', async () => {
362
+ manager = new Manager({
363
+ stateProvider: new State.Local.Provider(new State.Local.IndexedDbStore(`manager-google-idtoken-${Date.now()}`)),
364
+ networks: [
365
+ {
366
+ name: 'Test Network',
367
+ type: Network.NetworkType.MAINNET,
368
+ rpcUrl: LOCAL_RPC_URL,
369
+ chainId: Network.ChainId.ARBITRUM,
370
+ blockExplorer: { url: 'https://arbiscan.io' },
371
+ nativeCurrency: {
372
+ name: 'Ether',
373
+ symbol: 'ETH',
374
+ decimals: 18,
375
+ },
376
+ },
377
+ ],
378
+ relayers: [],
379
+ authCommitmentsDb,
380
+ authKeysDb,
381
+ identity: {
382
+ url: 'https://dev-identity.sequence-dev.app',
383
+ fetch: window.fetch,
384
+ google: {
385
+ enabled: true,
386
+ clientId: 'test-google-client-id',
387
+ authMethod: 'id-token',
388
+ },
389
+ },
390
+ })
391
+
392
+ const handlers = (manager as any).shared.handlers
393
+ expect(handlers.has('login-google-id-token')).toBe(false)
394
+ expect(handlers.has('login-google')).toBe(true)
395
+ expect(handlers.has('login-google-pkce')).toBe(false)
396
+ })
397
+
398
+ it('Should register the Apple ID token handler when configured explicitly', async () => {
399
+ manager = new Manager({
400
+ stateProvider: new State.Local.Provider(new State.Local.IndexedDbStore(`manager-apple-idtoken-${Date.now()}`)),
401
+ networks: [
402
+ {
403
+ name: 'Test Network',
404
+ type: Network.NetworkType.MAINNET,
405
+ rpcUrl: LOCAL_RPC_URL,
406
+ chainId: Network.ChainId.ARBITRUM,
407
+ blockExplorer: { url: 'https://arbiscan.io' },
408
+ nativeCurrency: {
409
+ name: 'Ether',
410
+ symbol: 'ETH',
411
+ decimals: 18,
412
+ },
413
+ },
414
+ ],
415
+ relayers: [],
416
+ authCommitmentsDb,
417
+ authKeysDb,
418
+ identity: {
419
+ url: 'https://dev-identity.sequence-dev.app',
420
+ fetch: window.fetch,
421
+ apple: {
422
+ enabled: true,
423
+ clientId: 'test-apple-client-id',
424
+ authMethod: 'id-token',
425
+ },
426
+ },
427
+ })
428
+
429
+ const handlers = (manager as any).shared.handlers
430
+ expect(handlers.has('login-apple-id-token')).toBe(false)
431
+ expect(handlers.has('login-apple')).toBe(true)
357
432
  })
358
433
 
359
434
  it('Should use auth databases when email authentication is enabled', async () => {
@@ -424,5 +499,50 @@ describe('Identity Authentication Databases', () => {
424
499
  const handlers = (manager as any).shared.handlers
425
500
  expect(handlers.has('login-apple')).toBe(true)
426
501
  })
502
+
503
+ it('Should register custom ID token providers without enabling redirect flow for them', async () => {
504
+ manager = new Manager({
505
+ stateProvider: new State.Local.Provider(new State.Local.IndexedDbStore(`manager-custom-idtoken-${Date.now()}`)),
506
+ networks: [
507
+ {
508
+ name: 'Test Network',
509
+ type: Network.NetworkType.MAINNET,
510
+ rpcUrl: LOCAL_RPC_URL,
511
+ chainId: Network.ChainId.ARBITRUM,
512
+ blockExplorer: { url: 'https://arbiscan.io' },
513
+ nativeCurrency: {
514
+ name: 'Ether',
515
+ symbol: 'ETH',
516
+ decimals: 18,
517
+ },
518
+ },
519
+ ],
520
+ relayers: [],
521
+ authCommitmentsDb,
522
+ authKeysDb,
523
+ identity: {
524
+ url: 'https://dev-identity.sequence-dev.app',
525
+ fetch: window.fetch,
526
+ customProviders: [
527
+ {
528
+ kind: 'custom-google-native',
529
+ authMethod: 'id-token',
530
+ issuer: 'https://accounts.google.com',
531
+ clientId: 'test-google-client-id',
532
+ },
533
+ ],
534
+ },
535
+ })
536
+
537
+ const handlers = (manager as any).shared.handlers
538
+ expect(handlers.has('custom-google-native')).toBe(true)
539
+ await expect(
540
+ manager.wallets.startSignUpWithRedirect({
541
+ kind: 'custom-google-native',
542
+ target: '/home',
543
+ metadata: {},
544
+ }),
545
+ ).rejects.toThrow('handler-does-not-support-redirect')
546
+ })
427
547
  })
428
548
  })
@@ -13,7 +13,7 @@ const mockCryptoSubtle = {
13
13
  exportKey: vi.fn(),
14
14
  }
15
15
 
16
- Object.defineProperty(global, 'window', {
16
+ Object.defineProperty(globalThis, 'window', {
17
17
  value: {
18
18
  crypto: {
19
19
  subtle: mockCryptoSubtle,