accounts 0.7.2 → 0.8.1

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 (109) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +19 -20
  3. package/dist/cli/Provider.d.ts +1 -1
  4. package/dist/cli/Provider.d.ts.map +1 -1
  5. package/dist/cli/Provider.js +4 -1
  6. package/dist/cli/Provider.js.map +1 -1
  7. package/dist/cli/keyring.js +1 -1
  8. package/dist/cli/keyring.js.map +1 -1
  9. package/dist/core/Account.d.ts +2 -0
  10. package/dist/core/Account.d.ts.map +1 -1
  11. package/dist/core/Account.js.map +1 -1
  12. package/dist/core/Adapter.d.ts +9 -1
  13. package/dist/core/Adapter.d.ts.map +1 -1
  14. package/dist/core/Dialog.d.ts +16 -1
  15. package/dist/core/Dialog.d.ts.map +1 -1
  16. package/dist/core/Dialog.js +40 -3
  17. package/dist/core/Dialog.js.map +1 -1
  18. package/dist/core/Messenger.d.ts +15 -0
  19. package/dist/core/Messenger.d.ts.map +1 -1
  20. package/dist/core/Messenger.js.map +1 -1
  21. package/dist/core/Provider.d.ts +2 -0
  22. package/dist/core/Provider.d.ts.map +1 -1
  23. package/dist/core/Provider.js +24 -6
  24. package/dist/core/Provider.js.map +1 -1
  25. package/dist/core/Remote.d.ts +7 -1
  26. package/dist/core/Remote.d.ts.map +1 -1
  27. package/dist/core/Remote.js +18 -2
  28. package/dist/core/Remote.js.map +1 -1
  29. package/dist/core/Schema.d.ts +17 -3
  30. package/dist/core/Schema.d.ts.map +1 -1
  31. package/dist/core/Store.d.ts +2 -0
  32. package/dist/core/Store.d.ts.map +1 -1
  33. package/dist/core/Store.js +12 -7
  34. package/dist/core/Store.js.map +1 -1
  35. package/dist/core/TrustedHosts.d.ts.map +1 -1
  36. package/dist/core/TrustedHosts.js +2 -0
  37. package/dist/core/TrustedHosts.js.map +1 -1
  38. package/dist/core/WebAuthnCeremony.d.ts +8 -0
  39. package/dist/core/WebAuthnCeremony.d.ts.map +1 -1
  40. package/dist/core/WebAuthnCeremony.js.map +1 -1
  41. package/dist/core/adapters/dialog.d.ts +3 -1
  42. package/dist/core/adapters/dialog.d.ts.map +1 -1
  43. package/dist/core/adapters/dialog.js +8 -5
  44. package/dist/core/adapters/dialog.js.map +1 -1
  45. package/dist/core/adapters/local.js +4 -4
  46. package/dist/core/adapters/local.js.map +1 -1
  47. package/dist/core/adapters/webAuthn.d.ts.map +1 -1
  48. package/dist/core/adapters/webAuthn.js +7 -2
  49. package/dist/core/adapters/webAuthn.js.map +1 -1
  50. package/dist/core/zod/rpc.d.ts +17 -7
  51. package/dist/core/zod/rpc.d.ts.map +1 -1
  52. package/dist/core/zod/rpc.js +5 -1
  53. package/dist/core/zod/rpc.js.map +1 -1
  54. package/dist/react/Remote.d.ts +2 -0
  55. package/dist/react/Remote.d.ts.map +1 -1
  56. package/dist/react/Remote.js +69 -0
  57. package/dist/react/Remote.js.map +1 -1
  58. package/dist/react-native/Provider.d.ts.map +1 -1
  59. package/dist/react-native/Provider.js +4 -1
  60. package/dist/react-native/Provider.js.map +1 -1
  61. package/dist/react-native/adapter.d.ts +1 -1
  62. package/dist/react-native/adapter.d.ts.map +1 -1
  63. package/dist/react-native/adapter.js +2 -0
  64. package/dist/react-native/adapter.js.map +1 -1
  65. package/dist/server/CliAuth.d.ts +82 -11
  66. package/dist/server/CliAuth.d.ts.map +1 -1
  67. package/dist/server/CliAuth.js +82 -11
  68. package/dist/server/CliAuth.js.map +1 -1
  69. package/dist/server/internal/handlers/codeAuth.d.ts +2 -2
  70. package/dist/server/internal/handlers/codeAuth.js +2 -2
  71. package/dist/server/internal/handlers/relay.d.ts.map +1 -1
  72. package/dist/server/internal/handlers/relay.js +183 -88
  73. package/dist/server/internal/handlers/relay.js.map +1 -1
  74. package/dist/server/internal/handlers/utils.d.ts +2 -2
  75. package/dist/server/internal/handlers/utils.d.ts.map +1 -1
  76. package/dist/server/internal/handlers/utils.js +7 -2
  77. package/dist/server/internal/handlers/utils.js.map +1 -1
  78. package/dist/server/internal/handlers/webAuthn.d.ts +2 -0
  79. package/dist/server/internal/handlers/webAuthn.d.ts.map +1 -1
  80. package/dist/server/internal/handlers/webAuthn.js +20 -9
  81. package/dist/server/internal/handlers/webAuthn.js.map +1 -1
  82. package/package.json +1 -1
  83. package/src/cli/Provider.test.ts +3 -1
  84. package/src/cli/Provider.ts +4 -2
  85. package/src/cli/keyring.ts +1 -1
  86. package/src/core/Account.ts +2 -0
  87. package/src/core/Adapter.ts +9 -1
  88. package/src/core/Dialog.browser.test.ts +3 -3
  89. package/src/core/Dialog.ts +51 -4
  90. package/src/core/Messenger.ts +12 -0
  91. package/src/core/Provider.ts +46 -18
  92. package/src/core/Remote.ts +26 -4
  93. package/src/core/Store.ts +12 -4
  94. package/src/core/TrustedHosts.ts +1 -0
  95. package/src/core/WebAuthnCeremony.ts +8 -0
  96. package/src/core/adapters/dialog.ts +10 -5
  97. package/src/core/adapters/local.ts +4 -4
  98. package/src/core/adapters/webAuthn.ts +7 -2
  99. package/src/core/zod/rpc.ts +5 -1
  100. package/src/react/Remote.ts +76 -0
  101. package/src/react-native/Provider.test.ts +1 -1
  102. package/src/react-native/Provider.ts +9 -1
  103. package/src/react-native/adapter.ts +3 -1
  104. package/src/server/CliAuth.ts +82 -11
  105. package/src/server/internal/handlers/codeAuth.ts +2 -2
  106. package/src/server/internal/handlers/relay.test.ts +351 -1
  107. package/src/server/internal/handlers/relay.ts +208 -93
  108. package/src/server/internal/handlers/utils.ts +8 -2
  109. package/src/server/internal/handlers/webAuthn.ts +24 -12
@@ -119,7 +119,7 @@ export function local(options: local.Options): Adapter.Adapter {
119
119
  message: '`createAccount` not configured on adapter.',
120
120
  })
121
121
  const { authorizeAccessKey: grantOptions, ...rest } = parameters
122
- const { accounts, signature } = await createAccount(rest)
122
+ const { accounts, email, signature, username } = await createAccount(rest)
123
123
 
124
124
  // Hydrate the first account for signing. Must be done here (not via
125
125
  // the store) because accounts aren't merged into the store until
@@ -137,7 +137,7 @@ export function local(options: local.Options): Adapter.Adapter {
137
137
  return await signKeyAuthorization(account, prepared)
138
138
  })()
139
139
 
140
- return { accounts, keyAuthorization, signature: signature_ }
140
+ return { accounts, email, keyAuthorization, signature: signature_, username }
141
141
  },
142
142
  async authorizeAccessKey(parameters) {
143
143
  const prepared = await prepareKeyAuthorization(parameters)
@@ -164,7 +164,7 @@ export function local(options: local.Options): Adapter.Adapter {
164
164
 
165
165
  // Pass the prepared digest (or the caller's) into loadAccounts so
166
166
  // the ceremony can sign it in a single biometric prompt.
167
- const { accounts, signature } = await loadAccounts({ ...rest, digest })
167
+ const { accounts, email, signature, username } = await loadAccounts({ ...rest, digest })
168
168
 
169
169
  // Hydrate here (not from the store) — same reason as createAccount.
170
170
  // Guard against empty accounts (e.g. user cancelled the ceremony).
@@ -181,7 +181,7 @@ export function local(options: local.Options): Adapter.Adapter {
181
181
  })
182
182
  : undefined
183
183
 
184
- return { accounts, keyAuthorization, signature: signature_ }
184
+ return { accounts, email, keyAuthorization, signature: signature_, username }
185
185
  },
186
186
  async revokeAccessKey(parameters) {
187
187
  AccessKey.revoke({
@@ -39,7 +39,7 @@ export function webAuthn(options: webAuthn.Options = {}): Adapter.Adapter {
39
39
  const rpId = options.publicKey?.rp.id
40
40
  if (!rpId) throw new Error('rpId is required')
41
41
  const credential = await Registration.create({ options })
42
- const { publicKey } = await ceremony.verifyRegistration(credential, {
42
+ const { publicKey, email, username } = await ceremony.verifyRegistration(credential, {
43
43
  name: parameters.name,
44
44
  })
45
45
  await storage.setItem('lastCredentialId', credential.id)
@@ -48,10 +48,13 @@ export function webAuthn(options: webAuthn.Options = {}): Adapter.Adapter {
48
48
  accounts: [
49
49
  {
50
50
  address: account.address,
51
+ label: parameters.name,
51
52
  keyType: 'webAuthn',
52
53
  credential: { id: credential.id, publicKey, rpId },
53
54
  },
54
55
  ],
56
+ email,
57
+ username,
55
58
  }
56
59
  },
57
60
  async loadAccounts(parameters = {}) {
@@ -73,7 +76,7 @@ export function webAuthn(options: webAuthn.Options = {}): Adapter.Adapter {
73
76
  if (!rpId) throw new Error('rpId is required')
74
77
 
75
78
  const response = await Authentication.sign({ options })
76
- const { publicKey } = await ceremony.verifyAuthentication(response)
79
+ const { publicKey, email, username } = await ceremony.verifyAuthentication(response)
77
80
 
78
81
  await storage.setItem('lastCredentialId', response.id)
79
82
 
@@ -99,7 +102,9 @@ export function webAuthn(options: webAuthn.Options = {}): Adapter.Adapter {
99
102
  credential: { id: response.id, publicKey, rpId },
100
103
  },
101
104
  ],
105
+ email,
102
106
  signature,
107
+ username,
103
108
  }
104
109
  },
105
110
  })(parameters)
@@ -73,6 +73,7 @@ export const transactionRequest = z.object({
73
73
  chainId: z.optional(u.number()),
74
74
  data: z.optional(u.hex()),
75
75
  feePayer: z.optional(z.union([z.boolean(), z.string()])),
76
+ feePayerSignature: z.optional(z.record(z.string(), z.unknown())),
76
77
  feeToken: z.optional(u.address()),
77
78
  from: z.optional(u.address()),
78
79
  gas: z.optional(u.bigint()),
@@ -345,7 +346,7 @@ export namespace wallet_getCapabilities {
345
346
  export namespace wallet_authorizeAccessKey {
346
347
  export const parameters = z.object({
347
348
  address: z.optional(u.address()),
348
- chainId: z.optional(u.number()),
349
+ chainId: z.optional(u.bigint()),
349
350
  expiry: z.number(),
350
351
  keyType: z.optional(keyType),
351
352
  limits: z.optional(
@@ -441,8 +442,10 @@ export namespace wallet_connect {
441
442
  ]),
442
443
  ),
443
444
  result: z.object({
445
+ email: z.optional(z.nullable(z.string())),
444
446
  keyAuthorization: z.optional(keyAuthorization),
445
447
  signature: z.optional(u.hex()),
448
+ username: z.optional(z.nullable(z.string())),
446
449
  }),
447
450
  }
448
451
 
@@ -546,6 +549,7 @@ export namespace wallet_deposit {
546
549
  z.object({
547
550
  address: z.optional(u.address()),
548
551
  chainId: z.optional(u.number()),
552
+ displayName: z.optional(z.string()),
549
553
  token: z.optional(u.address()),
550
554
  value: z.optional(z.string()),
551
555
  }),
@@ -40,6 +40,7 @@ export function useEnsureVisibility(
40
40
 
41
41
  const observer = new IntersectionObserver(
42
42
  (entries) => {
43
+ if (document.visibilityState === 'hidden') return
43
44
  const entry = entries[0]
44
45
  if (!entry) return
45
46
  const isVisible =
@@ -78,6 +79,81 @@ export function useState(
78
79
  return useStore(remote.store, selector as never)
79
80
  }
80
81
 
82
+ const bundledFonts = new Set(['Pilat', 'TT Norms', 'iA Writer Quattro'])
83
+
84
+ /** Applies theme overrides from URL search params and live messenger updates. */
85
+ export function useTheme(remote?: CoreRemote.Remote | undefined) {
86
+ useEffect(() => {
87
+ if (typeof window === 'undefined') return
88
+
89
+ const params = new URLSearchParams(window.location.search)
90
+ applyTheme({
91
+ accent: params.get('accent') ?? undefined,
92
+ radius: params.get('radius') ?? undefined,
93
+ font: params.get('font') ?? undefined,
94
+ scheme: params.get('scheme') ?? undefined,
95
+ })
96
+
97
+ return () => clearTheme()
98
+ }, [])
99
+
100
+ useEffect(() => {
101
+ if (!remote) return
102
+ return remote.messenger.on('theme', (payload) => {
103
+ clearTheme()
104
+ applyTheme(payload)
105
+ })
106
+ }, [remote])
107
+ }
108
+
109
+ /** Applies theme values to the document root. */
110
+ function applyTheme(theme: {
111
+ accent?: string | undefined
112
+ radius?: string | undefined
113
+ font?: string | undefined
114
+ scheme?: string | undefined
115
+ }) {
116
+ const root = document.documentElement
117
+ const { accent, radius, font, scheme } = theme
118
+
119
+ if (accent) {
120
+ const isHex = accent.startsWith('#')
121
+ root.setAttribute('data-accent', isHex ? 'custom' : accent)
122
+ if (isHex) root.style.setProperty('--accent-base', accent)
123
+ }
124
+ if (scheme) root.style.colorScheme = scheme
125
+ if (radius) root.setAttribute('data-radius', radius)
126
+ if (font) {
127
+ root.setAttribute('data-font', font === 'System' ? 'system' : font)
128
+ if (font === 'System') {
129
+ root.style.setProperty('--font-body', 'ui-sans-serif, system-ui, sans-serif')
130
+ return
131
+ }
132
+ if (!bundledFonts.has(font)) {
133
+ const id = `gf-${font.replace(/\s/g, '-')}`
134
+ if (!document.getElementById(id)) {
135
+ const link = document.createElement('link')
136
+ link.id = id
137
+ link.rel = 'stylesheet'
138
+ link.href = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(font)}:wght@400;500;600&display=swap`
139
+ document.head.appendChild(link)
140
+ }
141
+ }
142
+ root.style.setProperty('--font-body', `'${font}', sans-serif`)
143
+ }
144
+ }
145
+
146
+ /** Removes all theme overrides from the document root. */
147
+ function clearTheme() {
148
+ const root = document.documentElement
149
+ root.removeAttribute('data-accent')
150
+ root.removeAttribute('data-radius')
151
+ root.removeAttribute('data-font')
152
+ root.style.removeProperty('color-scheme')
153
+ root.style.removeProperty('--accent-base')
154
+ root.style.removeProperty('--font-body')
155
+ }
156
+
81
157
  export declare namespace useEnsureVisibility {
82
158
  type Options = {
83
159
  /** Whether visibility monitoring is enabled. @default true */
@@ -91,7 +91,7 @@ describe('create', () => {
91
91
  expiry: Math.floor(Date.now() / 1000) + 3600,
92
92
  }),
93
93
  chains: [chain],
94
- host: 'https://wallet.tempo.xyz',
94
+ host: 'https://wallet-next.tempo.xyz',
95
95
  open: browser.open,
96
96
  redirectUri: 'accounts-playground://auth',
97
97
  secureStorage,
@@ -3,7 +3,15 @@ import { reactNative } from './adapter.js'
3
3
 
4
4
  /** Creates a provider for React Native apps using system browser authentication. */
5
5
  export function create(options: create.Options): create.ReturnType {
6
- const { host = 'https://wallet.tempo.xyz', redirectUri, open, secureStorage, ...rest } = options
6
+ const {
7
+ // TODO: use the new host
8
+ // host = 'https://wallet-next.tempo.xyz',
9
+ host = 'https://wallet.tempo.xyz',
10
+ redirectUri,
11
+ open,
12
+ secureStorage,
13
+ ...rest
14
+ } = options
7
15
 
8
16
  return CoreProvider.create({
9
17
  ...rest,
@@ -408,7 +408,7 @@ export function reactNative(options: reactNative.Options): Adapter.Adapter {
408
408
 
409
409
  export declare namespace reactNative {
410
410
  export type Options = {
411
- /** Host URL for the mobile auth page. @default "https://wallet.tempo.xyz" */
411
+ /** Host URL for the mobile auth page. @default "https://wallet-next.tempo.xyz" */
412
412
  host: string
413
413
  /** Provider display name. @default "Tempo Mobile" */
414
414
  name?: string | undefined
@@ -491,6 +491,8 @@ function buildAuthUrl(
491
491
  state: string
492
492
  },
493
493
  ): string {
494
+ // TODO: use the new host
495
+ // const url = new URL('/remote/auth/mobile', host)
494
496
  const url = new URL('/mobile-auth', host)
495
497
  url.searchParams.set('pubKey', params.pubKey)
496
498
  if (params.keyType) url.searchParams.set('keyType', params.keyType)
@@ -361,6 +361,10 @@ export const Policy = {
361
361
  /**
362
362
  * Instantiates a CLI auth helper with shared defaults and cached clients.
363
363
  *
364
+ *
365
+ * @param {from.Options} options - Shared CLI auth defaults.
366
+ * @returns {CliAuth} CLI auth helper.
367
+ *
364
368
  * @example
365
369
  * ```ts
366
370
  * import { CliAuth } from 'accounts/server'
@@ -370,10 +374,10 @@ export const Policy = {
370
374
  * })
371
375
  *
372
376
  * const created = await cli.createDeviceCode({ request })
377
+ * const authorized = await cli.authorize({ request })
378
+ * const polled = await cli.poll({ request })
379
+ * const pending = await cli.pending({ code })
373
380
  * ```
374
- *
375
- * @param options - Shared CLI auth defaults.
376
- * @returns CLI auth helper.
377
381
  */
378
382
  export function from(options: from.Options = {}): CliAuth {
379
383
  const cache = createClientCache(options)
@@ -556,8 +560,25 @@ export declare namespace from {
556
560
  /**
557
561
  * Creates and stores a new device code.
558
562
  *
559
- * @param options - Shared defaults plus the incoming request.
560
- * @returns Created device code.
563
+ * @param {createDeviceCode.Options} options - Shared defaults plus the incoming request.
564
+ * @returns {Promise<createDeviceCode.ReturnType>} Created device code.
565
+ *
566
+ * @example
567
+ * ```ts
568
+ * import { Hono } from 'hono'
569
+ * import { CliAuth } from 'accounts/server'
570
+ * import { zValidator } from '@hono/zod-validator'
571
+ *
572
+ * export default new Hono<{ Bindings: Cloudflare.Env }>()
573
+ * // ... other routes (`/authorize`, `/poll:code`, `/pending:code`)
574
+ * .post('/code',
575
+ * zValidator('json', CliAuth.createRequest),
576
+ * async (c) => {
577
+ * const request = c.req.valid('json')
578
+ * const result = await CliAuth.createDeviceCode({ request })
579
+ * return c.json(z.encode(CliAuth.createResponse, result))
580
+ * })
581
+ * ```
561
582
  */
562
583
  export async function createDeviceCode(
563
584
  options: createDeviceCode.Options,
@@ -583,8 +604,24 @@ export declare namespace createDeviceCode {
583
604
  /**
584
605
  * Looks up a pending device code for browser approval UIs.
585
606
  *
586
- * @param options - Shared defaults plus the pending lookup parameters.
587
- * @returns Pending device-code payload.
607
+ * @param {pending.Options} options - Shared defaults plus the pending lookup parameters.
608
+ * @returns {Promise<pending.ReturnType>} Pending device-code payload.
609
+ *
610
+ * @example
611
+ * ```ts
612
+ * import { Hono } from 'hono'
613
+ * import { CliAuth } from 'accounts/server'
614
+ * import { zValidator } from '@hono/zod-validator'
615
+ *
616
+ * export default new Hono<{ Bindings: Cloudflare.Env }>()
617
+ * // ... other routes (`/code`, `/authorize`, `/poll:code`)
618
+ * .get('/pending:code',
619
+ * zValidator('param', z.object({ code: z.string() })),
620
+ * async (c) => {
621
+ * const code = c.req.param('code')
622
+ * const result = await CliAuth.pending({ code })
623
+ * return c.json(z.encode(CliAuth.pendingResponse, result))
624
+ * })
588
625
  */
589
626
  export async function pending(options: pending.Options): Promise<pending.ReturnType> {
590
627
  const { code, ...rest } = options
@@ -608,8 +645,25 @@ export declare namespace pending {
608
645
  /**
609
646
  * Polls a device code with PKCE verification.
610
647
  *
611
- * @param options - Shared defaults plus the poll parameters.
612
- * @returns Pending, authorized, or expired poll response.
648
+ * @param {poll.Options} options - Shared defaults plus the poll parameters.
649
+ * @returns {Promise<poll.ReturnType>} Pending, authorized, or expired poll response.
650
+ *
651
+ * @example
652
+ * ```ts
653
+ * import { Hono } from 'hono'
654
+ * import { CliAuth } from 'accounts/server'
655
+ * import { zValidator } from '@hono/zod-validator'
656
+ *
657
+ * export default new Hono<{ Bindings: Cloudflare.Env }>()
658
+ * // ... other routes (`/code`, `/authorize`, `/pending:code`)
659
+ * .post('/poll:code',
660
+ * zValidator('json', CliAuth.pollRequest),
661
+ * async (c) => {
662
+ * const request = c.req.valid('json')
663
+ * const result = await CliAuth.poll({ request })
664
+ * return c.json(z.encode(CliAuth.pollResponse, result))
665
+ * })
666
+ * ```
613
667
  */
614
668
  export async function poll(options: poll.Options): Promise<poll.ReturnType> {
615
669
  const { code, request, ...rest } = options
@@ -635,8 +689,25 @@ export declare namespace poll {
635
689
  /**
636
690
  * Authorizes a pending device code after validating the signed key authorization.
637
691
  *
638
- * @param options - Shared defaults plus the authorization request.
639
- * @returns Authorized response body.
692
+ * @param {authorize.Options} options - Shared defaults plus the authorization request.
693
+ * @returns {Promise<authorize.ReturnType>} Authorized response body.
694
+ *
695
+ * @example
696
+ * ```ts
697
+ * import { Hono } from 'hono'
698
+ * import { CliAuth } from 'accounts/server'
699
+ * import { zValidator } from '@hono/zod-validator'
700
+ *
701
+ * export default new Hono<{ Bindings: Cloudflare.Env }>()
702
+ * // ... other routes (`/code`, `/poll:code`, `/pending:code`)
703
+ * .post('/authorize',
704
+ * zValidator('json', CliAuth.authorizeRequest),
705
+ * async (c) => {
706
+ * const request = c.req.valid('json')
707
+ * const result = await CliAuth.authorize({ request })
708
+ * return c.json(z.encode(CliAuth.authorizeResponse, result))
709
+ * })
710
+ * ```
640
711
  */
641
712
  export async function authorize(options: authorize.Options): Promise<authorize.ReturnType> {
642
713
  const { client, request, ...rest } = options
@@ -15,8 +15,8 @@ import { type Handler, from } from '../../Handler.js'
15
15
  * - `POST /auth/pkce/poll/:code`
16
16
  * - `POST /auth/pkce`
17
17
  *
18
- * @param options - Options.
19
- * @returns Request handler.
18
+ * @param {codeAuth.Options} options - Options.
19
+ * @returns {Handler} Request handler.
20
20
  */
21
21
  export function codeAuth(options: codeAuth.Options = {}): Handler {
22
22
  const {