@atproto/oauth-provider 0.1.3 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/account/account.d.ts +6 -2
  3. package/dist/account/account.d.ts.map +1 -1
  4. package/dist/assets/app/bundle-manifest.json +3 -3
  5. package/dist/assets/app/main.css +1 -1
  6. package/dist/assets/app/main.js +1 -1
  7. package/dist/assets/app/main.js.map +1 -1
  8. package/dist/assets/assets-middleware.d.ts +2 -1
  9. package/dist/assets/assets-middleware.d.ts.map +1 -1
  10. package/dist/assets/assets-middleware.js +7 -0
  11. package/dist/assets/assets-middleware.js.map +1 -1
  12. package/dist/client/client-manager.d.ts +4 -3
  13. package/dist/client/client-manager.d.ts.map +1 -1
  14. package/dist/client/client-manager.js +60 -37
  15. package/dist/client/client-manager.js.map +1 -1
  16. package/dist/client/client.d.ts.map +1 -1
  17. package/dist/client/client.js +1 -3
  18. package/dist/client/client.js.map +1 -1
  19. package/dist/constants.d.ts +2 -0
  20. package/dist/constants.d.ts.map +1 -1
  21. package/dist/constants.js +3 -1
  22. package/dist/constants.js.map +1 -1
  23. package/dist/device/device-manager.d.ts +1 -1
  24. package/dist/device/device-manager.d.ts.map +1 -1
  25. package/dist/device/device-manager.js +2 -2
  26. package/dist/device/device-manager.js.map +1 -1
  27. package/dist/errors/invalid-authorization-details-error.d.ts +4 -3
  28. package/dist/errors/invalid-authorization-details-error.d.ts.map +1 -1
  29. package/dist/errors/invalid-authorization-details-error.js +4 -4
  30. package/dist/errors/invalid-authorization-details-error.js.map +1 -1
  31. package/dist/lib/http/request.d.ts +3 -0
  32. package/dist/lib/http/request.d.ts.map +1 -1
  33. package/dist/lib/http/request.js +24 -12
  34. package/dist/lib/http/request.js.map +1 -1
  35. package/dist/metadata/build-metadata.d.ts +0 -1
  36. package/dist/metadata/build-metadata.d.ts.map +1 -1
  37. package/dist/metadata/build-metadata.js +9 -35
  38. package/dist/metadata/build-metadata.js.map +1 -1
  39. package/dist/oauth-hooks.d.ts +3 -10
  40. package/dist/oauth-hooks.d.ts.map +1 -1
  41. package/dist/oauth-provider.d.ts +8 -13
  42. package/dist/oauth-provider.d.ts.map +1 -1
  43. package/dist/oauth-provider.js +169 -109
  44. package/dist/oauth-provider.js.map +1 -1
  45. package/dist/oauth-verifier.d.ts +1 -2
  46. package/dist/oauth-verifier.d.ts.map +1 -1
  47. package/dist/oauth-verifier.js.map +1 -1
  48. package/dist/output/build-authorize-data.d.ts +6 -0
  49. package/dist/output/build-authorize-data.d.ts.map +1 -1
  50. package/dist/output/build-authorize-data.js +1 -0
  51. package/dist/output/build-authorize-data.js.map +1 -1
  52. package/dist/replay/replay-manager.d.ts +1 -0
  53. package/dist/replay/replay-manager.d.ts.map +1 -1
  54. package/dist/replay/replay-manager.js +3 -0
  55. package/dist/replay/replay-manager.js.map +1 -1
  56. package/dist/replay/replay-store.d.ts +1 -1
  57. package/dist/request/request-info.d.ts +2 -0
  58. package/dist/request/request-info.d.ts.map +1 -1
  59. package/dist/request/request-manager.d.ts +3 -9
  60. package/dist/request/request-manager.d.ts.map +1 -1
  61. package/dist/request/request-manager.js +52 -77
  62. package/dist/request/request-manager.js.map +1 -1
  63. package/dist/request/types.d.ts +10 -10
  64. package/dist/signer/signed-token-payload.d.ts +85 -85
  65. package/dist/signer/signer.d.ts +23 -30
  66. package/dist/signer/signer.d.ts.map +1 -1
  67. package/dist/signer/signer.js +0 -40
  68. package/dist/signer/signer.js.map +1 -1
  69. package/dist/token/token-claims.d.ts +81 -81
  70. package/dist/token/token-manager.d.ts +1 -2
  71. package/dist/token/token-manager.d.ts.map +1 -1
  72. package/dist/token/token-manager.js +10 -37
  73. package/dist/token/token-manager.js.map +1 -1
  74. package/dist/token/types.d.ts +10 -10
  75. package/package.json +2 -3
  76. package/src/account/account.ts +11 -7
  77. package/src/assets/app/backend-data.ts +9 -2
  78. package/src/assets/app/components/accept-form.tsx +65 -51
  79. package/src/assets/app/components/client-name.tsx +24 -16
  80. package/src/assets/app/views/accept-view.tsx +7 -4
  81. package/src/assets/app/views/authorize-view.tsx +2 -1
  82. package/src/assets/assets-middleware.ts +14 -2
  83. package/src/client/client-manager.ts +78 -60
  84. package/src/client/client.ts +1 -4
  85. package/src/constants.ts +3 -0
  86. package/src/device/device-manager.ts +7 -1
  87. package/src/errors/invalid-authorization-details-error.ts +9 -4
  88. package/src/lib/http/request.ts +61 -15
  89. package/src/metadata/build-metadata.ts +9 -42
  90. package/src/oauth-hooks.ts +3 -13
  91. package/src/oauth-provider.ts +181 -159
  92. package/src/oauth-verifier.ts +1 -2
  93. package/src/output/build-authorize-data.ts +8 -0
  94. package/src/replay/replay-manager.ts +9 -0
  95. package/src/replay/replay-store.ts +1 -1
  96. package/src/request/request-info.ts +2 -0
  97. package/src/request/request-manager.ts +81 -107
  98. package/src/signer/signer.ts +0 -63
  99. package/src/token/token-manager.ts +8 -41
  100. package/dist/oidc/claims.d.ts +0 -16
  101. package/dist/oidc/claims.d.ts.map +0 -1
  102. package/dist/oidc/claims.js +0 -29
  103. package/dist/oidc/claims.js.map +0 -1
  104. package/dist/oidc/userinfo.d.ts +0 -7
  105. package/dist/oidc/userinfo.d.ts.map +0 -1
  106. package/dist/oidc/userinfo.js +0 -3
  107. package/dist/oidc/userinfo.js.map +0 -1
  108. package/dist/parameters/claims-requested.d.ts +0 -3
  109. package/dist/parameters/claims-requested.d.ts.map +0 -1
  110. package/dist/parameters/claims-requested.js +0 -77
  111. package/dist/parameters/claims-requested.js.map +0 -1
  112. package/dist/parameters/oidc-payload.d.ts +0 -31
  113. package/dist/parameters/oidc-payload.d.ts.map +0 -1
  114. package/dist/parameters/oidc-payload.js +0 -25
  115. package/dist/parameters/oidc-payload.js.map +0 -1
  116. package/src/assets/app/components/client-identifier.tsx +0 -31
  117. package/src/oidc/claims.ts +0 -35
  118. package/src/oidc/userinfo.ts +0 -11
  119. package/src/parameters/claims-requested.ts +0 -106
  120. package/src/parameters/oidc-payload.ts +0 -28
@@ -17,6 +17,7 @@ import {
17
17
  isLoopbackUrl,
18
18
  isOAuthClientIdDiscoverable,
19
19
  isOAuthClientIdLoopback,
20
+ OAuthAuthorizationServerMetadata,
20
21
  OAuthClientIdDiscoverable,
21
22
  OAuthClientIdLoopback,
22
23
  OAuthClientMetadata,
@@ -55,9 +56,10 @@ export type LoopbackMetadataGetter = (
55
56
 
56
57
  export class ClientManager {
57
58
  protected readonly jwks: CachedGetter<string, Jwks>
58
- protected readonly metadata: CachedGetter<string, OAuthClientMetadata>
59
+ protected readonly metadataGetter: CachedGetter<string, OAuthClientMetadata>
59
60
 
60
61
  constructor(
62
+ protected readonly serverMetadata: OAuthAuthorizationServerMetadata,
61
63
  protected readonly keyset: Keyset,
62
64
  protected readonly hooks: OAuthHooks,
63
65
  protected readonly store: ClientStore | null,
@@ -76,7 +78,7 @@ export class ClientManager {
76
78
  return jwks
77
79
  }, clientJwksCache)
78
80
 
79
- this.metadata = new CachedGetter(async (uri, options) => {
81
+ this.metadataGetter = new CachedGetter(async (uri, options) => {
80
82
  const metadata = await fetch(buildJsonGetRequest(uri, options)).then(
81
83
  fetchMetadataHandler,
82
84
  )
@@ -159,7 +161,7 @@ export class ClientManager {
159
161
  ): Promise<OAuthClientMetadata> {
160
162
  const metadataUrl = parseDiscoverableClientId(clientId)
161
163
 
162
- const metadata = await this.metadata.get(metadataUrl.href)
164
+ const metadata = await this.metadataGetter.get(metadataUrl.href)
163
165
 
164
166
  // Note: we do *not* re-validate the metadata here, as the metadata is
165
167
  // validated within the getter. This is to avoid double validation.
@@ -195,6 +197,18 @@ export class ClientManager {
195
197
  )
196
198
  }
197
199
 
200
+ // Known OIDC specific parameters
201
+ for (const k of [
202
+ 'default_max_age',
203
+ 'userinfo_signed_response_alg',
204
+ 'id_token_signed_response_alg',
205
+ 'userinfo_encrypted_response_alg',
206
+ ] as const) {
207
+ if (metadata[k] != null) {
208
+ throw new InvalidClientMetadataError(`Unsupported "${k}" parameter`)
209
+ }
210
+ }
211
+
198
212
  const clientUriUrl = metadata.client_uri
199
213
  ? new URL(metadata.client_uri)
200
214
  : null
@@ -204,13 +218,27 @@ export class ClientManager {
204
218
  throw new InvalidClientMetadataError('client_uri must be a valid URL')
205
219
  }
206
220
 
207
- const scopes = metadata.scope?.split(' ')
208
- if (
209
- metadata.grant_types.includes('refresh_token') !==
210
- (scopes?.includes('offline_access') ?? false)
211
- ) {
221
+ const scopes = metadata.scope?.split(' ').filter(Boolean)
222
+
223
+ const dupScope = scopes?.find(isDuplicate)
224
+ if (dupScope) {
225
+ throw new InvalidClientMetadataError(`Duplicate scope "${dupScope}"`)
226
+ }
227
+
228
+ if (scopes) {
229
+ for (const scope of scopes) {
230
+ // Note, once we have dynamic scopes, this check will need to be
231
+ // updated to check against the server's supported scopes.
232
+ if (!this.serverMetadata.scopes_supported?.includes(scope)) {
233
+ throw new InvalidClientMetadataError(`Unsupported scope "${scope}"`)
234
+ }
235
+ }
236
+ }
237
+
238
+ const dupGrantType = metadata.grant_types.find(isDuplicate)
239
+ if (dupGrantType) {
212
240
  throw new InvalidClientMetadataError(
213
- 'Grant type "refresh_token" requires scope "offline_access" (and vice versa)',
241
+ `Duplicate grant type "${dupGrantType}"`,
214
242
  )
215
243
  }
216
244
 
@@ -218,8 +246,8 @@ export class ClientManager {
218
246
  switch (grantType) {
219
247
  case 'authorization_code':
220
248
  case 'refresh_token':
221
- case 'implicit': // Required by OIDC (for id_token)
222
249
  continue
250
+ case 'implicit':
223
251
  case 'password':
224
252
  throw new InvalidClientMetadataError(
225
253
  `Grant type "${grantType}" is not allowed`,
@@ -241,35 +269,6 @@ export class ClientManager {
241
269
  )
242
270
  }
243
271
 
244
- if (
245
- metadata.userinfo_signed_response_alg &&
246
- !this.keyset.signAlgorithms.includes(
247
- metadata.userinfo_signed_response_alg,
248
- )
249
- ) {
250
- throw new InvalidClientMetadataError(
251
- `Unsupported "userinfo_signed_response_alg" ${metadata.userinfo_signed_response_alg}`,
252
- )
253
- }
254
-
255
- if (
256
- metadata.id_token_signed_response_alg &&
257
- !this.keyset.signAlgorithms.includes(
258
- metadata.id_token_signed_response_alg,
259
- )
260
- ) {
261
- throw new InvalidClientMetadataError(
262
- `Unsupported "id_token_signed_response_alg" ${metadata.id_token_signed_response_alg}`,
263
- )
264
- }
265
-
266
- if (metadata.userinfo_encrypted_response_alg) {
267
- // We only support signature for now.
268
- throw new InvalidClientMetadataError(
269
- 'Encrypted userinfo response is not supported',
270
- )
271
- }
272
-
273
272
  const method = metadata[`token_endpoint_auth_method`]
274
273
  switch (method) {
275
274
  case undefined:
@@ -338,37 +337,28 @@ export class ClientManager {
338
337
  }
339
338
 
340
339
  for (const responseType of metadata.response_types) {
341
- const rt = responseType.split(' ')
340
+ if (responseType.includes('id_token')) {
341
+ throw new InvalidClientMetadataError(
342
+ `OpenID Connect response type "${responseType}" is not supported`,
343
+ )
344
+ }
342
345
 
343
346
  // ATPROTO spec requires the use of PKCE
344
- if (rt.includes('token')) {
347
+ if (responseType !== 'code') {
345
348
  throw new InvalidClientMetadataError(
346
- '"token" response type is not compatible with PKCE (use "code" instead)',
349
+ `Unsupported response type "${responseType}"`,
347
350
  )
348
351
  }
349
352
 
350
353
  // Consistency check
351
354
  if (
352
- rt.includes('code') &&
355
+ responseType === 'code' &&
353
356
  !metadata.grant_types.includes('authorization_code')
354
357
  ) {
355
358
  throw new InvalidClientMetadataError(
356
359
  `Response type "${responseType}" requires the "authorization_code" grant type`,
357
360
  )
358
361
  }
359
-
360
- // Asking for "code token" or "code id_token" is fine (as long as the
361
- // grant_types includes "authorization_code" and the scope includes
362
- // "openid"). Asking for "token" or "id_token" (without "code") requires
363
- // the "implicit" grant type.
364
- if (
365
- (rt.includes('token') || rt.includes('id_token')) &&
366
- !metadata.grant_types.includes('implicit')
367
- ) {
368
- throw new InvalidClientMetadataError(
369
- `Response type "${responseType}" requires the "implicit" grant type`,
370
- )
371
- }
372
362
  }
373
363
 
374
364
  if (metadata.application_type === 'native') {
@@ -383,11 +373,33 @@ export class ClientManager {
383
373
  // > accordingly.
384
374
  }
385
375
 
376
+ if (metadata.authorization_details_types?.length) {
377
+ const dupAuthDetailsType =
378
+ metadata.authorization_details_types.find(isDuplicate)
379
+ if (dupAuthDetailsType) {
380
+ throw new InvalidClientMetadataError(
381
+ `Duplicate authorization_details_type "${dupAuthDetailsType}"`,
382
+ )
383
+ }
384
+
385
+ const authorizationDetailsTypesSupported =
386
+ this.serverMetadata.authorization_details_types_supported
387
+ if (!authorizationDetailsTypesSupported) {
388
+ throw new InvalidClientMetadataError(
389
+ 'authorization_details_types are not supported',
390
+ )
391
+ }
392
+ for (const type of metadata.authorization_details_types) {
393
+ if (!authorizationDetailsTypesSupported.includes(type)) {
394
+ throw new InvalidClientMetadataError(
395
+ `Unsupported authorization_details_type "${type}"`,
396
+ )
397
+ }
398
+ }
399
+ }
400
+
386
401
  if (!metadata.redirect_uris?.length) {
387
- // https://openid.net/specs/openid-connect-registration-1_0.html#rfc.section.2
388
- //
389
- // > OPs can require that request_uri values used be pre-registered with
390
- // > the require_request_uri_registration discovery parameter.
402
+ // ATPROTO spec requires that at least one redirect URI is provided
391
403
 
392
404
  throw new InvalidClientMetadataError(
393
405
  'At least one redirect_uri is required',
@@ -786,6 +798,12 @@ export class ClientManager {
786
798
  }
787
799
  }
788
800
 
801
+ function isDuplicate<
802
+ T extends string | number | boolean | null | undefined | symbol,
803
+ >(value: T, index: number, array: T[]) {
804
+ return array.includes(value, index + 1)
805
+ }
806
+
789
807
  function reverseDomain(domain: string) {
790
808
  return domain.split('.').reverse().join('.')
791
809
  }
@@ -140,6 +140,7 @@ export class Client {
140
140
  audience: checks.audience,
141
141
  subject: this.id,
142
142
  maxTokenAge: CLIENT_ASSERTION_MAX_AGE / 1000,
143
+ requiredClaims: ['jti'],
143
144
  }).catch((err) => {
144
145
  if (err instanceof JOSEError) {
145
146
  const msg = `Validation of "client_assertion" failed: ${err.message}`
@@ -153,10 +154,6 @@ export class Client {
153
154
  throw new InvalidClientError(`"kid" required in client_assertion`)
154
155
  }
155
156
 
156
- if (!result.payload.jti) {
157
- throw new InvalidClientError(`"jti" required in client_assertion`)
158
- }
159
-
160
157
  const clientAuth: ClientAuth = {
161
158
  method: CLIENT_ASSERTION_TYPE_JWT_BEARER,
162
159
  jkt: await authJwkThumbprint(result.key),
package/src/constants.ts CHANGED
@@ -67,3 +67,6 @@ export const DPOP_NONCE_MAX_AGE = 3 * MINUTE
67
67
 
68
68
  /** 5 seconds */
69
69
  export const SESSION_FIXATION_MAX_AGE = 5 * SECOND
70
+
71
+ /** 1 day */
72
+ export const CODE_CHALLENGE_REPLAY_TIMEFRAME = 1 * DAY
@@ -100,10 +100,16 @@ export class DeviceManager {
100
100
  public async load(
101
101
  req: IncomingMessage,
102
102
  res: ServerResponse,
103
+ forceRotate = false,
103
104
  ): Promise<{ deviceId: DeviceId }> {
104
105
  const cookie = await this.getCookie(req)
105
106
  if (cookie) {
106
- return this.refresh(req, res, cookie.value, cookie.mustRotate)
107
+ return this.refresh(
108
+ req,
109
+ res,
110
+ cookie.value,
111
+ forceRotate || cookie.mustRotate,
112
+ )
107
113
  } else {
108
114
  return this.create(req, res)
109
115
  }
@@ -1,4 +1,5 @@
1
- import { OAuthError } from './oauth-error.js'
1
+ import { OAuthAuthenticationRequestParameters } from '@atproto/oauth-types'
2
+ import { AccessDeniedError } from './access-denied-error.js'
2
3
 
3
4
  /**
4
5
  * @see
@@ -15,8 +16,12 @@ import { OAuthError } from './oauth-error.js'
15
16
  * - contains fields with invalid values for the authorization details type, or
16
17
  * - is missing required fields for the authorization details type.
17
18
  */
18
- export class InvalidAuthorizationDetailsError extends OAuthError {
19
- constructor(error_description: string, cause?: unknown) {
20
- super('invalid_authorization_details', error_description, 400, cause)
19
+ export class InvalidAuthorizationDetailsError extends AccessDeniedError {
20
+ constructor(
21
+ parameters: OAuthAuthenticationRequestParameters,
22
+ error_description: string,
23
+ cause?: unknown,
24
+ ) {
25
+ super(parameters, error_description, 'invalid_authorization_details', cause)
21
26
  }
22
27
  }
@@ -28,6 +28,27 @@ export async function validateRequestPayload<S extends z.ZodTypeAny>(
28
28
  return schema.parseAsync(payload, { path: ['body'] })
29
29
  }
30
30
 
31
+ export function validateHeaderValue(
32
+ req: IncomingMessage,
33
+ name: keyof IncomingMessage['headers'],
34
+ allowedValues: readonly (string | null)[],
35
+ ) {
36
+ const value = req.headers[name] ?? null
37
+
38
+ if (Array.isArray(value)) {
39
+ throw createHttpError(400, `Invalid ${name} header`)
40
+ }
41
+
42
+ if (!allowedValues.includes(value)) {
43
+ throw createHttpError(
44
+ 400,
45
+ value
46
+ ? `Forbidden ${name} header "${value}" (expected ${allowedValues})`
47
+ : `Missing ${name} header`,
48
+ )
49
+ }
50
+ }
51
+
31
52
  export function validateFetchMode(
32
53
  req: IncomingMessage,
33
54
  res: ServerResponse,
@@ -39,20 +60,45 @@ export function validateFetchMode(
39
60
  | 'cors'
40
61
  )[],
41
62
  ) {
42
- const reqMode = req.headers['sec-fetch-mode'] ?? null
63
+ validateHeaderValue(req, 'sec-fetch-mode', expectedMode)
64
+ }
43
65
 
44
- if (Array.isArray(reqMode)) {
45
- throw createHttpError(400, `Invalid sec-fetch-mode header`)
46
- }
66
+ export function validateFetchDest(
67
+ req: IncomingMessage,
68
+ res: ServerResponse,
69
+ expectedDest: readonly (
70
+ | null
71
+ | 'document'
72
+ | 'embed'
73
+ | 'font'
74
+ | 'image'
75
+ | 'manifest'
76
+ | 'media'
77
+ | 'object'
78
+ | 'report'
79
+ | 'script'
80
+ | 'serviceworker'
81
+ | 'sharedworker'
82
+ | 'style'
83
+ | 'worker'
84
+ | 'xslt'
85
+ )[],
86
+ ) {
87
+ validateHeaderValue(req, 'sec-fetch-dest', expectedDest)
88
+ }
47
89
 
48
- if (!(expectedMode as (string | null)[]).includes(reqMode)) {
49
- throw createHttpError(
50
- 403,
51
- reqMode
52
- ? `Forbidden sec-fetch-mode "${reqMode}" (expected ${expectedMode})`
53
- : `Missing sec-fetch-mode (expected ${expectedMode})`,
54
- )
55
- }
90
+ export function validateFetchSite(
91
+ req: IncomingMessage,
92
+ res: ServerResponse,
93
+ expectedSite: readonly (
94
+ | null
95
+ | 'same-origin'
96
+ | 'same-site'
97
+ | 'cross-site'
98
+ | 'none'
99
+ )[],
100
+ ) {
101
+ validateHeaderValue(req, 'sec-fetch-site', expectedSite)
56
102
  }
57
103
 
58
104
  export function validateReferer(
@@ -64,7 +110,7 @@ export function validateReferer(
64
110
  const referer = req.headers['referer']
65
111
  const refererUrl = referer ? new URL(referer) : null
66
112
  if (refererUrl ? !urlMatch(refererUrl, reference) : !allowNull) {
67
- throw createHttpError(403, `Invalid referer ${referer}`)
113
+ throw createHttpError(400, `Invalid referer ${referer}`)
68
114
  }
69
115
  }
70
116
 
@@ -95,7 +141,7 @@ export function validateSameOrigin(
95
141
  ) {
96
142
  const reqOrigin = req.headers['origin']
97
143
  if (reqOrigin ? reqOrigin !== origin : !allowNull) {
98
- throw createHttpError(403, `Invalid origin ${reqOrigin}`)
144
+ throw createHttpError(400, `Invalid origin ${reqOrigin}`)
99
145
  }
100
146
  }
101
147
 
@@ -113,7 +159,7 @@ export function validateCsrfToken(
113
159
  !cookieName ||
114
160
  cookies[cookieName] !== csrfToken
115
161
  ) {
116
- throw createHttpError(403, `Invalid CSRF token`)
162
+ throw createHttpError(400, `Invalid CSRF token`)
117
163
  }
118
164
 
119
165
  if (clearCookie) {
@@ -2,11 +2,9 @@ import { Keyset } from '@atproto/jwk'
2
2
  import { OAuthAuthorizationServerMetadata } from '@atproto/oauth-types'
3
3
 
4
4
  import { Client } from '../client/client.js'
5
- import { OIDC_STANDARD_CLAIMS } from '../oidc/claims.js'
6
5
  import { VERIFY_ALGOS } from '../lib/util/crypto.js'
7
6
 
8
7
  export type CustomMetadata = {
9
- claims_supported?: string[]
10
8
  scopes_supported?: string[]
11
9
  authorization_details_types_supported?: string[]
12
10
  protected_resources?: string[]
@@ -25,35 +23,10 @@ export function buildMetadata(
25
23
  issuer,
26
24
 
27
25
  scopes_supported: [
28
- 'offline_access',
29
- 'openid',
30
- 'email',
31
- 'phone',
32
- 'profile',
33
-
26
+ 'atproto',
27
+ //
34
28
  ...(customMetadata?.scopes_supported ?? []),
35
29
  ],
36
- claims_supported: [
37
- /* IESG (Always provided) */
38
-
39
- 'sub', // did
40
- 'iss', // Authorization Server Origin
41
- 'aud',
42
- 'exp',
43
- 'iat',
44
- 'jti',
45
- 'client_id',
46
-
47
- /* OpenID */
48
-
49
- // 'acr', // "0"
50
- // 'amr',
51
- // 'azp',
52
- 'auth_time', // number - seconds since epoch
53
- 'nonce', // always required in "id_token", why would it not be supported?
54
-
55
- ...(customMetadata?.claims_supported ?? OIDC_STANDARD_CLAIMS),
56
- ],
57
30
  subject_types_supported: [
58
31
  //
59
32
  'public', // The same "sub" is returned for all clients
@@ -62,15 +35,15 @@ export function buildMetadata(
62
35
  response_types_supported: [
63
36
  // OAuth
64
37
  'code',
65
- 'token',
38
+ // 'token',
66
39
 
67
40
  // OpenID
68
- 'none',
69
- 'code id_token token',
70
- 'code id_token',
71
- 'code token',
72
- 'id_token token',
73
- 'id_token',
41
+ // 'none',
42
+ // 'code id_token token',
43
+ // 'code id_token',
44
+ // 'code token',
45
+ // 'id_token token',
46
+ // 'id_token',
74
47
  ],
75
48
  response_modes_supported: [
76
49
  // https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes
@@ -93,7 +66,6 @@ export function buildMetadata(
93
66
  //
94
67
  'en-US',
95
68
  ],
96
- id_token_signing_alg_values_supported: [...keyset.signAlgorithms],
97
69
  display_values_supported: [
98
70
  //
99
71
  'page',
@@ -110,10 +82,6 @@ export function buildMetadata(
110
82
  request_object_encryption_alg_values_supported: [], // None
111
83
  request_object_encryption_enc_values_supported: [], // None
112
84
 
113
- // No claim makes sense to be translated
114
- claims_locales_supported: [],
115
-
116
- claims_parameter_supported: true,
117
85
  request_parameter_supported: true,
118
86
  request_uri_parameter_supported: true,
119
87
  require_request_uri_registration: true,
@@ -130,7 +98,6 @@ export function buildMetadata(
130
98
 
131
99
  introspection_endpoint: new URL('/oauth/introspect', issuer).href,
132
100
 
133
- userinfo_endpoint: new URL('/oauth/userinfo', issuer).href,
134
101
  // end_session_endpoint: new URL('/oauth/logout', issuer).href,
135
102
 
136
103
  // https://datatracker.ietf.org/doc/html/rfc9126#section-5
@@ -11,6 +11,7 @@ import { ClientAuth } from './client/client-auth.js'
11
11
  import { ClientId } from './client/client-id.js'
12
12
  import { ClientInfo } from './client/client-info.js'
13
13
  import { Client } from './client/client.js'
14
+ import { InvalidAuthorizationDetailsError } from './errors/invalid-authorization-details-error.js'
14
15
  import { Awaitable } from './lib/util/type.js'
15
16
 
16
17
  // Make sure all types needed to implement the OAuthHooks are exported
@@ -20,6 +21,7 @@ export type {
20
21
  ClientAuth,
21
22
  ClientId,
22
23
  ClientInfo,
24
+ InvalidAuthorizationDetailsError,
23
25
  Jwks,
24
26
  OAuthAuthenticationRequestParameters,
25
27
  OAuthAuthorizationDetails,
@@ -42,7 +44,7 @@ export type OAuthHooks = {
42
44
 
43
45
  /**
44
46
  * Allows enriching the authorization details with additional information
45
- * before the tokens are issued.
47
+ * when the tokens are issued.
46
48
  *
47
49
  * @see {@link https://datatracker.ietf.org/doc/html/rfc9396 | RFC 9396}
48
50
  */
@@ -51,16 +53,4 @@ export type OAuthHooks = {
51
53
  parameters: OAuthAuthenticationRequestParameters
52
54
  account: Account
53
55
  }) => Awaitable<undefined | OAuthAuthorizationDetails>
54
-
55
- /**
56
- * Allows altering the token response before it is sent to the client.
57
- */
58
- onTokenResponse?: (
59
- tokenResponse: OAuthTokenResponse,
60
- data: {
61
- client: Client
62
- parameters: OAuthAuthenticationRequestParameters
63
- account: Account
64
- },
65
- ) => Awaitable<void>
66
56
  }