@tempoxyz/id 0.0.1 → 0.0.2
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/app/server/.vite/manifest.json +65 -65
- package/dist/app/server/assets/Grant-D_tun0mF.js.map +1 -1
- package/dist/app/server/assets/authorize-DXYo-K2n-B-6vqhvc.js +56 -0
- package/dist/app/server/assets/authorize-DXYo-K2n-B-6vqhvc.js.map +1 -0
- package/dist/app/server/assets/core-FSS9-r35.js +56 -0
- package/dist/app/server/assets/core-FSS9-r35.js.map +1 -0
- package/dist/app/server/assets/default-u_dwuiYb-Bby14ucv.js +2 -0
- package/dist/app/server/assets/{default-u_dwuiYb-DOrMMcg9.js.map → default-u_dwuiYb-Bby14ucv.js.map} +1 -1
- package/dist/app/server/assets/{dist-CoH0hD1s.js → dist-B9KiBJYw.js} +2 -2
- package/dist/app/server/assets/{dist-CoH0hD1s.js.map → dist-B9KiBJYw.js.map} +1 -1
- package/dist/app/server/assets/{dist-DcICI3fd.js → dist-CU1NXSiz.js} +2 -2
- package/dist/app/server/assets/{dist-DcICI3fd.js.map → dist-CU1NXSiz.js.map} +1 -1
- package/dist/app/server/assets/dist-CetH4JSY.js +1 -0
- package/dist/app/server/assets/{effect-QlVUlMFu-mXPkuQTb.js → effect-QlVUlMFu-Cm-eTtaO.js} +2 -2
- package/dist/app/server/assets/{effect-QlVUlMFu-mXPkuQTb.js.map → effect-QlVUlMFu-Cm-eTtaO.js.map} +1 -1
- package/dist/app/server/assets/factory-g3i_Ffi5.js +5 -0
- package/dist/app/server/assets/{factory-dvoOlKer.js.map → factory-g3i_Ffi5.js.map} +1 -1
- package/dist/app/server/assets/get-id-field-BpOAoqqI.js +6 -0
- package/dist/app/server/assets/get-id-field-BpOAoqqI.js.map +1 -0
- package/dist/app/server/assets/{index-CLddUTqr-CC33CMvS.js → index-CLddUTqr-CmgkbWi9.js} +2 -2
- package/dist/app/server/assets/{index-CLddUTqr-CC33CMvS.js.map → index-CLddUTqr-CmgkbWi9.js.map} +1 -1
- package/dist/app/server/assets/{index-DZEfthgZ-Ec2MMQ_e.js → index-DZEfthgZ-BnPmps9O.js} +2 -2
- package/dist/app/server/assets/{index-DZEfthgZ-Ec2MMQ_e.js.map → index-DZEfthgZ-BnPmps9O.js.map} +1 -1
- package/dist/app/server/assets/kysely-adapter-B8VoPArC.js +1 -0
- package/dist/app/server/assets/{schema-check-oLqvHR8y.js → schema-check-Bgwl1v0_.js} +2 -2
- package/dist/app/server/assets/{schema-check-oLqvHR8y.js.map → schema-check-Bgwl1v0_.js.map} +1 -1
- package/dist/app/server/assets/{sury-CWZTCd75-CDaOS2oM.js → sury-CWZTCd75-Bd_PzqCV.js} +2 -2
- package/dist/app/server/assets/{sury-CWZTCd75-CDaOS2oM.js.map → sury-CWZTCd75-Bd_PzqCV.js.map} +1 -1
- package/dist/app/server/assets/{valibot--1zFm7rT-CIKoH3MS.js → valibot--1zFm7rT-Dd2aEhWG.js} +2 -2
- package/dist/app/server/assets/{valibot--1zFm7rT-CIKoH3MS.js.map → valibot--1zFm7rT-Dd2aEhWG.js.map} +1 -1
- package/dist/app/server/assets/{valibot-D_HTw1Gn-BdPyLkXt.js → valibot-D_HTw1Gn-C1l3va6F.js} +2 -2
- package/dist/app/server/assets/{valibot-D_HTw1Gn-BdPyLkXt.js.map → valibot-D_HTw1Gn-C1l3va6F.js.map} +1 -1
- package/dist/app/server/assets/{zod-Bwrt9trS-BezI5Gi1.js → zod-Bwrt9trS-Dry2w0in.js} +2 -2
- package/dist/app/server/assets/{zod-Bwrt9trS-BezI5Gi1.js.map → zod-Bwrt9trS-Dry2w0in.js.map} +1 -1
- package/dist/app/server/assets/{zod-DSgpEGAE-DZ7M_Q33.js → zod-DSgpEGAE-BZMP86UA.js} +2 -2
- package/dist/app/server/assets/{zod-DSgpEGAE-DZ7M_Q33.js.map → zod-DSgpEGAE-BZMP86UA.js.map} +1 -1
- package/dist/app/server/index.js +35 -35
- package/dist/app/server/index.js.map +1 -1
- package/dist/client/Auth.d.ts +1 -1
- package/dist/client/Auth.d.ts.map +1 -1
- package/dist/client/Auth.js.map +1 -1
- package/dist/client/Decorator.d.ts +3 -1
- package/dist/client/Decorator.d.ts.map +1 -1
- package/dist/client/Decorator.js +1 -0
- package/dist/client/Decorator.js.map +1 -1
- package/dist/client/actions/passkey.d.ts +63 -2
- package/dist/client/actions/passkey.d.ts.map +1 -1
- package/dist/client/actions/passkey.js +121 -3
- package/dist/client/actions/passkey.js.map +1 -1
- package/dist/internal/Apps.d.ts +14 -0
- package/dist/internal/Apps.d.ts.map +1 -1
- package/dist/internal/Apps.js +14 -2
- package/dist/internal/Apps.js.map +1 -1
- package/dist/internal/Auth.d.ts +7 -0
- package/dist/internal/Auth.d.ts.map +1 -1
- package/dist/internal/Auth.js +2 -1
- package/dist/internal/Auth.js.map +1 -1
- package/dist/internal/Authorize.d.ts.map +1 -1
- package/dist/internal/Authorize.js +13 -13
- package/dist/internal/Authorize.js.map +1 -1
- package/dist/internal/ErrorResponse.d.ts +5 -0
- package/dist/internal/ErrorResponse.d.ts.map +1 -1
- package/dist/internal/ErrorResponse.js +8 -0
- package/dist/internal/ErrorResponse.js.map +1 -1
- package/dist/internal/KeyAuthorization.d.ts.map +1 -1
- package/dist/internal/KeyAuthorization.js +28 -28
- package/dist/internal/KeyAuthorization.js.map +1 -1
- package/dist/internal/Owner.d.ts +16 -0
- package/dist/internal/Owner.d.ts.map +1 -1
- package/dist/internal/Owner.js.map +1 -1
- package/dist/internal/Passkey.d.ts +66 -4
- package/dist/internal/Passkey.d.ts.map +1 -1
- package/dist/internal/Passkey.js +66 -30
- package/dist/internal/Passkey.js.map +1 -1
- package/dist/internal/Privy.d.ts +27 -0
- package/dist/internal/Privy.d.ts.map +1 -1
- package/dist/internal/Privy.js +82 -0
- package/dist/internal/Privy.js.map +1 -1
- package/dist/internal/TempoAccount.d.ts +57 -0
- package/dist/internal/TempoAccount.d.ts.map +1 -1
- package/dist/internal/TempoAccount.js +118 -5
- package/dist/internal/TempoAccount.js.map +1 -1
- package/dist/internal/owners/Privy.d.ts.map +1 -1
- package/dist/internal/owners/Privy.js +11 -2
- package/dist/internal/owners/Privy.js.map +1 -1
- package/package.json +1 -1
- package/src/api/account.ts +140 -103
- package/src/api/authenticate.ts +48 -51
- package/src/api/authorize.ts +35 -36
- package/src/api/device.ts +20 -23
- package/src/api/health.ts +1 -2
- package/src/api/lib/AccountStep.ts +75 -0
- package/src/api/lib/Auth.ts +6 -0
- package/src/api/lib/Cors.ts +3 -0
- package/src/api/lib/KeySigner.ts +1 -1
- package/src/api/oauth.ts +23 -22
- package/src/client/Auth.ts +1 -0
- package/src/client/Decorator.ts +6 -1
- package/src/client/actions/passkey.ts +197 -3
- package/src/internal/Apps.ts +26 -2
- package/src/internal/Auth.ts +4 -0
- package/src/internal/Authorize.ts +13 -15
- package/src/internal/ErrorResponse.ts +10 -0
- package/src/internal/KeyAuthorization.ts +28 -31
- package/src/internal/OpenApi.ts +35 -0
- package/src/internal/Owner.ts +16 -0
- package/src/internal/Passkey.ts +99 -34
- package/src/internal/Privy.ts +128 -0
- package/src/internal/TempoAccount.ts +221 -7
- package/src/internal/owners/Privy.ts +19 -6
- package/dist/app/server/assets/authorize-DXYo-K2n-rmQQ42x1.js +0 -56
- package/dist/app/server/assets/authorize-DXYo-K2n-rmQQ42x1.js.map +0 -1
- package/dist/app/server/assets/core-BBrZXfrB.js +0 -56
- package/dist/app/server/assets/core-BBrZXfrB.js.map +0 -1
- package/dist/app/server/assets/default-u_dwuiYb-DOrMMcg9.js +0 -2
- package/dist/app/server/assets/dist-CR6FW-yS.js +0 -1
- package/dist/app/server/assets/factory-dvoOlKer.js +0 -5
- package/dist/app/server/assets/get-id-field-OwLpfjj2.js +0 -6
- package/dist/app/server/assets/get-id-field-OwLpfjj2.js.map +0 -1
- package/dist/app/server/assets/kysely-adapter-B8tQwTcu.js +0 -1
package/src/api/authorize.ts
CHANGED
|
@@ -45,17 +45,17 @@ namespace schema {
|
|
|
45
45
|
.union([
|
|
46
46
|
z.literal(true).meta({
|
|
47
47
|
description:
|
|
48
|
-
"
|
|
48
|
+
"Returns a passkey step for a passkey-owned account, in place of `webauthn_required`. The passkey's relying party must cover the page's origin.",
|
|
49
49
|
}),
|
|
50
50
|
z.object({ assertion: Passkey.schema.assertion }).meta({
|
|
51
51
|
description:
|
|
52
|
-
"The passkey's assertion over the
|
|
52
|
+
"The passkey's assertion over the passkey step's challenge, which signs the key.",
|
|
53
53
|
}),
|
|
54
54
|
])
|
|
55
55
|
.optional()
|
|
56
56
|
.meta({
|
|
57
57
|
description:
|
|
58
|
-
"Whether the
|
|
58
|
+
"Whether the page can sign the key with the account's passkey. Without it, a passkey-owned account returns `webauthn_required`.",
|
|
59
59
|
}),
|
|
60
60
|
}),
|
|
61
61
|
)
|
|
@@ -69,7 +69,7 @@ namespace schema {
|
|
|
69
69
|
export const json = z.object({
|
|
70
70
|
account: TempoAccount.schema.tempoAccount.shape.address.meta({
|
|
71
71
|
description:
|
|
72
|
-
"The address of the account
|
|
72
|
+
"The address of the selected account, one of the user's accounts in the app's realms.",
|
|
73
73
|
examples: ['0x8C3b5d0e2A4f6B1c9D7e3F5a0B2c4D6e8F1a3B5c'],
|
|
74
74
|
}),
|
|
75
75
|
oauth_query: z
|
|
@@ -78,7 +78,7 @@ namespace schema {
|
|
|
78
78
|
.max(8192)
|
|
79
79
|
.meta({
|
|
80
80
|
description:
|
|
81
|
-
"The
|
|
81
|
+
"The signed query string of the account page, which names the app's pending authorization request.",
|
|
82
82
|
examples: [
|
|
83
83
|
'client_id=Qk3vT9xZbLmN2pRw8YcHjF6sDaE4uGtK&response_type=code&exp=1760000000&sig=Gx3kB0',
|
|
84
84
|
],
|
|
@@ -88,7 +88,7 @@ namespace schema {
|
|
|
88
88
|
.optional()
|
|
89
89
|
.meta({
|
|
90
90
|
description:
|
|
91
|
-
"The passkey's assertion over the
|
|
91
|
+
"The passkey's assertion over the passkey step's challenge, which signs the key.",
|
|
92
92
|
}),
|
|
93
93
|
})
|
|
94
94
|
|
|
@@ -97,7 +97,7 @@ namespace schema {
|
|
|
97
97
|
.object({
|
|
98
98
|
redirect: z.string().meta({
|
|
99
99
|
description:
|
|
100
|
-
"
|
|
100
|
+
"The URL to send the browser to next: the approval page, or the app's `redirect_uri` with a code.",
|
|
101
101
|
examples: [
|
|
102
102
|
'https://app.example.com/callback?code=Vb7mQ2xLkP9sTzR4&state=af0ifjsldkj',
|
|
103
103
|
],
|
|
@@ -115,12 +115,12 @@ namespace schema {
|
|
|
115
115
|
export const json = z.object({
|
|
116
116
|
accept: z.boolean().meta({
|
|
117
117
|
description:
|
|
118
|
-
'Whether the user approves the request. `false`
|
|
118
|
+
'Whether the user approves the request. `false` returns `access_denied` to the app.',
|
|
119
119
|
examples: [true],
|
|
120
120
|
}),
|
|
121
121
|
account: TempoAccount.schema.tempoAccount.shape.address.optional().meta({
|
|
122
122
|
description:
|
|
123
|
-
|
|
123
|
+
'The Tempo Account that signs the key, as the approval page showed it. Ignored if the key names its account.',
|
|
124
124
|
examples: ['0x8C3b5d0e2A4f6B1c9D7e3F5a0B2c4D6e8F1a3B5c'],
|
|
125
125
|
}),
|
|
126
126
|
oauth_query: z
|
|
@@ -129,7 +129,7 @@ namespace schema {
|
|
|
129
129
|
.max(8192)
|
|
130
130
|
.meta({
|
|
131
131
|
description:
|
|
132
|
-
"The
|
|
132
|
+
"The signed query string of the approval page, which names the app's pending authorization request.",
|
|
133
133
|
examples: [
|
|
134
134
|
'client_id=Qk3vT9xZbLmN2pRw8YcHjF6sDaE4uGtK&response_type=code&exp=1760000000&sig=Gx3kB0',
|
|
135
135
|
],
|
|
@@ -139,7 +139,7 @@ namespace schema {
|
|
|
139
139
|
.optional()
|
|
140
140
|
.meta({
|
|
141
141
|
description:
|
|
142
|
-
"The passkey's assertion over the
|
|
142
|
+
"The passkey's assertion over the passkey step's challenge, which signs the key.",
|
|
143
143
|
}),
|
|
144
144
|
session_id: z
|
|
145
145
|
.string()
|
|
@@ -147,7 +147,7 @@ namespace schema {
|
|
|
147
147
|
.max(256)
|
|
148
148
|
.meta({
|
|
149
149
|
description:
|
|
150
|
-
'The ID
|
|
150
|
+
'The session ID that the approval page showed. The decision fails if another session signed in since.',
|
|
151
151
|
examples: ['8nVq3kZ0bT5wXy2mLp7sR4dA'],
|
|
152
152
|
}),
|
|
153
153
|
skip_upgrade: z
|
|
@@ -155,7 +155,7 @@ namespace schema {
|
|
|
155
155
|
.optional()
|
|
156
156
|
.meta({
|
|
157
157
|
description:
|
|
158
|
-
"Whether the
|
|
158
|
+
"Whether to sign the key before a pending upgrade of the wallet.tempo passkey's account. The key then stops working after the upgrade, unless the upgrade registers it.",
|
|
159
159
|
examples: [true],
|
|
160
160
|
}),
|
|
161
161
|
})
|
|
@@ -167,7 +167,7 @@ namespace schema {
|
|
|
167
167
|
.object({
|
|
168
168
|
redirect: z.url().meta({
|
|
169
169
|
description:
|
|
170
|
-
"The app's `redirect_uri`, with
|
|
170
|
+
"The app's `redirect_uri`, with a code, or with `error=access_denied` for a denial.",
|
|
171
171
|
examples: [
|
|
172
172
|
'https://app.example.com/callback?code=Vb7mQ2xLkP9sTzR4&state=af0ifjsldkj',
|
|
173
173
|
],
|
|
@@ -186,7 +186,7 @@ namespace schema {
|
|
|
186
186
|
.max(8192)
|
|
187
187
|
.meta({
|
|
188
188
|
description:
|
|
189
|
-
"The
|
|
189
|
+
"The signed query string of the approval page, which names the app's pending authorization request.",
|
|
190
190
|
examples: [
|
|
191
191
|
'client_id=Qk3vT9xZbLmN2pRw8YcHjF6sDaE4uGtK&response_type=code&exp=1760000000&sig=Gx3kB0',
|
|
192
192
|
],
|
|
@@ -198,15 +198,14 @@ namespace schema {
|
|
|
198
198
|
.object({
|
|
199
199
|
redirect: z.url().meta({
|
|
200
200
|
description:
|
|
201
|
-
"The app's `redirect_uri`, with the
|
|
201
|
+
"The app's `redirect_uri`, with the key's refusal as `error` and `error_description`.",
|
|
202
202
|
examples: [
|
|
203
203
|
'https://app.example.com/callback?error=invalid_request&state=af0ifjsldkj',
|
|
204
204
|
],
|
|
205
205
|
}),
|
|
206
206
|
})
|
|
207
207
|
.meta({
|
|
208
|
-
description:
|
|
209
|
-
'A key that Tempo ID refuses, which returns the browser to the app.',
|
|
208
|
+
description: 'A refused key, which returns the browser to the app.',
|
|
210
209
|
}),
|
|
211
210
|
z
|
|
212
211
|
.object({
|
|
@@ -225,16 +224,16 @@ namespace schema {
|
|
|
225
224
|
.optional()
|
|
226
225
|
.meta({
|
|
227
226
|
description:
|
|
228
|
-
"The Tempo Account that the approval applies to
|
|
227
|
+
"The Tempo Account that the approval applies to. Present only if the user has more than one account in the app's realms.",
|
|
229
228
|
}),
|
|
230
229
|
app: z.string().meta({
|
|
231
230
|
description:
|
|
232
|
-
"The app's display name, or the host of its redirect URI
|
|
231
|
+
"The app's display name, or the host of its redirect URI if it has none.",
|
|
233
232
|
examples: ['Tempo Cash'],
|
|
234
233
|
}),
|
|
235
234
|
destination: z.string().meta({
|
|
236
235
|
description:
|
|
237
|
-
"The host of the app's redirect URI, or its scheme for a native app. For a third-party app, the host
|
|
236
|
+
"The host of the app's redirect URI, or its scheme for a native app. For a third-party app, the host of its metadata document.",
|
|
238
237
|
examples: ['cash.tempo.xyz'],
|
|
239
238
|
}),
|
|
240
239
|
email: z.email().meta({
|
|
@@ -255,7 +254,7 @@ namespace schema {
|
|
|
255
254
|
}),
|
|
256
255
|
name: Chain.schema.optional().meta({
|
|
257
256
|
description:
|
|
258
|
-
|
|
257
|
+
"The chain's name in `GET /v1/chains`. Absent for an unlisted chain.",
|
|
259
258
|
examples: ['testnet'],
|
|
260
259
|
}),
|
|
261
260
|
})
|
|
@@ -265,7 +264,7 @@ namespace schema {
|
|
|
265
264
|
.optional()
|
|
266
265
|
.meta({
|
|
267
266
|
description:
|
|
268
|
-
"The message that the key's witness binds, such as a sign-in challenge from the app.
|
|
267
|
+
"The message that the key's witness binds, such as a sign-in challenge from the app.",
|
|
269
268
|
examples: [
|
|
270
269
|
'Sign in to app.example.com\nNonce: 7f3a9c2e5b1d4f60',
|
|
271
270
|
],
|
|
@@ -275,7 +274,7 @@ namespace schema {
|
|
|
275
274
|
.optional()
|
|
276
275
|
.meta({
|
|
277
276
|
description:
|
|
278
|
-
|
|
277
|
+
"The requested key's permissions, and the account that signs it. Absent without a key.",
|
|
279
278
|
}),
|
|
280
279
|
logo: z
|
|
281
280
|
.string()
|
|
@@ -283,24 +282,24 @@ namespace schema {
|
|
|
283
282
|
.optional()
|
|
284
283
|
.meta({
|
|
285
284
|
description:
|
|
286
|
-
"The app's logo as a `data:` URL
|
|
285
|
+
"The app's logo, as a `data:` URL. Present only for a third-party app whose site names a logo.",
|
|
287
286
|
examples: ['data:image/png;base64,iVBORw0KGgo='],
|
|
288
287
|
}),
|
|
289
288
|
session: z.string().meta({
|
|
290
289
|
description:
|
|
291
|
-
'The ID of the signed-in session,
|
|
290
|
+
'The ID of the signed-in session, to send to `POST /v1/authorize/consent`.',
|
|
292
291
|
examples: ['8nVq3kZ0bT5wXy2mLp7sR4dA'],
|
|
293
292
|
}),
|
|
294
293
|
verified: z.boolean().meta({
|
|
295
294
|
description:
|
|
296
|
-
'Whether the app is a registered app,
|
|
295
|
+
'Whether the app is a registered app, which the page marks as verified.',
|
|
297
296
|
examples: [true],
|
|
298
297
|
}),
|
|
299
298
|
})
|
|
300
299
|
.nullable()
|
|
301
300
|
.meta({
|
|
302
301
|
description:
|
|
303
|
-
'The request
|
|
302
|
+
'The request, or `null` for an invalid query or an unknown app.',
|
|
304
303
|
}),
|
|
305
304
|
})
|
|
306
305
|
.meta({ description: 'The request that the approval page shows.' }),
|
|
@@ -328,7 +327,7 @@ namespace schema {
|
|
|
328
327
|
}),
|
|
329
328
|
balance: Balance.schema.balance.nullable().meta({
|
|
330
329
|
description:
|
|
331
|
-
"The account's
|
|
330
|
+
"The account's USD balance on the key's chain, or on Tempo mainnet without a key. `null` if the balance cannot be read.",
|
|
332
331
|
}),
|
|
333
332
|
owner:
|
|
334
333
|
TempoAccount.schema.tempoAccount.shape.config.shape.owners.unwrap()
|
|
@@ -356,7 +355,7 @@ namespace schema {
|
|
|
356
355
|
.nullable()
|
|
357
356
|
.meta({
|
|
358
357
|
description:
|
|
359
|
-
'The account step, or `null` for
|
|
358
|
+
'The account step, or `null` for an invalid query or an unknown app.',
|
|
360
359
|
}),
|
|
361
360
|
})
|
|
362
361
|
}
|
|
@@ -535,7 +534,7 @@ export const authorize = new Hono<Env>()
|
|
|
535
534
|
'/v1/authorize',
|
|
536
535
|
OpenApi.describeRoute({
|
|
537
536
|
description:
|
|
538
|
-
"
|
|
537
|
+
"Headless authorization: authorizes the app from a first-party app's page without a redirect, and returns a code or tokens with any signed key. With `passkey: true`, a passkey-owned account first returns a passkey step, and the repeat with its assertion signs the key.",
|
|
539
538
|
responses: OpenApi.responses({
|
|
540
539
|
errors: [
|
|
541
540
|
...Authorize.check.errors,
|
|
@@ -614,7 +613,7 @@ export const authorize = new Hono<Env>()
|
|
|
614
613
|
'/v1/authorize/account',
|
|
615
614
|
OpenApi.describeRoute({
|
|
616
615
|
description:
|
|
617
|
-
"
|
|
616
|
+
"Redirect authorization, account page: selects the Tempo Account that the app uses, and returns the next redirect. The selection holds for the app's later authorizations in the session.",
|
|
618
617
|
responses: OpenApi.responses({
|
|
619
618
|
errors: [
|
|
620
619
|
...BodyLimit.errors,
|
|
@@ -754,7 +753,7 @@ export const authorize = new Hono<Env>()
|
|
|
754
753
|
'/v1/authorize/consent',
|
|
755
754
|
OpenApi.describeRoute({
|
|
756
755
|
description:
|
|
757
|
-
"
|
|
756
|
+
"Redirect authorization, approval page: records the user's decision, and returns the redirect to the app. An approval stores the consent and signs any key.",
|
|
758
757
|
responses: OpenApi.responses({
|
|
759
758
|
errors: [
|
|
760
759
|
...Authorize.checkKey.errors,
|
|
@@ -891,7 +890,7 @@ export const authorize = new Hono<Env>()
|
|
|
891
890
|
'/v1/authorize/request',
|
|
892
891
|
OpenApi.describeRoute({
|
|
893
892
|
description:
|
|
894
|
-
|
|
893
|
+
'Redirect authorization, approval page: returns the app, the Tempo Account, and any key of the request in `oauth_query`. For a refused key, it returns the redirect to the app with the refusal.',
|
|
895
894
|
responses: OpenApi.responses({
|
|
896
895
|
errors: [
|
|
897
896
|
...OpenApi.validate.errors,
|
|
@@ -927,7 +926,7 @@ export const authorize = new Hono<Env>()
|
|
|
927
926
|
'/i/authorize/request',
|
|
928
927
|
OpenApi.describeRoute({
|
|
929
928
|
description:
|
|
930
|
-
"
|
|
929
|
+
"Returns the request in `oauth_query`, as `GET /v1/authorize/request` does, without its accounts. Internal to Tempo ID's approval page.",
|
|
931
930
|
// Internal routes stay out of `/openapi.json` and `/docs`.
|
|
932
931
|
hide: true,
|
|
933
932
|
responses: OpenApi.responses({
|
|
@@ -959,7 +958,7 @@ export const authorize = new Hono<Env>()
|
|
|
959
958
|
'/v1/authorize/accounts',
|
|
960
959
|
OpenApi.describeRoute({
|
|
961
960
|
description:
|
|
962
|
-
"
|
|
961
|
+
"Redirect authorization, account page: returns the app of the request in `oauth_query`, and the user's Tempo Accounts in its realms with their balances.",
|
|
963
962
|
responses: OpenApi.responses({
|
|
964
963
|
errors: [
|
|
965
964
|
...AccountStep.accounts.errors,
|
package/src/api/device.ts
CHANGED
|
@@ -48,41 +48,39 @@ namespace schema {
|
|
|
48
48
|
})
|
|
49
49
|
.optional()
|
|
50
50
|
.meta({
|
|
51
|
-
description:
|
|
52
|
-
'The app that requested the code. Absent when the app has no display name.',
|
|
51
|
+
description: 'The app that requested the code.',
|
|
53
52
|
}),
|
|
54
53
|
challenge: z
|
|
55
54
|
.string()
|
|
56
55
|
.optional()
|
|
57
56
|
.meta({
|
|
58
57
|
description:
|
|
59
|
-
"The message
|
|
58
|
+
"The message that the key's witness binds, as an EIP-191 message.",
|
|
60
59
|
examples: ['Sign in to app.example.com\nNonce: 7f3a9c2e5b1d4f60'],
|
|
61
60
|
}),
|
|
62
61
|
created_at: z.number().meta({
|
|
63
|
-
description: 'When the
|
|
62
|
+
description: 'When the device requested the code, in Unix seconds.',
|
|
64
63
|
examples: [1792075800],
|
|
65
64
|
}),
|
|
66
65
|
device: z
|
|
67
66
|
.string()
|
|
68
67
|
.optional()
|
|
69
68
|
.meta({
|
|
70
|
-
description:
|
|
71
|
-
'The name that the CLI reported, such as its hostname. Absent for a code from `POST /device/code`.',
|
|
69
|
+
description: "The device's name, such as its hostname.",
|
|
72
70
|
examples: ['ada-macbook'],
|
|
73
71
|
}),
|
|
74
72
|
ip: z
|
|
75
73
|
.string()
|
|
76
74
|
.optional()
|
|
77
75
|
.meta({
|
|
78
|
-
description: "The
|
|
76
|
+
description: "The device's IP address.",
|
|
79
77
|
examples: ['203.0.113.7'],
|
|
80
78
|
}),
|
|
81
79
|
key_authorization: KeyAuthorization.schema.description
|
|
82
80
|
.extend({
|
|
83
81
|
account: KeyAuthorization.schema.description.shape.account.meta({
|
|
84
82
|
description:
|
|
85
|
-
'The Tempo Account that
|
|
83
|
+
'The Tempo Account that signs the key. Absent if the user has none, or the accounts cannot be read.',
|
|
86
84
|
examples: ['0x9a073914db0d149375ac2b7fbb72c68c0780fb5f'],
|
|
87
85
|
}),
|
|
88
86
|
chain: z
|
|
@@ -96,40 +94,40 @@ namespace schema {
|
|
|
96
94
|
}),
|
|
97
95
|
name: Chain.schema.optional().meta({
|
|
98
96
|
description:
|
|
99
|
-
|
|
97
|
+
"The chain's name in `GET /v1/chains`. Absent for an unlisted chain.",
|
|
100
98
|
examples: ['testnet'],
|
|
101
99
|
}),
|
|
102
100
|
})
|
|
103
101
|
.optional()
|
|
104
102
|
.meta({
|
|
105
103
|
description:
|
|
106
|
-
'The chain that the key acts on. Absent
|
|
104
|
+
'The chain that the key acts on. Absent after the user decides.',
|
|
107
105
|
}),
|
|
108
106
|
refusal: KeySigner.schema.refusal.optional(),
|
|
109
107
|
})
|
|
110
108
|
.optional()
|
|
111
109
|
.meta({
|
|
112
110
|
description:
|
|
113
|
-
|
|
111
|
+
"The requested key's permissions. Absent without a key.",
|
|
114
112
|
}),
|
|
115
113
|
location: z
|
|
116
114
|
.string()
|
|
117
115
|
.optional()
|
|
118
116
|
.meta({
|
|
119
117
|
description:
|
|
120
|
-
"The
|
|
118
|
+
"The device's city and country, from its IP address.",
|
|
121
119
|
examples: ['Berlin, DE'],
|
|
122
120
|
}),
|
|
123
121
|
status: z.enum(['approved', 'denied', 'pending']).meta({
|
|
124
122
|
description:
|
|
125
|
-
'Whether the user approved or denied the code
|
|
123
|
+
'Whether the user approved or denied the code, or `pending`.',
|
|
126
124
|
examples: ['pending'],
|
|
127
125
|
}),
|
|
128
126
|
user_code: z
|
|
129
127
|
.string()
|
|
130
128
|
.meta({ description: 'The user code.', examples: ['WDJB-MJHT'] }),
|
|
131
129
|
})
|
|
132
|
-
.meta({ description:
|
|
130
|
+
.meta({ description: "The device's request." }),
|
|
133
131
|
})
|
|
134
132
|
}
|
|
135
133
|
|
|
@@ -137,7 +135,7 @@ namespace schema {
|
|
|
137
135
|
export const json = z.object({
|
|
138
136
|
account: TempoAccount.schema.tempoAccount.shape.address.optional().meta({
|
|
139
137
|
description:
|
|
140
|
-
|
|
138
|
+
'The Tempo Account that signs the key, as the device page showed it. Ignored if the key names its account.',
|
|
141
139
|
examples: ['0x8C3b5d0e2A4f6B1c9D7e3F5a0B2c4D6e8F1a3B5c'],
|
|
142
140
|
}),
|
|
143
141
|
code: z
|
|
@@ -149,7 +147,7 @@ namespace schema {
|
|
|
149
147
|
.optional()
|
|
150
148
|
.meta({
|
|
151
149
|
description:
|
|
152
|
-
"The passkey's assertion over the
|
|
150
|
+
"The passkey's assertion over the passkey step's challenge, which signs the key.",
|
|
153
151
|
}),
|
|
154
152
|
})
|
|
155
153
|
|
|
@@ -160,7 +158,7 @@ namespace schema {
|
|
|
160
158
|
.object({
|
|
161
159
|
status: z.literal('approved').meta({
|
|
162
160
|
description:
|
|
163
|
-
|
|
161
|
+
"The device's next poll returns its tokens and any signed key.",
|
|
164
162
|
}),
|
|
165
163
|
})
|
|
166
164
|
.meta({ description: 'A stored approval.' }),
|
|
@@ -173,8 +171,7 @@ namespace schema {
|
|
|
173
171
|
|
|
174
172
|
export const response = z.object({
|
|
175
173
|
status: z.literal('denied').meta({
|
|
176
|
-
description:
|
|
177
|
-
'The user denied the code. The next poll returns `access_denied`.',
|
|
174
|
+
description: "The device's next poll returns `access_denied`.",
|
|
178
175
|
}),
|
|
179
176
|
})
|
|
180
177
|
}
|
|
@@ -344,7 +341,7 @@ export const device = new Hono<Env>()
|
|
|
344
341
|
'/v1/device/requests/:code',
|
|
345
342
|
OpenApi.describeRoute({
|
|
346
343
|
description:
|
|
347
|
-
'
|
|
344
|
+
'Device grant, step 2 of 4: returns the request of a user code, as the device page shows it. The first signed-in user to read a pending code binds it.',
|
|
348
345
|
responses: OpenApi.responses({
|
|
349
346
|
errors: [
|
|
350
347
|
...OpenApi.validate.errors,
|
|
@@ -354,7 +351,7 @@ export const device = new Hono<Env>()
|
|
|
354
351
|
ErrorResponse.CrossSiteRequestError,
|
|
355
352
|
],
|
|
356
353
|
success: {
|
|
357
|
-
description: '
|
|
354
|
+
description: "The device's request.",
|
|
358
355
|
schema: schema.requests.response,
|
|
359
356
|
},
|
|
360
357
|
}),
|
|
@@ -437,7 +434,7 @@ export const device = new Hono<Env>()
|
|
|
437
434
|
'/v1/device/approve',
|
|
438
435
|
OpenApi.describeRoute({
|
|
439
436
|
description:
|
|
440
|
-
"
|
|
437
|
+
"Device grant, step 3 of 4: approves the device code, so the device's next poll of `/oauth2/token` returns tokens and any signed key.",
|
|
441
438
|
responses: OpenApi.responses({
|
|
442
439
|
errors: [
|
|
443
440
|
...BodyLimit.errors,
|
|
@@ -485,7 +482,7 @@ export const device = new Hono<Env>()
|
|
|
485
482
|
'/v1/device/deny',
|
|
486
483
|
OpenApi.describeRoute({
|
|
487
484
|
description:
|
|
488
|
-
"
|
|
485
|
+
"Device grant, step 3 of 4: denies the device code, so the device's next poll of `/oauth2/token` returns `access_denied`.",
|
|
489
486
|
responses: OpenApi.responses({
|
|
490
487
|
errors: [
|
|
491
488
|
...BodyLimit.errors,
|
package/src/api/health.ts
CHANGED
|
@@ -54,8 +54,7 @@ export const health = new Hono<
|
|
|
54
54
|
.get(
|
|
55
55
|
'/health',
|
|
56
56
|
OpenApi.describeRoute({
|
|
57
|
-
description:
|
|
58
|
-
'Responds while the Worker is up, with the version that served the request.',
|
|
57
|
+
description: 'Returns the version of the Worker that serves the request.',
|
|
59
58
|
responses: OpenApi.responses({
|
|
60
59
|
success: {
|
|
61
60
|
description: 'The Worker is up.',
|
|
@@ -71,6 +71,81 @@ export namespace accounts {
|
|
|
71
71
|
export const errors = TempoAccount.held.errors
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Returns every address of the Tempo Accounts that the user holds in the realms of the registered app
|
|
76
|
+
* with `clientId`, as `TempoAccount.addresses` reads them, for an access token's `tempo_accounts` claim.
|
|
77
|
+
* It creates no account and never throws for an upstream kind that fails or misses its deadline,
|
|
78
|
+
* such as Privy during an outage. It logs the failure, and leaves out that kind's addresses.
|
|
79
|
+
*/
|
|
80
|
+
export async function addresses(c: Context<Env>, options: addresses.Options) {
|
|
81
|
+
// Each Privy read spends the quota that every user shares, so the user's grants count against
|
|
82
|
+
// `ACCOUNT_LIMIT`, apart from the user's account reads. Only registered apps, which the seeds
|
|
83
|
+
// list, receive the claim, and each counts on its own, so no other app's loop of refreshes strips
|
|
84
|
+
// its tokens. Past the limit, the grant leaves out Privy's addresses, and reads Privy no more.
|
|
85
|
+
const [admitted, reached] = await Promise.all([
|
|
86
|
+
RateLimit.count(c, {
|
|
87
|
+
binding: 'ACCOUNT_LIMIT',
|
|
88
|
+
key: `${options.userId}:${options.clientId}`,
|
|
89
|
+
}),
|
|
90
|
+
Timing.measure(c, realms(c, options), 'AccountStep.realms'),
|
|
91
|
+
])
|
|
92
|
+
if (!admitted)
|
|
93
|
+
Log.warn(c, {
|
|
94
|
+
fields: { clientId: options.clientId },
|
|
95
|
+
message:
|
|
96
|
+
'An access token left out the Privy addresses of a user past the token read limit.',
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
const result = await Timing.measure(
|
|
100
|
+
c,
|
|
101
|
+
TempoAccount.addresses(c, {
|
|
102
|
+
db,
|
|
103
|
+
env,
|
|
104
|
+
maxAge: addresses.maxAge,
|
|
105
|
+
realms: reached,
|
|
106
|
+
timeout: addresses.timeout,
|
|
107
|
+
upstream: admitted,
|
|
108
|
+
userId: options.userId,
|
|
109
|
+
waitUntil: (promise) => c.executionCtx.waitUntil(promise),
|
|
110
|
+
warn: (entry) => Log.warn(c, entry),
|
|
111
|
+
}),
|
|
112
|
+
'TempoAccount.addresses',
|
|
113
|
+
)
|
|
114
|
+
for (const error of result.errors)
|
|
115
|
+
Log.warn(c, {
|
|
116
|
+
error,
|
|
117
|
+
fields: { clientId: options.clientId },
|
|
118
|
+
message:
|
|
119
|
+
'An access token left out the addresses of an owner kind that failed to list.',
|
|
120
|
+
})
|
|
121
|
+
return result.addresses
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export declare namespace addresses {
|
|
125
|
+
type Options = {
|
|
126
|
+
/** The app's client ID. */
|
|
127
|
+
clientId: string
|
|
128
|
+
/** The user's ID. */
|
|
129
|
+
userId: string
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export namespace addresses {
|
|
134
|
+
/**
|
|
135
|
+
* How old, in seconds, the user's Privy wallets that a grant reads from the data center's cache
|
|
136
|
+
* may be. A refresh within this time reads Privy no more, and a wallet that Privy creates
|
|
137
|
+
* elsewhere appears within it.
|
|
138
|
+
*/
|
|
139
|
+
export const maxAge = 60
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* How long each owner kind that reads an upstream service may list, in milliseconds, so a slow
|
|
143
|
+
* Privy delays no token by more. Privy's own client waits up to 8 seconds for each of two
|
|
144
|
+
* attempts.
|
|
145
|
+
*/
|
|
146
|
+
export const timeout = 750
|
|
147
|
+
}
|
|
148
|
+
|
|
74
149
|
/**
|
|
75
150
|
* Returns whether the first account of a user with `email`, in the realms of the app with
|
|
76
151
|
* `clientId`, waits for the upgrade of a wallet.tempo passkey of the email, as {@link create} reads
|
package/src/api/lib/Auth.ts
CHANGED
|
@@ -40,6 +40,12 @@ export const middleware = createMiddleware<Env>(async (c, next) => {
|
|
|
40
40
|
export function create(c: Context<Env>): core_Auth.Instance {
|
|
41
41
|
return betterAuth(
|
|
42
42
|
core_Auth.options({
|
|
43
|
+
accounts: (options) =>
|
|
44
|
+
Timing.measure(
|
|
45
|
+
c,
|
|
46
|
+
AccountStep.addresses(c, options),
|
|
47
|
+
'AccountStep.addresses',
|
|
48
|
+
),
|
|
43
49
|
// A request that already chose the account, such as `POST /v1/authorize`, needs no account step.
|
|
44
50
|
// A key names its chain, where each account is listed at its address there, so the step reads
|
|
45
51
|
// the accounts that a selection names.
|
package/src/api/lib/Cors.ts
CHANGED
package/src/api/lib/KeySigner.ts
CHANGED
|
@@ -44,7 +44,7 @@ export namespace schema {
|
|
|
44
44
|
])
|
|
45
45
|
.meta({
|
|
46
46
|
description:
|
|
47
|
-
"Why approval cannot sign the key:
|
|
47
|
+
"Why the approval cannot sign the key: another user's account (`foreign_account`), no account (`no_account`), an unusable passkey (`passkey`), unreadable accounts (`unavailable`), or a pending upgrade (`upgrade`).",
|
|
48
48
|
examples: ['passkey'],
|
|
49
49
|
})
|
|
50
50
|
}
|