@atproto/oauth-provider 0.18.3 → 0.19.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/CHANGELOG.md +40 -0
- package/dist/account/account-manager.d.ts +11 -7
- package/dist/account/account-manager.d.ts.map +1 -1
- package/dist/account/account-manager.js +82 -19
- package/dist/account/account-manager.js.map +1 -1
- package/dist/account/account-store.d.ts +47 -13
- package/dist/account/account-store.d.ts.map +1 -1
- package/dist/account/account-store.js +12 -9
- package/dist/account/account-store.js.map +1 -1
- package/dist/account/sign-in-data.d.ts +2 -2
- package/dist/account/sign-up-input.d.ts +5 -5
- package/dist/client/client-manager.d.ts.map +1 -1
- package/dist/client/client-manager.js.map +1 -1
- package/dist/client/client.d.ts +1 -1
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js.map +1 -1
- package/dist/customization/branding.d.ts +52 -52
- package/dist/customization/colors.d.ts +24 -24
- package/dist/customization/colors.d.ts.map +1 -1
- package/dist/customization/customization.d.ts +77 -77
- package/dist/customization/links.d.ts +4 -4
- package/dist/device/device-manager.d.ts +20 -20
- package/dist/device/device-manager.d.ts.map +1 -1
- package/dist/device/device-manager.js.map +1 -1
- package/dist/device/device-store.js +1 -1
- package/dist/device/device-store.js.map +1 -1
- package/dist/dpop/dpop-manager.d.ts.map +1 -1
- package/dist/dpop/dpop-manager.js.map +1 -1
- package/dist/dpop/dpop-nonce.d.ts.map +1 -1
- package/dist/dpop/dpop-nonce.js +1 -1
- package/dist/dpop/dpop-nonce.js.map +1 -1
- package/dist/errors/access-denied-error.d.ts.map +1 -1
- package/dist/errors/account-selection-required-error.d.ts.map +1 -1
- package/dist/errors/authorization-error.d.ts.map +1 -1
- package/dist/errors/authorization-error.js.map +1 -1
- package/dist/errors/consent-required-error.d.ts.map +1 -1
- package/dist/errors/handle-unavailable-error.d.ts +1 -1
- package/dist/errors/handle-unavailable-error.d.ts.map +1 -1
- package/dist/errors/handle-unavailable-error.js.map +1 -1
- package/dist/errors/invalid-authorization-details-error.d.ts.map +1 -1
- package/dist/errors/invalid-client-error.d.ts.map +1 -1
- package/dist/errors/invalid-client-id-error.d.ts.map +1 -1
- package/dist/errors/invalid-client-metadata-error.d.ts.map +1 -1
- package/dist/errors/invalid-credentials-error.d.ts +9 -9
- package/dist/errors/invalid-credentials-error.d.ts.map +1 -1
- package/dist/errors/invalid-credentials-error.js +8 -8
- package/dist/errors/invalid-credentials-error.js.map +1 -1
- package/dist/errors/invalid-dpop-key-binding-error.d.ts.map +1 -1
- package/dist/errors/invalid-dpop-proof-error.d.ts.map +1 -1
- package/dist/errors/invalid-grant-error.d.ts.map +1 -1
- package/dist/errors/invalid-invite-code-error.d.ts.map +1 -1
- package/dist/errors/invalid-redirect-uri-error.d.ts.map +1 -1
- package/dist/errors/invalid-request-error.d.ts.map +1 -1
- package/dist/errors/invalid-scope-error.d.ts.map +1 -1
- package/dist/errors/invalid-token-error.d.ts.map +1 -1
- package/dist/errors/invalid-token-error.js.map +1 -1
- package/dist/errors/login-required-error.d.ts.map +1 -1
- package/dist/errors/oauth-error.d.ts.map +1 -1
- package/dist/errors/oauth-error.js.map +1 -1
- package/dist/errors/second-authentication-factor-required-error.d.ts +2 -2
- package/dist/errors/second-authentication-factor-required-error.d.ts.map +1 -1
- package/dist/errors/second-authentication-factor-required-error.js.map +1 -1
- package/dist/errors/unauthorized-client-error.d.ts.map +1 -1
- package/dist/errors/use-dpop-nonce-error.d.ts.map +1 -1
- package/dist/errors/www-authenticate-error.d.ts.map +1 -1
- package/dist/lexicon/lexicon-getter.d.ts.map +1 -1
- package/dist/lexicon/lexicon-manager.d.ts.map +1 -1
- package/dist/lexicon/lexicon-store.js +1 -1
- package/dist/lexicon/lexicon-store.js.map +1 -1
- package/dist/lib/csp/index.d.ts +3 -3
- package/dist/lib/csp/index.d.ts.map +1 -1
- package/dist/lib/hcaptcha.d.ts +3 -3
- package/dist/lib/hcaptcha.d.ts.map +1 -1
- package/dist/lib/hcaptcha.js.map +1 -1
- package/dist/lib/html/build-document.d.ts.map +1 -1
- package/dist/lib/html/html.d.ts.map +1 -1
- package/dist/lib/html/tags.d.ts.map +1 -1
- package/dist/lib/http/accept.js.map +1 -1
- package/dist/lib/http/context.js.map +1 -1
- package/dist/lib/http/middleware.js.map +1 -1
- package/dist/lib/http/parser.d.ts +5 -5
- package/dist/lib/http/parser.d.ts.map +1 -1
- package/dist/lib/http/response.js.map +1 -1
- package/dist/lib/http/router.d.ts.map +1 -1
- package/dist/lib/http/stream.d.ts +5 -5
- package/dist/lib/http/stream.d.ts.map +1 -1
- package/dist/lib/util/authorization-header.d.ts +1 -1
- package/dist/lib/util/authorization-header.d.ts.map +1 -1
- package/dist/lib/util/color.js.map +1 -1
- package/dist/lib/util/crypto.d.ts +1 -1
- package/dist/lib/util/crypto.d.ts.map +1 -1
- package/dist/lib/util/date.js.map +1 -1
- package/dist/lib/util/function.js +1 -1
- package/dist/lib/util/function.js.map +1 -1
- package/dist/lib/util/type.d.ts.map +1 -1
- package/dist/oauth-hooks.d.ts +77 -4
- package/dist/oauth-hooks.d.ts.map +1 -1
- package/dist/oauth-hooks.js.map +1 -1
- package/dist/oauth-middleware.js.map +1 -1
- package/dist/oauth-provider.d.ts +31 -31
- package/dist/oauth-provider.d.ts.map +1 -1
- package/dist/oauth-provider.js +15 -9
- package/dist/oauth-provider.js.map +1 -1
- package/dist/oauth-verifier.d.ts.map +1 -1
- package/dist/replay/replay-manager.d.ts.map +1 -1
- package/dist/replay/replay-manager.js.map +1 -1
- package/dist/replay/replay-store-memory.d.ts.map +1 -1
- package/dist/replay/replay-store-redis.d.ts.map +1 -1
- package/dist/request/code.d.ts.map +1 -1
- package/dist/request/request-data.d.ts +4 -4
- package/dist/request/request-data.d.ts.map +1 -1
- package/dist/request/request-data.js +1 -1
- package/dist/request/request-data.js.map +1 -1
- package/dist/request/request-manager.d.ts +32 -32
- package/dist/request/request-manager.d.ts.map +1 -1
- package/dist/request/request-manager.js +7 -4
- package/dist/request/request-manager.js.map +1 -1
- package/dist/request/request-store.d.ts +1 -1
- package/dist/request/request-store.js +2 -2
- package/dist/request/request-store.js.map +1 -1
- package/dist/result/authorization-result-authorize-page.d.ts +1 -1
- package/dist/result/authorization-result-authorize-page.js.map +1 -1
- package/dist/router/assets/assets-manifest.js.map +1 -1
- package/dist/router/assets/assets.d.ts +1 -1
- package/dist/router/assets/assets.d.ts.map +1 -1
- package/dist/router/assets/assets.js.map +1 -1
- package/dist/router/assets/send-account-page.d.ts.map +1 -1
- package/dist/router/assets/send-authorization-page.d.ts.map +1 -1
- package/dist/router/assets/send-authorization-page.js +1 -1
- package/dist/router/assets/send-authorization-page.js.map +1 -1
- package/dist/router/assets/send-cookie-error-page.d.ts.map +1 -1
- package/dist/router/assets/send-error-page.d.ts.map +1 -1
- package/dist/router/assets/send-redirect.d.ts +2 -2
- package/dist/router/assets/send-redirect.d.ts.map +1 -1
- package/dist/router/create-api-middleware.d.ts.map +1 -1
- package/dist/router/create-api-middleware.js +87 -51
- package/dist/router/create-api-middleware.js.map +1 -1
- package/dist/signer/access-token-payload.d.ts +1762 -1762
- package/dist/signer/access-token-payload.d.ts.map +1 -1
- package/dist/signer/access-token-payload.js +2 -2
- package/dist/signer/access-token-payload.js.map +1 -1
- package/dist/signer/api-token-payload.d.ts +1738 -1738
- package/dist/signer/api-token-payload.d.ts.map +1 -1
- package/dist/signer/api-token-payload.js +2 -2
- package/dist/signer/api-token-payload.js.map +1 -1
- package/dist/signer/signer.d.ts +6 -187
- package/dist/signer/signer.d.ts.map +1 -1
- package/dist/signer/signer.js.map +1 -1
- package/dist/token/refresh-token.d.ts.map +1 -1
- package/dist/token/token-claims.d.ts +2 -1
- package/dist/token/token-claims.d.ts.map +1 -1
- package/dist/token/token-claims.js.map +1 -1
- package/dist/token/token-data.d.ts +3 -3
- package/dist/token/token-data.d.ts.map +1 -1
- package/dist/token/token-data.js.map +1 -1
- package/dist/token/token-id.d.ts.map +1 -1
- package/dist/token/token-manager.d.ts +3 -3
- package/dist/token/token-manager.d.ts.map +1 -1
- package/dist/token/token-manager.js +14 -14
- package/dist/token/token-manager.js.map +1 -1
- package/dist/token/token-store.d.ts +3 -3
- package/dist/token/token-store.d.ts.map +1 -1
- package/dist/token/token-store.js +3 -3
- package/dist/token/token-store.js.map +1 -1
- package/dist/types/handle.d.ts +5 -1
- package/dist/types/handle.d.ts.map +1 -1
- package/dist/types/handle.js +9 -8
- package/dist/types/handle.js.map +1 -1
- package/package.json +19 -19
- package/src/account/account-manager.ts +123 -20
- package/src/account/account-store.ts +67 -19
- package/src/device/device-store.ts +1 -1
- package/src/errors/invalid-credentials-error.ts +8 -8
- package/src/lexicon/lexicon-store.ts +1 -1
- package/src/lib/util/function.ts +2 -2
- package/src/oauth-hooks.ts +85 -3
- package/src/oauth-provider.ts +17 -9
- package/src/request/request-data.ts +5 -5
- package/src/request/request-manager.ts +11 -4
- package/src/request/request-store.ts +3 -3
- package/src/result/authorization-result-authorize-page.ts +1 -1
- package/src/router/assets/send-authorization-page.ts +1 -1
- package/src/router/create-api-middleware.ts +128 -67
- package/src/signer/access-token-payload.ts +2 -2
- package/src/signer/api-token-payload.ts +2 -2
- package/src/signer/signer.ts +13 -4
- package/src/token/token-claims.ts +2 -1
- package/src/token/token-data.ts +3 -3
- package/src/token/token-manager.ts +15 -15
- package/src/token/token-store.ts +6 -6
- package/src/types/handle.ts +21 -16
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/oidc/sub.d.ts +0 -4
- package/dist/oidc/sub.d.ts.map +0 -1
- package/dist/oidc/sub.js +0 -3
- package/dist/oidc/sub.js.map +0 -1
- package/src/oidc/sub.ts +0 -4
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
2
2
|
import createHttpError from 'http-errors'
|
|
3
3
|
import { z } from 'zod'
|
|
4
|
+
import { Did, didSchema } from '@atproto/did'
|
|
4
5
|
import { signedJwtSchema } from '@atproto/jwk'
|
|
5
6
|
import {
|
|
6
7
|
API_ENDPOINT_PREFIX,
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
OAuthResponseMode,
|
|
17
18
|
oauthRedirectUriSchema,
|
|
18
19
|
oauthResponseModeSchema,
|
|
20
|
+
oauthScopeSchema,
|
|
19
21
|
} from '@atproto/oauth-types'
|
|
20
22
|
import { signInDataSchema } from '../account/sign-in-data.js'
|
|
21
23
|
import { signUpInputSchema } from '../account/sign-up-input.js'
|
|
@@ -49,14 +51,13 @@ import { asArray } from '../lib/util/cast.js'
|
|
|
49
51
|
import { localeSchema } from '../lib/util/locale.js'
|
|
50
52
|
import type { Awaitable } from '../lib/util/type.js'
|
|
51
53
|
import type { OAuthProvider } from '../oauth-provider.js'
|
|
52
|
-
import { Sub, subSchema } from '../oidc/sub.js'
|
|
53
54
|
import { RequestUri, requestUriSchema } from '../request/request-uri.js'
|
|
54
55
|
import { AuthorizationRedirectParameters } from '../result/authorization-redirect-parameters.js'
|
|
55
56
|
import { tokenIdSchema } from '../token/token-id.js'
|
|
56
57
|
import { emailOtpSchema } from '../types/email-otp.js'
|
|
57
58
|
import { emailSchema } from '../types/email.js'
|
|
58
59
|
import { handleSchema } from '../types/handle.js'
|
|
59
|
-
import { newPasswordSchema } from '../types/password.js'
|
|
60
|
+
import { newPasswordSchema, oldPasswordSchema } from '../types/password.js'
|
|
60
61
|
import { validateCsrfToken } from './assets/csrf.js'
|
|
61
62
|
import {
|
|
62
63
|
ERROR_REDIRECT_KEYS,
|
|
@@ -69,8 +70,6 @@ import {
|
|
|
69
70
|
} from './assets/send-redirect.js'
|
|
70
71
|
import type { MiddlewareOptions } from './middleware-options.js'
|
|
71
72
|
|
|
72
|
-
const verifyHandleSchema = z.object({ handle: handleSchema }).strict()
|
|
73
|
-
|
|
74
73
|
export function createApiMiddleware<
|
|
75
74
|
Ctx extends object | void = void,
|
|
76
75
|
Req extends IncomingMessage = IncomingMessage,
|
|
@@ -87,7 +86,7 @@ export function createApiMiddleware<
|
|
|
87
86
|
apiRoute({
|
|
88
87
|
method: 'POST',
|
|
89
88
|
endpoint: '/verify-handle-availability',
|
|
90
|
-
schema:
|
|
89
|
+
schema: z.object({ handle: handleSchema }).strict(),
|
|
91
90
|
async handler() {
|
|
92
91
|
await server.accountManager.verifyHandleAvailability(this.input.handle)
|
|
93
92
|
return { json: { available: true } }
|
|
@@ -116,13 +115,13 @@ export function createApiMiddleware<
|
|
|
116
115
|
// Only "remember" the newly created account if it was not created during an
|
|
117
116
|
// OAuth flow.
|
|
118
117
|
if (remember) {
|
|
119
|
-
await server.accountManager.upsertDeviceAccount(deviceId, account.
|
|
118
|
+
await server.accountManager.upsertDeviceAccount(deviceId, account.did)
|
|
120
119
|
}
|
|
121
120
|
|
|
122
121
|
const ephemeralToken = remember
|
|
123
122
|
? undefined
|
|
124
123
|
: await server.signer.createEphemeralToken({
|
|
125
|
-
sub: account.
|
|
124
|
+
sub: account.did,
|
|
126
125
|
deviceId,
|
|
127
126
|
requestUri: this.requestUri,
|
|
128
127
|
})
|
|
@@ -159,46 +158,21 @@ export function createApiMiddleware<
|
|
|
159
158
|
)
|
|
160
159
|
|
|
161
160
|
if (remember) {
|
|
162
|
-
await server.accountManager.upsertDeviceAccount(deviceId, account.
|
|
161
|
+
await server.accountManager.upsertDeviceAccount(deviceId, account.did)
|
|
163
162
|
} else {
|
|
164
163
|
// In case the user was already signed in, and signed in again, this
|
|
165
164
|
// time without "remember me", let's sign them off of the device.
|
|
166
|
-
await server.accountManager.removeDeviceAccount(deviceId, account.
|
|
165
|
+
await server.accountManager.removeDeviceAccount(deviceId, account.did)
|
|
167
166
|
}
|
|
168
167
|
|
|
169
168
|
const ephemeralToken = remember
|
|
170
169
|
? undefined
|
|
171
170
|
: await server.signer.createEphemeralToken({
|
|
172
|
-
sub: account.
|
|
171
|
+
sub: account.did,
|
|
173
172
|
deviceId,
|
|
174
173
|
requestUri,
|
|
175
174
|
})
|
|
176
175
|
|
|
177
|
-
if (requestUri) {
|
|
178
|
-
// Check if a consent is required for the client, but only if this
|
|
179
|
-
// call is made within the context of an oauth request.
|
|
180
|
-
|
|
181
|
-
const { clientId, parameters } = await server.requestManager.get(
|
|
182
|
-
requestUri,
|
|
183
|
-
deviceId,
|
|
184
|
-
)
|
|
185
|
-
|
|
186
|
-
const { authorizedClients } = await server.accountManager.getAccount(
|
|
187
|
-
account.sub,
|
|
188
|
-
)
|
|
189
|
-
|
|
190
|
-
const json = {
|
|
191
|
-
account,
|
|
192
|
-
ephemeralToken,
|
|
193
|
-
consentRequired: server.checkConsentRequired(
|
|
194
|
-
parameters,
|
|
195
|
-
authorizedClients.get(clientId),
|
|
196
|
-
),
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
return { json }
|
|
200
|
-
}
|
|
201
|
-
|
|
202
176
|
const json = { account, ephemeralToken }
|
|
203
177
|
return { json }
|
|
204
178
|
},
|
|
@@ -211,12 +185,12 @@ export function createApiMiddleware<
|
|
|
211
185
|
endpoint: '/sign-out',
|
|
212
186
|
schema: z
|
|
213
187
|
.object({
|
|
214
|
-
|
|
188
|
+
did: z.union([didSchema, z.array(didSchema)]),
|
|
215
189
|
})
|
|
216
190
|
.strict(),
|
|
217
191
|
rotateDeviceCookies: true,
|
|
218
192
|
async handler() {
|
|
219
|
-
const uniqueSubs = new Set(asArray(this.input.
|
|
193
|
+
const uniqueSubs = new Set(asArray(this.input.did))
|
|
220
194
|
|
|
221
195
|
for (const sub of uniqueSubs) {
|
|
222
196
|
await server.accountManager.removeDeviceAccount(this.deviceId, sub)
|
|
@@ -275,7 +249,7 @@ export function createApiMiddleware<
|
|
|
275
249
|
endpoint: '/update-email-request',
|
|
276
250
|
schema: z
|
|
277
251
|
.object({
|
|
278
|
-
|
|
252
|
+
did: didSchema,
|
|
279
253
|
locale: localeSchema.optional(),
|
|
280
254
|
})
|
|
281
255
|
.strict(),
|
|
@@ -301,23 +275,23 @@ export function createApiMiddleware<
|
|
|
301
275
|
endpoint: '/update-email-confirm',
|
|
302
276
|
schema: z
|
|
303
277
|
.object({
|
|
304
|
-
|
|
278
|
+
did: didSchema,
|
|
305
279
|
email: emailSchema,
|
|
306
280
|
token: emailOtpSchema.optional(),
|
|
307
281
|
locale: localeSchema.optional(),
|
|
308
282
|
})
|
|
309
283
|
.strict(),
|
|
310
284
|
async handler(req, res) {
|
|
311
|
-
|
|
285
|
+
let { account } = await authenticate.call(this, req, res)
|
|
312
286
|
|
|
313
|
-
await server.accountManager.updateEmailConfirm(
|
|
287
|
+
account = await server.accountManager.updateEmailConfirm(
|
|
314
288
|
this.deviceId,
|
|
315
289
|
this.deviceMetadata,
|
|
316
290
|
this.input,
|
|
317
291
|
account,
|
|
318
292
|
)
|
|
319
293
|
|
|
320
|
-
return { json: {
|
|
294
|
+
return { json: { account } }
|
|
321
295
|
},
|
|
322
296
|
}),
|
|
323
297
|
)
|
|
@@ -328,7 +302,7 @@ export function createApiMiddleware<
|
|
|
328
302
|
endpoint: '/verify-email-request',
|
|
329
303
|
schema: z
|
|
330
304
|
.object({
|
|
331
|
-
|
|
305
|
+
did: didSchema,
|
|
332
306
|
locale: localeSchema.optional(),
|
|
333
307
|
})
|
|
334
308
|
.strict(),
|
|
@@ -353,22 +327,22 @@ export function createApiMiddleware<
|
|
|
353
327
|
endpoint: '/verify-email-confirm',
|
|
354
328
|
schema: z
|
|
355
329
|
.object({
|
|
356
|
-
|
|
330
|
+
did: didSchema,
|
|
357
331
|
token: emailOtpSchema,
|
|
358
332
|
email: emailSchema,
|
|
359
333
|
})
|
|
360
334
|
.strict(),
|
|
361
335
|
async handler(req, res) {
|
|
362
|
-
|
|
336
|
+
let { account } = await authenticate.call(this, req, res)
|
|
363
337
|
|
|
364
|
-
await server.accountManager.verifyEmailConfirm(
|
|
338
|
+
account = await server.accountManager.verifyEmailConfirm(
|
|
365
339
|
this.deviceId,
|
|
366
340
|
this.deviceMetadata,
|
|
367
341
|
this.input,
|
|
368
342
|
account,
|
|
369
343
|
)
|
|
370
344
|
|
|
371
|
-
return { json: {
|
|
345
|
+
return { json: { account } }
|
|
372
346
|
},
|
|
373
347
|
}),
|
|
374
348
|
)
|
|
@@ -379,14 +353,104 @@ export function createApiMiddleware<
|
|
|
379
353
|
endpoint: '/update-handle',
|
|
380
354
|
schema: z
|
|
381
355
|
.object({
|
|
382
|
-
|
|
356
|
+
did: didSchema,
|
|
383
357
|
handle: handleSchema,
|
|
384
358
|
})
|
|
385
359
|
.strict(),
|
|
360
|
+
async handler(req, res) {
|
|
361
|
+
let { account } = await authenticate.call(this, req, res)
|
|
362
|
+
|
|
363
|
+
account = await server.accountManager.updateHandle(
|
|
364
|
+
this.deviceId,
|
|
365
|
+
this.deviceMetadata,
|
|
366
|
+
this.input,
|
|
367
|
+
account,
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
return { json: { account } }
|
|
371
|
+
},
|
|
372
|
+
}),
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
router.use(
|
|
376
|
+
apiRoute({
|
|
377
|
+
method: 'POST',
|
|
378
|
+
endpoint: '/deactivate-account',
|
|
379
|
+
schema: z.object({ did: didSchema }).strict(),
|
|
380
|
+
async handler(req, res) {
|
|
381
|
+
let { account } = await authenticate.call(this, req, res)
|
|
382
|
+
|
|
383
|
+
if (!account.deactivated) {
|
|
384
|
+
account = await server.accountManager.deactivateAccount(
|
|
385
|
+
this.deviceId,
|
|
386
|
+
this.deviceMetadata,
|
|
387
|
+
account,
|
|
388
|
+
)
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return { json: { account } }
|
|
392
|
+
},
|
|
393
|
+
}),
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
router.use(
|
|
397
|
+
apiRoute({
|
|
398
|
+
method: 'POST',
|
|
399
|
+
endpoint: '/reactivate-account',
|
|
400
|
+
schema: z.object({ did: didSchema }).strict(),
|
|
401
|
+
async handler(req, res) {
|
|
402
|
+
let { account } = await authenticate.call(this, req, res)
|
|
403
|
+
|
|
404
|
+
if (account.deactivated) {
|
|
405
|
+
account = await server.accountManager.reactivateAccount(
|
|
406
|
+
this.deviceId,
|
|
407
|
+
this.deviceMetadata,
|
|
408
|
+
account,
|
|
409
|
+
)
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
return { json: { account } }
|
|
413
|
+
},
|
|
414
|
+
}),
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
router.use(
|
|
418
|
+
apiRoute({
|
|
419
|
+
method: 'POST',
|
|
420
|
+
endpoint: '/delete-account-request',
|
|
421
|
+
schema: z
|
|
422
|
+
.object({ did: didSchema, locale: localeSchema.optional() })
|
|
423
|
+
.strict(),
|
|
424
|
+
async handler(req, res) {
|
|
425
|
+
const { account } = await authenticate.call(this, req, res)
|
|
426
|
+
|
|
427
|
+
await server.accountManager.deleteAccountRequest(
|
|
428
|
+
this.deviceId,
|
|
429
|
+
this.deviceMetadata,
|
|
430
|
+
this.input,
|
|
431
|
+
account,
|
|
432
|
+
)
|
|
433
|
+
|
|
434
|
+
return { json: { success: true } }
|
|
435
|
+
},
|
|
436
|
+
}),
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
router.use(
|
|
440
|
+
apiRoute({
|
|
441
|
+
method: 'POST',
|
|
442
|
+
endpoint: '/delete-account-confirm',
|
|
443
|
+
schema: z
|
|
444
|
+
.object({
|
|
445
|
+
did: didSchema,
|
|
446
|
+
token: emailOtpSchema,
|
|
447
|
+
password: oldPasswordSchema,
|
|
448
|
+
})
|
|
449
|
+
.strict(),
|
|
386
450
|
async handler(req, res) {
|
|
387
451
|
const { account } = await authenticate.call(this, req, res)
|
|
388
452
|
|
|
389
|
-
await server.accountManager.
|
|
453
|
+
await server.accountManager.deleteAccountConfirm(
|
|
390
454
|
this.deviceId,
|
|
391
455
|
this.deviceMetadata,
|
|
392
456
|
this.input,
|
|
@@ -424,12 +488,12 @@ export function createApiMiddleware<
|
|
|
424
488
|
apiRoute({
|
|
425
489
|
method: 'GET',
|
|
426
490
|
endpoint: '/oauth-sessions',
|
|
427
|
-
schema: z.object({
|
|
491
|
+
schema: z.object({ did: didSchema }).strict(),
|
|
428
492
|
async handler(req, res) {
|
|
429
493
|
const { account } = await authenticate.call(this, req, res)
|
|
430
494
|
|
|
431
495
|
const tokenInfos = await server.tokenManager.listAccountTokens(
|
|
432
|
-
account.
|
|
496
|
+
account.did,
|
|
433
497
|
)
|
|
434
498
|
|
|
435
499
|
const clientIds = tokenInfos.map((tokenInfo) => tokenInfo.data.clientId)
|
|
@@ -468,12 +532,12 @@ export function createApiMiddleware<
|
|
|
468
532
|
apiRoute({
|
|
469
533
|
method: 'GET',
|
|
470
534
|
endpoint: '/account-sessions',
|
|
471
|
-
schema: z.object({
|
|
535
|
+
schema: z.object({ did: didSchema }).strict(),
|
|
472
536
|
async handler(req, res) {
|
|
473
537
|
const { account } = await authenticate.call(this, req, res)
|
|
474
538
|
|
|
475
539
|
const deviceAccounts = await server.accountManager.listAccountDevices(
|
|
476
|
-
account.
|
|
540
|
+
account.did,
|
|
477
541
|
)
|
|
478
542
|
|
|
479
543
|
const json = deviceAccounts.map(
|
|
@@ -499,7 +563,7 @@ export function createApiMiddleware<
|
|
|
499
563
|
apiRoute({
|
|
500
564
|
method: 'POST',
|
|
501
565
|
endpoint: '/revoke-account-session',
|
|
502
|
-
schema: z.object({
|
|
566
|
+
schema: z.object({ did: didSchema, deviceId: deviceIdSchema }).strict(),
|
|
503
567
|
async handler() {
|
|
504
568
|
// @NOTE This route is not authenticated. If a user is able to steal
|
|
505
569
|
// another user's session cookie, we allow them to revoke the device
|
|
@@ -507,7 +571,7 @@ export function createApiMiddleware<
|
|
|
507
571
|
|
|
508
572
|
await server.accountManager.removeDeviceAccount(
|
|
509
573
|
this.input.deviceId,
|
|
510
|
-
this.input.
|
|
574
|
+
this.input.did,
|
|
511
575
|
)
|
|
512
576
|
|
|
513
577
|
return { json: { success: true } }
|
|
@@ -519,7 +583,7 @@ export function createApiMiddleware<
|
|
|
519
583
|
apiRoute({
|
|
520
584
|
method: 'POST',
|
|
521
585
|
endpoint: '/revoke-oauth-session',
|
|
522
|
-
schema: z.object({
|
|
586
|
+
schema: z.object({ did: didSchema, tokenId: tokenIdSchema }).strict(),
|
|
523
587
|
async handler(req, res) {
|
|
524
588
|
const { account } = await authenticate.call(this, req, res)
|
|
525
589
|
|
|
@@ -527,7 +591,7 @@ export function createApiMiddleware<
|
|
|
527
591
|
this.input.tokenId,
|
|
528
592
|
)
|
|
529
593
|
|
|
530
|
-
if (!tokenInfo || tokenInfo.account.
|
|
594
|
+
if (!tokenInfo || tokenInfo.account.did !== account.did) {
|
|
531
595
|
// report this as though the token was not found
|
|
532
596
|
throw new InvalidRequestError(`Invalid token`)
|
|
533
597
|
}
|
|
@@ -544,10 +608,7 @@ export function createApiMiddleware<
|
|
|
544
608
|
method: 'POST',
|
|
545
609
|
endpoint: '/consent',
|
|
546
610
|
schema: z
|
|
547
|
-
.object({
|
|
548
|
-
sub: z.union([subSchema, signedJwtSchema]),
|
|
549
|
-
scope: z.string().optional(),
|
|
550
|
-
})
|
|
611
|
+
.object({ did: didSchema, scope: oauthScopeSchema.optional() })
|
|
551
612
|
.strict(),
|
|
552
613
|
async handler(req, res) {
|
|
553
614
|
if (!this.requestUri) {
|
|
@@ -710,7 +771,7 @@ export function createApiMiddleware<
|
|
|
710
771
|
return router.buildMiddleware()
|
|
711
772
|
|
|
712
773
|
async function authenticate(
|
|
713
|
-
this: ApiContext<void, {
|
|
774
|
+
this: ApiContext<void, { did: Did }>,
|
|
714
775
|
req: Req,
|
|
715
776
|
_res: Res,
|
|
716
777
|
) {
|
|
@@ -724,7 +785,7 @@ export function createApiMiddleware<
|
|
|
724
785
|
await server.signer.verifyEphemeralToken(ephemeralToken)
|
|
725
786
|
|
|
726
787
|
if (
|
|
727
|
-
payload.sub === this.input.
|
|
788
|
+
payload.sub === this.input.did &&
|
|
728
789
|
payload.deviceId === this.deviceId &&
|
|
729
790
|
payload.requestUri === this.requestUri
|
|
730
791
|
) {
|
|
@@ -744,7 +805,7 @@ export function createApiMiddleware<
|
|
|
744
805
|
// Ensures the "sub" has an active session on the device
|
|
745
806
|
const deviceAccount = await server.accountManager.getDeviceAccount(
|
|
746
807
|
this.deviceId,
|
|
747
|
-
this.input.
|
|
808
|
+
this.input.did,
|
|
748
809
|
)
|
|
749
810
|
|
|
750
811
|
// The session exists but was created too long ago
|
|
@@ -756,7 +817,7 @@ export function createApiMiddleware<
|
|
|
756
817
|
} catch (err) {
|
|
757
818
|
throw new WWWAuthenticateError(
|
|
758
819
|
'unauthorized',
|
|
759
|
-
`User ${this.input.
|
|
820
|
+
`User ${this.input.did} not authenticated on this device`,
|
|
760
821
|
{ Bearer: {} },
|
|
761
822
|
err,
|
|
762
823
|
)
|
|
@@ -803,9 +864,9 @@ export function createApiMiddleware<
|
|
|
803
864
|
}[keyof ApiEndpoints],
|
|
804
865
|
S extends // A schema that validates the POST input or GET params
|
|
805
866
|
ApiEndpoints[E] extends { method: 'POST'; input: infer I }
|
|
806
|
-
? z.ZodType<I>
|
|
867
|
+
? z.ZodType<I, z.ZodTypeDef, unknown>
|
|
807
868
|
: ApiEndpoints[E] extends { method: 'GET'; params: infer P }
|
|
808
|
-
? z.ZodType<P>
|
|
869
|
+
? z.ZodType<P, z.ZodTypeDef, unknown>
|
|
809
870
|
: void,
|
|
810
871
|
>(options: {
|
|
811
872
|
method: M
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
|
+
import { didSchema } from '@atproto/did'
|
|
2
3
|
import { jwtPayloadSchema } from '@atproto/jwk'
|
|
3
4
|
import { clientIdSchema } from '../client/client-id.js'
|
|
4
|
-
import { subSchema } from '../oidc/sub.js'
|
|
5
5
|
import { tokenIdSchema } from '../token/token-id.js'
|
|
6
6
|
|
|
7
7
|
export const accessTokenPayloadSchema = jwtPayloadSchema
|
|
@@ -9,7 +9,7 @@ export const accessTokenPayloadSchema = jwtPayloadSchema
|
|
|
9
9
|
.extend({
|
|
10
10
|
// Following are required
|
|
11
11
|
jti: tokenIdSchema,
|
|
12
|
-
sub:
|
|
12
|
+
sub: didSchema,
|
|
13
13
|
exp: z.number().int(),
|
|
14
14
|
iat: z.number().int(),
|
|
15
15
|
iss: z.string().min(1),
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { z } from 'zod'
|
|
2
|
+
import { didSchema } from '@atproto/did'
|
|
2
3
|
import { jwtPayloadSchema } from '@atproto/jwk'
|
|
3
4
|
import { deviceIdSchema } from '../oauth-store.js'
|
|
4
|
-
import { subSchema } from '../oidc/sub.js'
|
|
5
5
|
import { requestUriSchema } from '../request/request-uri.js'
|
|
6
6
|
|
|
7
7
|
export const apiTokenPayloadSchema = jwtPayloadSchema
|
|
8
8
|
.extend({
|
|
9
|
-
sub:
|
|
9
|
+
sub: didSchema,
|
|
10
10
|
|
|
11
11
|
deviceId: deviceIdSchema,
|
|
12
12
|
// If the token is bound to a particular authorization request, it can only
|
package/src/signer/signer.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
+
JwtHeader,
|
|
2
3
|
JwtPayload,
|
|
3
4
|
JwtPayloadGetter,
|
|
4
5
|
JwtSignHeader,
|
|
6
|
+
Key,
|
|
5
7
|
Keyset,
|
|
6
8
|
SignedJwt,
|
|
7
9
|
VerifyOptions,
|
|
10
|
+
VerifyResult,
|
|
8
11
|
} from '@atproto/jwk'
|
|
9
12
|
import { EPHEMERAL_SESSION_MAX_AGE } from '../constants.js'
|
|
10
13
|
import { dateToEpoch } from '../lib/util/date.js'
|
|
@@ -29,7 +32,7 @@ export class Signer {
|
|
|
29
32
|
async verify<C extends string = never>(
|
|
30
33
|
token: SignedJwt,
|
|
31
34
|
options?: Omit<VerifyOptions<C>, 'issuer'>,
|
|
32
|
-
) {
|
|
35
|
+
): Promise<VerifyResult<C> & { key: Key }> {
|
|
33
36
|
return this.keyset.verifyJwt<C>(token, {
|
|
34
37
|
...options,
|
|
35
38
|
issuer: [this.issuer],
|
|
@@ -64,7 +67,10 @@ export class Signer {
|
|
|
64
67
|
async verifyAccessToken<C extends string = never>(
|
|
65
68
|
token: SignedJwt,
|
|
66
69
|
options?: Omit<VerifyOptions<C>, 'issuer' | 'typ'>,
|
|
67
|
-
) {
|
|
70
|
+
): Promise<{
|
|
71
|
+
protectedHeader: JwtHeader
|
|
72
|
+
payload: RequiredKey<AccessTokenPayload, C>
|
|
73
|
+
}> {
|
|
68
74
|
const result = await this.verify<C>(token, { ...options, typ: 'at+jwt' })
|
|
69
75
|
return {
|
|
70
76
|
protectedHeader: result.protectedHeader,
|
|
@@ -77,7 +83,7 @@ export class Signer {
|
|
|
77
83
|
|
|
78
84
|
async createEphemeralToken(
|
|
79
85
|
payload: OmitKey<ApiTokenPayload, 'iss' | 'aud' | 'iat'>,
|
|
80
|
-
) {
|
|
86
|
+
): Promise<SignedJwt> {
|
|
81
87
|
return this.sign(
|
|
82
88
|
{
|
|
83
89
|
alg: undefined,
|
|
@@ -94,7 +100,10 @@ export class Signer {
|
|
|
94
100
|
async verifyEphemeralToken<C extends string = never>(
|
|
95
101
|
token: SignedJwt,
|
|
96
102
|
options?: Omit<VerifyOptions<C>, 'issuer' | 'audience' | 'typ'>,
|
|
97
|
-
) {
|
|
103
|
+
): Promise<{
|
|
104
|
+
protectedHeader: JwtHeader
|
|
105
|
+
payload: RequiredKey<ApiTokenPayload, C>
|
|
106
|
+
}> {
|
|
98
107
|
const result = await this.verify<C>(token, {
|
|
99
108
|
...options,
|
|
100
109
|
maxTokenAge: options?.maxTokenAge ?? EPHEMERAL_SESSION_MAX_AGE / 1e3,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Did } from '@atproto/did'
|
|
1
2
|
import { OAuthScope } from '@atproto/oauth-types'
|
|
2
3
|
import { ClientId } from '../client/client-id.js'
|
|
3
4
|
import { TokenId } from './token-id.js'
|
|
@@ -11,7 +12,7 @@ import { TokenId } from './token-id.js'
|
|
|
11
12
|
*/
|
|
12
13
|
export type TokenClaims = {
|
|
13
14
|
jti: TokenId
|
|
14
|
-
sub:
|
|
15
|
+
sub: Did
|
|
15
16
|
iat: number
|
|
16
17
|
exp: number
|
|
17
18
|
aud: string | [string, ...string[]]
|
package/src/token/token-data.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Did } from '@atproto/did'
|
|
1
2
|
import {
|
|
2
3
|
OAuthAuthorizationDetails,
|
|
3
4
|
OAuthAuthorizationRequestParameters,
|
|
@@ -5,7 +6,6 @@ import {
|
|
|
5
6
|
import { ClientAuth, ClientAuthLegacy } from '../client/client-auth.js'
|
|
6
7
|
import { ClientId } from '../client/client-id.js'
|
|
7
8
|
import { DeviceId } from '../device/device-id.js'
|
|
8
|
-
import { Sub } from '../oidc/sub.js'
|
|
9
9
|
import { Code } from '../request/code.js'
|
|
10
10
|
|
|
11
11
|
export type {
|
|
@@ -13,9 +13,9 @@ export type {
|
|
|
13
13
|
ClientId,
|
|
14
14
|
Code,
|
|
15
15
|
DeviceId,
|
|
16
|
+
Did,
|
|
16
17
|
OAuthAuthorizationDetails,
|
|
17
18
|
OAuthAuthorizationRequestParameters,
|
|
18
|
-
Sub,
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export type TokenData = {
|
|
@@ -25,7 +25,7 @@ export type TokenData = {
|
|
|
25
25
|
clientId: ClientId
|
|
26
26
|
clientAuth: ClientAuth | ClientAuthLegacy
|
|
27
27
|
deviceId: DeviceId | null
|
|
28
|
-
|
|
28
|
+
did: Did
|
|
29
29
|
parameters: OAuthAuthorizationRequestParameters
|
|
30
30
|
details?: null // Legacy field, not used
|
|
31
31
|
code: Code | null
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Did } from '@atproto/did'
|
|
1
2
|
import { SignedJwt, isSignedJwt } from '@atproto/jwk'
|
|
2
3
|
import { LexResolverError } from '@atproto/lex-resolver'
|
|
3
4
|
import type { Account } from '@atproto/oauth-provider-api'
|
|
@@ -20,7 +21,6 @@ import { LexiconManager } from '../lexicon/lexicon-manager.js'
|
|
|
20
21
|
import { RequestMetadata } from '../lib/http/request.js'
|
|
21
22
|
import { dateToEpoch, dateToRelativeSeconds } from '../lib/util/date.js'
|
|
22
23
|
import { OAuthHooks } from '../oauth-hooks.js'
|
|
23
|
-
import { Sub } from '../oidc/sub.js'
|
|
24
24
|
import { Code, isCode } from '../request/code.js'
|
|
25
25
|
import { AccessTokenPayload } from '../signer/access-token-payload.js'
|
|
26
26
|
import { Signer } from '../signer/signer.js'
|
|
@@ -61,10 +61,10 @@ export class TokenManager {
|
|
|
61
61
|
): Promise<OAuthAccessToken> {
|
|
62
62
|
const claims: TokenClaims = {
|
|
63
63
|
jti: tokenId,
|
|
64
|
-
sub: account.
|
|
64
|
+
sub: account.did,
|
|
65
65
|
iat: dateToEpoch(issuedAt),
|
|
66
66
|
exp: dateToEpoch(expiresAt),
|
|
67
|
-
aud: account.
|
|
67
|
+
aud: account.pds,
|
|
68
68
|
|
|
69
69
|
...(parameters.dpop_jkt && {
|
|
70
70
|
cnf: { jkt: parameters.dpop_jkt },
|
|
@@ -136,7 +136,7 @@ export class TokenManager {
|
|
|
136
136
|
accessToken,
|
|
137
137
|
refreshToken,
|
|
138
138
|
expiresAt,
|
|
139
|
-
account.
|
|
139
|
+
account.did,
|
|
140
140
|
scope,
|
|
141
141
|
)
|
|
142
142
|
|
|
@@ -147,7 +147,7 @@ export class TokenManager {
|
|
|
147
147
|
clientId: client.id,
|
|
148
148
|
clientAuth,
|
|
149
149
|
deviceId,
|
|
150
|
-
|
|
150
|
+
did: account.did,
|
|
151
151
|
parameters,
|
|
152
152
|
details: null,
|
|
153
153
|
scope,
|
|
@@ -191,7 +191,7 @@ export class TokenManager {
|
|
|
191
191
|
accessToken: OAuthAccessToken,
|
|
192
192
|
refreshToken: string | undefined,
|
|
193
193
|
expiresAt: Date,
|
|
194
|
-
|
|
194
|
+
did: Did,
|
|
195
195
|
scope: string,
|
|
196
196
|
): OAuthTokenResponse {
|
|
197
197
|
return {
|
|
@@ -209,7 +209,7 @@ export class TokenManager {
|
|
|
209
209
|
// ATPROTO extension: add the sub claim to the token response to allow
|
|
210
210
|
// clients to resolve the PDS url (audience) using the did resolution
|
|
211
211
|
// mechanism.
|
|
212
|
-
sub,
|
|
212
|
+
sub: did,
|
|
213
213
|
}
|
|
214
214
|
}
|
|
215
215
|
|
|
@@ -263,7 +263,7 @@ export class TokenManager {
|
|
|
263
263
|
accessToken,
|
|
264
264
|
nextRefreshToken,
|
|
265
265
|
expiresAt,
|
|
266
|
-
account.
|
|
266
|
+
account.did,
|
|
267
267
|
scope,
|
|
268
268
|
)
|
|
269
269
|
|
|
@@ -305,10 +305,10 @@ export class TokenManager {
|
|
|
305
305
|
if (!tokenInfo) return null
|
|
306
306
|
|
|
307
307
|
// Fool-proof: Invalid store implementation ?
|
|
308
|
-
if (payload.sub !== tokenInfo.account.
|
|
308
|
+
if (payload.sub !== tokenInfo.account.did) {
|
|
309
309
|
await this.deleteToken(tokenInfo.id)
|
|
310
310
|
throw new Error(
|
|
311
|
-
`Account sub (${tokenInfo.account.
|
|
311
|
+
`Account sub (${tokenInfo.account.did}) does not match token sub (${payload.sub})`,
|
|
312
312
|
)
|
|
313
313
|
}
|
|
314
314
|
|
|
@@ -395,20 +395,20 @@ export class TokenManager {
|
|
|
395
395
|
|
|
396
396
|
return {
|
|
397
397
|
jti: tokenId,
|
|
398
|
-
sub: account.
|
|
398
|
+
sub: account.did,
|
|
399
399
|
iat: dateToEpoch(data.updatedAt),
|
|
400
400
|
exp: dateToEpoch(data.expiresAt),
|
|
401
|
-
aud: account.
|
|
401
|
+
aud: account.pds,
|
|
402
402
|
scope: data.scope ?? data.parameters.scope,
|
|
403
403
|
// https://datatracker.ietf.org/doc/html/rfc8693#section-4.3
|
|
404
404
|
client_id: data.clientId,
|
|
405
405
|
}
|
|
406
406
|
}
|
|
407
407
|
|
|
408
|
-
async listAccountTokens(
|
|
409
|
-
const results = await this.store.listAccountTokens(
|
|
408
|
+
async listAccountTokens(did: Did): Promise<TokenInfo[]> {
|
|
409
|
+
const results = await this.store.listAccountTokens(did)
|
|
410
410
|
return results
|
|
411
|
-
.filter((tokenInfo) => tokenInfo.account.
|
|
411
|
+
.filter((tokenInfo) => tokenInfo.account.did === did) // Fool proof
|
|
412
412
|
.filter((tokenInfo) => !isCurrentTokenExpired(tokenInfo))
|
|
413
413
|
}
|
|
414
414
|
}
|