@atproto/oauth-provider 0.18.4 → 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.
Files changed (91) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/account/account-manager.d.ts +11 -7
  3. package/dist/account/account-manager.d.ts.map +1 -1
  4. package/dist/account/account-manager.js +82 -19
  5. package/dist/account/account-manager.js.map +1 -1
  6. package/dist/account/account-store.d.ts +47 -13
  7. package/dist/account/account-store.d.ts.map +1 -1
  8. package/dist/account/account-store.js +4 -1
  9. package/dist/account/account-store.js.map +1 -1
  10. package/dist/account/sign-up-input.d.ts +2 -2
  11. package/dist/errors/invalid-credentials-error.d.ts +9 -9
  12. package/dist/errors/invalid-credentials-error.d.ts.map +1 -1
  13. package/dist/errors/invalid-credentials-error.js +8 -8
  14. package/dist/errors/invalid-credentials-error.js.map +1 -1
  15. package/dist/lib/util/function.js +1 -1
  16. package/dist/lib/util/function.js.map +1 -1
  17. package/dist/oauth-hooks.d.ts +77 -4
  18. package/dist/oauth-hooks.d.ts.map +1 -1
  19. package/dist/oauth-hooks.js.map +1 -1
  20. package/dist/oauth-provider.d.ts.map +1 -1
  21. package/dist/oauth-provider.js +15 -9
  22. package/dist/oauth-provider.js.map +1 -1
  23. package/dist/request/request-data.d.ts +4 -4
  24. package/dist/request/request-data.d.ts.map +1 -1
  25. package/dist/request/request-data.js +1 -1
  26. package/dist/request/request-data.js.map +1 -1
  27. package/dist/request/request-manager.d.ts.map +1 -1
  28. package/dist/request/request-manager.js +7 -4
  29. package/dist/request/request-manager.js.map +1 -1
  30. package/dist/request/request-store.d.ts +1 -1
  31. package/dist/request/request-store.js.map +1 -1
  32. package/dist/result/authorization-result-authorize-page.d.ts +1 -1
  33. package/dist/result/authorization-result-authorize-page.js.map +1 -1
  34. package/dist/router/assets/send-authorization-page.js +1 -1
  35. package/dist/router/assets/send-authorization-page.js.map +1 -1
  36. package/dist/router/create-api-middleware.d.ts.map +1 -1
  37. package/dist/router/create-api-middleware.js +87 -51
  38. package/dist/router/create-api-middleware.js.map +1 -1
  39. package/dist/signer/access-token-payload.d.ts +15 -15
  40. package/dist/signer/access-token-payload.js +2 -2
  41. package/dist/signer/access-token-payload.js.map +1 -1
  42. package/dist/signer/api-token-payload.d.ts +15 -15
  43. package/dist/signer/api-token-payload.js +2 -2
  44. package/dist/signer/api-token-payload.js.map +1 -1
  45. package/dist/signer/signer.d.ts +6 -187
  46. package/dist/signer/signer.d.ts.map +1 -1
  47. package/dist/signer/signer.js.map +1 -1
  48. package/dist/token/token-claims.d.ts +2 -1
  49. package/dist/token/token-claims.d.ts.map +1 -1
  50. package/dist/token/token-claims.js.map +1 -1
  51. package/dist/token/token-data.d.ts +3 -3
  52. package/dist/token/token-data.d.ts.map +1 -1
  53. package/dist/token/token-data.js.map +1 -1
  54. package/dist/token/token-manager.d.ts +3 -3
  55. package/dist/token/token-manager.d.ts.map +1 -1
  56. package/dist/token/token-manager.js +14 -14
  57. package/dist/token/token-manager.js.map +1 -1
  58. package/dist/token/token-store.d.ts +3 -3
  59. package/dist/token/token-store.d.ts.map +1 -1
  60. package/dist/token/token-store.js.map +1 -1
  61. package/dist/types/handle.d.ts +5 -1
  62. package/dist/types/handle.d.ts.map +1 -1
  63. package/dist/types/handle.js +9 -8
  64. package/dist/types/handle.js.map +1 -1
  65. package/package.json +10 -10
  66. package/src/account/account-manager.ts +123 -20
  67. package/src/account/account-store.ts +59 -11
  68. package/src/errors/invalid-credentials-error.ts +8 -8
  69. package/src/lib/util/function.ts +2 -2
  70. package/src/oauth-hooks.ts +85 -3
  71. package/src/oauth-provider.ts +17 -9
  72. package/src/request/request-data.ts +5 -5
  73. package/src/request/request-manager.ts +11 -4
  74. package/src/request/request-store.ts +1 -1
  75. package/src/result/authorization-result-authorize-page.ts +1 -1
  76. package/src/router/assets/send-authorization-page.ts +1 -1
  77. package/src/router/create-api-middleware.ts +128 -67
  78. package/src/signer/access-token-payload.ts +2 -2
  79. package/src/signer/api-token-payload.ts +2 -2
  80. package/src/signer/signer.ts +13 -4
  81. package/src/token/token-claims.ts +2 -1
  82. package/src/token/token-data.ts +3 -3
  83. package/src/token/token-manager.ts +15 -15
  84. package/src/token/token-store.ts +3 -3
  85. package/src/types/handle.ts +21 -16
  86. package/tsconfig.build.tsbuildinfo +1 -1
  87. package/dist/oidc/sub.d.ts +0 -4
  88. package/dist/oidc/sub.d.ts.map +0 -1
  89. package/dist/oidc/sub.js +0 -3
  90. package/dist/oidc/sub.js.map +0 -1
  91. package/src/oidc/sub.ts +0 -4
@@ -1,9 +1,9 @@
1
+ import { Did } from '@atproto/did'
1
2
  import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
2
3
  import { ClientAuth, ClientAuthLegacy } from '../client/client-auth.js'
3
4
  import { ClientId } from '../client/client-id.js'
4
5
  import { DeviceId } from '../device/device-id.js'
5
6
  import { NonNullableKeys } from '../lib/util/type.js'
6
- import { Sub } from '../oidc/sub.js'
7
7
  import { Code } from './code.js'
8
8
 
9
9
  export type {
@@ -12,8 +12,8 @@ export type {
12
12
  ClientId,
13
13
  Code,
14
14
  DeviceId,
15
+ Did,
15
16
  OAuthAuthorizationRequestParameters,
16
- Sub,
17
17
  }
18
18
 
19
19
  export type RequestData = {
@@ -22,15 +22,15 @@ export type RequestData = {
22
22
  parameters: Readonly<OAuthAuthorizationRequestParameters>
23
23
  expiresAt: Date
24
24
  deviceId: DeviceId | null
25
- sub: Sub | null
25
+ did: Did | null
26
26
  code: Code | null
27
27
  }
28
28
 
29
29
  export type RequestDataAuthorized = NonNullableKeys<
30
30
  RequestData,
31
- 'sub' | 'deviceId'
31
+ 'did' | 'deviceId'
32
32
  >
33
33
 
34
34
  export const isRequestDataAuthorized = (
35
35
  data: RequestData,
36
- ): data is RequestDataAuthorized => data.sub !== null && data.deviceId !== null
36
+ ): data is RequestDataAuthorized => data.did !== null && data.deviceId !== null
@@ -78,7 +78,7 @@ export class RequestManager {
78
78
  parameters,
79
79
  expiresAt,
80
80
  deviceId,
81
- sub: null,
81
+ did: null,
82
82
  code: null,
83
83
  })
84
84
 
@@ -337,7 +337,7 @@ export class RequestManager {
337
337
  const updates: UpdateRequestData = {}
338
338
 
339
339
  try {
340
- if (data.sub || data.code) {
340
+ if (data.did || data.code) {
341
341
  // If an account was linked to the request, the next step is to exchange
342
342
  // the code for a token.
343
343
  throw new AccessDeniedError(
@@ -404,6 +404,13 @@ export class RequestManager {
404
404
  let { parameters } = data
405
405
 
406
406
  try {
407
+ if (account.deactivated) {
408
+ throw new AccessDeniedError(
409
+ parameters,
410
+ 'This account has been deactivated',
411
+ )
412
+ }
413
+
407
414
  if (data.expiresAt < new Date()) {
408
415
  throw new AccessDeniedError(parameters, 'This request has expired')
409
416
  }
@@ -419,7 +426,7 @@ export class RequestManager {
419
426
  'This request was initiated from another device',
420
427
  )
421
428
  }
422
- if (data.sub || data.code) {
429
+ if (data.did || data.code) {
423
430
  throw new AccessDeniedError(
424
431
  parameters,
425
432
  'This request was already authorized',
@@ -453,7 +460,7 @@ export class RequestManager {
453
460
 
454
461
  // Bind the request to the account, preventing it from being used again.
455
462
  await this.store.updateRequest(requestId, {
456
- sub: account.sub,
463
+ did: account.did,
457
464
  code,
458
465
  // Allow the client to exchange the code for a token within the next 60 seconds.
459
466
  expiresAt: new Date(Date.now() + AUTHORIZATION_INACTIVITY_TIMEOUT),
@@ -12,7 +12,7 @@ export type { Awaitable }
12
12
 
13
13
  export type UpdateRequestData = Pick<
14
14
  Partial<RequestData>,
15
- 'sub' | 'code' | 'deviceId' | 'expiresAt' | 'parameters'
15
+ 'did' | 'code' | 'deviceId' | 'expiresAt' | 'parameters'
16
16
  >
17
17
 
18
18
  export type FoundRequestResult = {
@@ -12,5 +12,5 @@ export type AuthorizationResultAuthorizePage = {
12
12
 
13
13
  requestUri: RequestUri
14
14
  sessions: readonly Session[]
15
- selectedSub?: Account['sub']
15
+ selectedDid?: Account['did']
16
16
  }
@@ -33,7 +33,7 @@ export function sendAuthorizePageFactory(
33
33
  loginHint: data.parameters.login_hint,
34
34
  promptMode: data.parameters.prompt,
35
35
  permissionSets: Object.fromEntries(data.permissionSets),
36
- selectedSub: data.selectedSub,
36
+ selectedDid: data.selectedDid,
37
37
  },
38
38
  __sessions: data.sessions,
39
39
  },
@@ -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: verifyHandleSchema,
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.sub)
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.sub,
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.sub)
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.sub)
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.sub,
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
- sub: z.union([subSchema, z.array(subSchema)]),
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.sub))
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
- sub: subSchema,
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
- sub: subSchema,
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
- const { account } = await authenticate.call(this, req, res)
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: { success: true } }
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
- sub: subSchema,
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
- sub: subSchema,
330
+ did: didSchema,
357
331
  token: emailOtpSchema,
358
332
  email: emailSchema,
359
333
  })
360
334
  .strict(),
361
335
  async handler(req, res) {
362
- const { account } = await authenticate.call(this, req, res)
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: { success: true } }
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
- sub: subSchema,
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.updateHandle(
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({ sub: subSchema }).strict(),
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.sub,
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({ sub: subSchema }).strict(),
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.sub,
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({ sub: subSchema, deviceId: deviceIdSchema }).strict(),
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.sub,
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({ sub: subSchema, tokenId: tokenIdSchema }).strict(),
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.sub !== account.sub) {
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, { sub: Sub }>,
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.sub &&
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.sub,
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.sub} not authenticated on this device`,
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: subSchema,
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: subSchema,
9
+ sub: didSchema,
10
10
 
11
11
  deviceId: deviceIdSchema,
12
12
  // If the token is bound to a particular authorization request, it can only
@@ -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: string
15
+ sub: Did
15
16
  iat: number
16
17
  exp: number
17
18
  aud: string | [string, ...string[]]
@@ -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
- sub: Sub
28
+ did: Did
29
29
  parameters: OAuthAuthorizationRequestParameters
30
30
  details?: null // Legacy field, not used
31
31
  code: Code | null