@atproto/oauth-provider 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (136) hide show
  1. package/CHANGELOG.md +46 -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 +3 -3
  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 +91 -77
  15. package/dist/client/client-manager.js.map +1 -1
  16. package/dist/client/client.d.ts +2 -3
  17. package/dist/client/client.d.ts.map +1 -1
  18. package/dist/client/client.js +6 -12
  19. package/dist/client/client.js.map +1 -1
  20. package/dist/constants.d.ts +2 -0
  21. package/dist/constants.d.ts.map +1 -1
  22. package/dist/constants.js +3 -1
  23. package/dist/constants.js.map +1 -1
  24. package/dist/device/device-manager.d.ts +1 -1
  25. package/dist/device/device-manager.d.ts.map +1 -1
  26. package/dist/device/device-manager.js +2 -2
  27. package/dist/device/device-manager.js.map +1 -1
  28. package/dist/dpop/dpop-manager.d.ts +0 -1
  29. package/dist/dpop/dpop-manager.d.ts.map +1 -1
  30. package/dist/dpop/dpop-manager.js +1 -4
  31. package/dist/dpop/dpop-manager.js.map +1 -1
  32. package/dist/errors/invalid-authorization-details-error.d.ts +4 -3
  33. package/dist/errors/invalid-authorization-details-error.d.ts.map +1 -1
  34. package/dist/errors/invalid-authorization-details-error.js +4 -4
  35. package/dist/errors/invalid-authorization-details-error.js.map +1 -1
  36. package/dist/lib/http/parser.d.ts +13 -7
  37. package/dist/lib/http/parser.d.ts.map +1 -1
  38. package/dist/lib/http/parser.js +29 -9
  39. package/dist/lib/http/parser.js.map +1 -1
  40. package/dist/lib/http/request.d.ts +8 -5
  41. package/dist/lib/http/request.d.ts.map +1 -1
  42. package/dist/lib/http/request.js +24 -12
  43. package/dist/lib/http/request.js.map +1 -1
  44. package/dist/lib/http/stream.d.ts.map +1 -1
  45. package/dist/lib/http/stream.js +3 -2
  46. package/dist/lib/http/stream.js.map +1 -1
  47. package/dist/metadata/build-metadata.d.ts +0 -1
  48. package/dist/metadata/build-metadata.d.ts.map +1 -1
  49. package/dist/metadata/build-metadata.js +9 -49
  50. package/dist/metadata/build-metadata.js.map +1 -1
  51. package/dist/oauth-hooks.d.ts +3 -10
  52. package/dist/oauth-hooks.d.ts.map +1 -1
  53. package/dist/oauth-provider.d.ts +10 -15
  54. package/dist/oauth-provider.d.ts.map +1 -1
  55. package/dist/oauth-provider.js +176 -114
  56. package/dist/oauth-provider.js.map +1 -1
  57. package/dist/oauth-verifier.d.ts +1 -2
  58. package/dist/oauth-verifier.d.ts.map +1 -1
  59. package/dist/oauth-verifier.js.map +1 -1
  60. package/dist/output/build-authorize-data.d.ts +6 -0
  61. package/dist/output/build-authorize-data.d.ts.map +1 -1
  62. package/dist/output/build-authorize-data.js +1 -0
  63. package/dist/output/build-authorize-data.js.map +1 -1
  64. package/dist/replay/replay-manager.d.ts +1 -0
  65. package/dist/replay/replay-manager.d.ts.map +1 -1
  66. package/dist/replay/replay-manager.js +3 -0
  67. package/dist/replay/replay-manager.js.map +1 -1
  68. package/dist/replay/replay-store.d.ts +1 -1
  69. package/dist/request/request-info.d.ts +2 -0
  70. package/dist/request/request-info.d.ts.map +1 -1
  71. package/dist/request/request-manager.d.ts +3 -9
  72. package/dist/request/request-manager.d.ts.map +1 -1
  73. package/dist/request/request-manager.js +52 -77
  74. package/dist/request/request-manager.js.map +1 -1
  75. package/dist/request/types.d.ts +10 -10
  76. package/dist/signer/signed-token-payload.d.ts +88 -88
  77. package/dist/signer/signer.d.ts +24 -31
  78. package/dist/signer/signer.d.ts.map +1 -1
  79. package/dist/signer/signer.js +0 -40
  80. package/dist/signer/signer.js.map +1 -1
  81. package/dist/token/token-claims.d.ts +84 -84
  82. package/dist/token/token-manager.d.ts +1 -2
  83. package/dist/token/token-manager.d.ts.map +1 -1
  84. package/dist/token/token-manager.js +10 -37
  85. package/dist/token/token-manager.js.map +1 -1
  86. package/dist/token/types.d.ts +10 -10
  87. package/package.json +3 -3
  88. package/src/account/account.ts +11 -7
  89. package/src/assets/app/backend-data.ts +9 -2
  90. package/src/assets/app/components/accept-form.tsx +65 -51
  91. package/src/assets/app/components/client-name.tsx +24 -16
  92. package/src/assets/app/components/url-viewer.tsx +3 -3
  93. package/src/assets/app/views/accept-view.tsx +7 -4
  94. package/src/assets/app/views/authorize-view.tsx +2 -1
  95. package/src/assets/assets-middleware.ts +14 -2
  96. package/src/client/client-manager.ts +124 -120
  97. package/src/client/client.ts +5 -17
  98. package/src/constants.ts +3 -0
  99. package/src/device/device-manager.ts +7 -1
  100. package/src/dpop/dpop-manager.ts +1 -6
  101. package/src/errors/invalid-authorization-details-error.ts +9 -4
  102. package/src/lib/http/parser.ts +37 -13
  103. package/src/lib/http/request.ts +61 -15
  104. package/src/lib/http/stream.ts +5 -2
  105. package/src/metadata/build-metadata.ts +9 -56
  106. package/src/oauth-hooks.ts +3 -13
  107. package/src/oauth-provider.ts +187 -177
  108. package/src/oauth-verifier.ts +1 -2
  109. package/src/output/build-authorize-data.ts +8 -0
  110. package/src/replay/replay-manager.ts +9 -0
  111. package/src/replay/replay-store.ts +1 -1
  112. package/src/request/request-info.ts +2 -0
  113. package/src/request/request-manager.ts +81 -107
  114. package/src/signer/signer.ts +0 -63
  115. package/src/token/token-manager.ts +8 -41
  116. package/dist/oidc/claims.d.ts +0 -16
  117. package/dist/oidc/claims.d.ts.map +0 -1
  118. package/dist/oidc/claims.js +0 -29
  119. package/dist/oidc/claims.js.map +0 -1
  120. package/dist/oidc/userinfo.d.ts +0 -7
  121. package/dist/oidc/userinfo.d.ts.map +0 -1
  122. package/dist/oidc/userinfo.js +0 -3
  123. package/dist/oidc/userinfo.js.map +0 -1
  124. package/dist/parameters/claims-requested.d.ts +0 -3
  125. package/dist/parameters/claims-requested.d.ts.map +0 -1
  126. package/dist/parameters/claims-requested.js +0 -77
  127. package/dist/parameters/claims-requested.js.map +0 -1
  128. package/dist/parameters/oidc-payload.d.ts +0 -31
  129. package/dist/parameters/oidc-payload.d.ts.map +0 -1
  130. package/dist/parameters/oidc-payload.js +0 -25
  131. package/dist/parameters/oidc-payload.js.map +0 -1
  132. package/src/assets/app/components/client-identifier.tsx +0 -31
  133. package/src/oidc/claims.ts +0 -35
  134. package/src/oidc/userinfo.ts +0 -11
  135. package/src/parameters/claims-requested.ts +0 -106
  136. package/src/parameters/oidc-payload.ts +0 -28
@@ -3,7 +3,6 @@ import {
3
3
  CLIENT_ASSERTION_TYPE_JWT_BEARER,
4
4
  OAuthClientIdentification,
5
5
  OAuthClientMetadata,
6
- OAuthEndpointName,
7
6
  } from '@atproto/oauth-types'
8
7
  import {
9
8
  UnsecuredJWT,
@@ -101,13 +100,6 @@ export class Client {
101
100
  })
102
101
  }
103
102
 
104
- protected getAuthMethod(endpoint: OAuthEndpointName) {
105
- return (
106
- this.metadata[`${endpoint}_endpoint_auth_method`] ||
107
- this.metadata[`token_endpoint_auth_method`]
108
- )
109
- }
110
-
111
103
  /**
112
104
  * @see {@link https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1}
113
105
  * @see {@link https://datatracker.ietf.org/doc/html/draft-ietf-oauth-jwt-bearer-11#section-3}
@@ -115,7 +107,6 @@ export class Client {
115
107
  */
116
108
  public async verifyCredentials(
117
109
  input: OAuthClientIdentification,
118
- endpoint: OAuthEndpointName,
119
110
  checks: {
120
111
  audience: string
121
112
  },
@@ -124,7 +115,7 @@ export class Client {
124
115
  // for replay protection
125
116
  nonce?: string
126
117
  }> {
127
- const method = this.getAuthMethod(endpoint)
118
+ const method = this.metadata[`token_endpoint_auth_method`]
128
119
 
129
120
  if (method === 'none') {
130
121
  const clientAuth: ClientAuth = { method: 'none' }
@@ -149,6 +140,7 @@ export class Client {
149
140
  audience: checks.audience,
150
141
  subject: this.id,
151
142
  maxTokenAge: CLIENT_ASSERTION_MAX_AGE / 1000,
143
+ requiredClaims: ['jti'],
152
144
  }).catch((err) => {
153
145
  if (err instanceof JOSEError) {
154
146
  const msg = `Validation of "client_assertion" failed: ${err.message}`
@@ -162,10 +154,6 @@ export class Client {
162
154
  throw new InvalidClientError(`"kid" required in client_assertion`)
163
155
  }
164
156
 
165
- if (!result.payload.jti) {
166
- throw new InvalidClientError(`"jti" required in client_assertion`)
167
- }
168
-
169
157
  const clientAuth: ClientAuth = {
170
158
  method: CLIENT_ASSERTION_TYPE_JWT_BEARER,
171
159
  jkt: await authJwkThumbprint(result.key),
@@ -192,7 +180,7 @@ export class Client {
192
180
  }
193
181
 
194
182
  throw new InvalidClientMetadataError(
195
- `Unsupported ${endpoint}_endpoint_auth_method "${method}"`,
183
+ `Unsupported token_endpoint_auth_method "${method}"`,
196
184
  )
197
185
  }
198
186
 
@@ -204,11 +192,11 @@ export class Client {
204
192
  */
205
193
  public async validateClientAuth(clientAuth: ClientAuth): Promise<boolean> {
206
194
  if (clientAuth.method === 'none') {
207
- return this.getAuthMethod('token') === 'none'
195
+ return this.metadata[`token_endpoint_auth_method`] === 'none'
208
196
  }
209
197
 
210
198
  if (clientAuth.method === CLIENT_ASSERTION_TYPE_JWT_BEARER) {
211
- if (this.getAuthMethod('token') !== 'private_key_jwt') {
199
+ if (this.metadata[`token_endpoint_auth_method`] !== 'private_key_jwt') {
212
200
  return false
213
201
  }
214
202
  try {
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
  }
@@ -52,13 +52,12 @@ export class DpopManager {
52
52
 
53
53
  const { protectedHeader, payload } = await jwtVerify<{
54
54
  iat: number
55
- exp: number
56
55
  jti: string
57
56
  }>(proof, EmbeddedJWK, {
58
57
  typ: 'dpop+jwt',
59
58
  maxTokenAge: 10,
60
59
  clockTolerance: DPOP_NONCE_MAX_AGE / 1e3,
61
- requiredClaims: ['iat', 'exp', 'jti'],
60
+ requiredClaims: ['iat', 'jti'],
62
61
  }).catch((err) => {
63
62
  const message =
64
63
  err instanceof JOSEError
@@ -71,10 +70,6 @@ export class DpopManager {
71
70
  throw new InvalidDpopProofError('Invalid or missing jti property')
72
71
  }
73
72
 
74
- if (payload.exp - payload.iat > DPOP_NONCE_MAX_AGE / 3 / 1e3) {
75
- throw new InvalidDpopProofError('DPoP proof validity too long')
76
- }
77
-
78
73
  // Note rfc9110#section-9.1 states that the method name is case-sensitive
79
74
  if (!htm || htm !== payload['htm']) {
80
75
  throw new InvalidDpopProofError('DPoP htm mismatch')
@@ -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
  }
@@ -1,13 +1,33 @@
1
1
  import { parse as parseJson } from '@hapi/bourne'
2
+ import { type as hapiContentType } from '@hapi/content'
2
3
  import createHttpError from 'http-errors'
3
4
 
4
5
  export type JsonScalar = string | number | boolean | null
5
6
  export type Json = JsonScalar | Json[] | { [_ in string]?: Json }
6
7
 
8
+ export const parseContentType = (type: string): ContentType => {
9
+ try {
10
+ return hapiContentType(type)
11
+ } catch (err) {
12
+ // De-boomify the error
13
+ if (err?.['isBoom']) {
14
+ throw createHttpError(err['output']['statusCode'], err['message'])
15
+ }
16
+
17
+ throw err
18
+ }
19
+ }
20
+
21
+ export type ContentType = {
22
+ mime: string
23
+ charset?: string
24
+ boundary?: string
25
+ }
26
+
7
27
  export type Parser<T extends string = string, R = unknown> = {
8
28
  readonly name: string
9
- readonly test: (type: string) => type is T
10
- readonly parse: (buffer: Buffer) => R
29
+ readonly test: (mime: string) => mime is T
30
+ readonly parse: (buffer: Buffer, type: ContentType) => R
11
31
  }
12
32
 
13
33
  export type ParserName<P extends Parser> = P extends { readonly name: infer N }
@@ -22,12 +42,13 @@ export type ParserForType<P extends Parser, T> =
22
42
  export const parsers = [
23
43
  {
24
44
  name: 'json',
25
- test: (
26
- type: string,
27
- ): type is `application/json` | `application/${string}+json` => {
28
- return /^application\/(?:.+\+)?json$/.test(type)
45
+ test: (mime): mime is `application/json` | `application/${string}+json` => {
46
+ return /^application\/(?:.+\+)?json$/.test(mime)
29
47
  },
30
- parse: (buffer: Buffer): Json => {
48
+ parse: (buffer, { charset }): Json => {
49
+ if (charset != null && !/^utf-?8$/i.test(charset)) {
50
+ throw createHttpError(415, 'Unsupported charset')
51
+ }
31
52
  try {
32
53
  return parseJson(buffer.toString())
33
54
  } catch (err) {
@@ -37,10 +58,13 @@ export const parsers = [
37
58
  },
38
59
  {
39
60
  name: 'urlencoded',
40
- test: (type: string): type is 'application/x-www-form-urlencoded' => {
41
- return type === 'application/x-www-form-urlencoded'
61
+ test: (mime): mime is 'application/x-www-form-urlencoded' => {
62
+ return mime === 'application/x-www-form-urlencoded'
42
63
  },
43
- parse: (buffer: Buffer): Partial<Record<string, string>> => {
64
+ parse: (buffer, { charset }): Partial<Record<string, string>> => {
65
+ if (charset != null && !/^utf-?8$/i.test(charset)) {
66
+ throw createHttpError(415, 'Unsupported charset')
67
+ }
44
68
  try {
45
69
  if (!buffer.length) return {}
46
70
  return Object.fromEntries(new URLSearchParams(buffer.toString()))
@@ -51,10 +75,10 @@ export const parsers = [
51
75
  },
52
76
  {
53
77
  name: 'bytes',
54
- test: (type: string): type is 'application/octet-stream' => {
55
- return type === 'application/octet-stream'
78
+ test: (mime): mime is 'application/octet-stream' => {
79
+ return mime === 'application/octet-stream'
56
80
  },
57
- parse: (buffer: Buffer): Buffer => buffer,
81
+ parse: (buffer): Buffer => buffer,
58
82
  },
59
83
  ] as const satisfies Parser[]
60
84
 
@@ -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) {
@@ -7,6 +7,7 @@ import {
7
7
  KnownNames,
8
8
  KnownParser,
9
9
  KnownTypes,
10
+ parseContentType,
10
11
  ParserForType,
11
12
  ParserResult,
12
13
  parsers,
@@ -64,9 +65,11 @@ export async function parseStream(
64
65
  throw createHttpError(400, 'Invalid content-type')
65
66
  }
66
67
 
68
+ const type = parseContentType(contentType)
69
+
67
70
  const parser = parsers.find(
68
71
  (parser) =>
69
- allow?.includes(parser.name) !== false && parser.test(contentType),
72
+ allow?.includes(parser.name) !== false && parser.test(type.mime),
70
73
  )
71
74
 
72
75
  if (!parser) {
@@ -74,5 +77,5 @@ export async function parseStream(
74
77
  }
75
78
 
76
79
  const buffer = await readStream(req)
77
- return parser.parse(buffer)
80
+ return parser.parse(buffer, type)
78
81
  }
@@ -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,
@@ -127,28 +95,13 @@ export function buildMetadata(
127
95
  token_endpoint_auth_signing_alg_values_supported: [...VERIFY_ALGOS],
128
96
 
129
97
  revocation_endpoint: new URL('/oauth/revoke', issuer).href,
130
- revocation_endpoint_auth_methods_supported: [
131
- ...Client.AUTH_METHODS_SUPPORTED,
132
- ],
133
- revocation_endpoint_auth_signing_alg_values_supported: [...VERIFY_ALGOS],
134
98
 
135
99
  introspection_endpoint: new URL('/oauth/introspect', issuer).href,
136
- introspection_endpoint_auth_methods_supported: [
137
- ...Client.AUTH_METHODS_SUPPORTED,
138
- ],
139
- introspection_endpoint_auth_signing_alg_values_supported: [...VERIFY_ALGOS],
140
100
 
141
- userinfo_endpoint: new URL('/oauth/userinfo', issuer).href,
142
101
  // end_session_endpoint: new URL('/oauth/logout', issuer).href,
143
102
 
144
103
  // https://datatracker.ietf.org/doc/html/rfc9126#section-5
145
104
  pushed_authorization_request_endpoint: new URL('/oauth/par', issuer).href,
146
- pushed_authorization_request_endpoint_auth_methods_supported: [
147
- ...Client.AUTH_METHODS_SUPPORTED,
148
- ],
149
- pushed_authorization_request_endpoint_auth_signing_alg_values_supported: [
150
- ...VERIFY_ALGOS,
151
- ],
152
105
 
153
106
  require_pushed_authorization_requests: true,
154
107
 
@@ -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
  }