@atproto/oauth-provider 0.9.3 → 0.10.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 +22 -0
- package/dist/client/client-manager.d.ts.map +1 -1
- package/dist/client/client-manager.js +0 -7
- package/dist/client/client-manager.js.map +1 -1
- package/dist/metadata/build-metadata.d.ts +0 -1
- package/dist/metadata/build-metadata.d.ts.map +1 -1
- package/dist/metadata/build-metadata.js +6 -2
- package/dist/metadata/build-metadata.js.map +1 -1
- package/dist/oauth-hooks.d.ts +14 -1
- package/dist/oauth-hooks.d.ts.map +1 -1
- package/dist/oauth-hooks.js +3 -1
- package/dist/oauth-hooks.js.map +1 -1
- package/dist/oauth-provider.d.ts +1 -0
- package/dist/oauth-provider.d.ts.map +1 -1
- package/dist/oauth-provider.js +18 -29
- package/dist/oauth-provider.js.map +1 -1
- package/dist/oauth-verifier.d.ts +2 -1
- package/dist/oauth-verifier.d.ts.map +1 -1
- package/dist/oauth-verifier.js.map +1 -1
- package/dist/request/request-manager.d.ts +5 -5
- package/dist/request/request-manager.d.ts.map +1 -1
- package/dist/request/request-manager.js +52 -34
- package/dist/request/request-manager.js.map +1 -1
- package/dist/request/request-store.d.ts +6 -6
- package/dist/request/request-store.d.ts.map +1 -1
- package/dist/result/authorization-result-authorize-page.d.ts +2 -3
- package/dist/result/authorization-result-authorize-page.d.ts.map +1 -1
- package/dist/router/assets/send-authorization-page.js +3 -2
- package/dist/router/assets/send-authorization-page.js.map +1 -1
- package/dist/router/create-api-middleware.d.ts.map +1 -1
- package/dist/router/create-api-middleware.js +9 -4
- package/dist/router/create-api-middleware.js.map +1 -1
- package/dist/token/verify-token-claims.d.ts +1 -0
- package/dist/token/verify-token-claims.d.ts.map +1 -1
- package/dist/token/verify-token-claims.js.map +1 -1
- package/package.json +6 -5
- package/src/client/client-manager.ts +0 -8
- package/src/metadata/build-metadata.ts +8 -3
- package/src/oauth-hooks.ts +15 -0
- package/src/oauth-provider.ts +19 -30
- package/src/oauth-verifier.ts +3 -1
- package/src/request/request-manager.ts +65 -38
- package/src/request/request-store.ts +6 -6
- package/src/result/authorization-result-authorize-page.ts +2 -3
- package/src/router/assets/send-authorization-page.ts +3 -3
- package/src/router/create-api-middleware.ts +9 -3
- package/src/token/verify-token-claims.ts +8 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/request/request-info.d.ts +0 -14
- package/dist/request/request-info.d.ts.map +0 -1
- package/dist/request/request-info.js +0 -3
- package/dist/request/request-info.js.map +0 -1
- package/src/request/request-info.ts +0 -14
package/src/oauth-provider.ts
CHANGED
@@ -233,6 +233,7 @@ export type OAuthProviderOptions = OAuthProviderConfig &
|
|
233
233
|
|
234
234
|
export class OAuthProvider extends OAuthVerifier {
|
235
235
|
protected readonly accessTokenMode: AccessTokenMode
|
236
|
+
protected readonly hooks: OAuthHooks
|
236
237
|
|
237
238
|
public readonly metadata: OAuthAuthorizationServerMetadata
|
238
239
|
public readonly customization: Customization
|
@@ -286,21 +287,19 @@ export class OAuthProvider extends OAuthVerifier {
|
|
286
287
|
const deviceManagerOptions: DeviceManagerOptions =
|
287
288
|
deviceManagerOptionsSchema.parse(rest)
|
288
289
|
|
289
|
-
// @NOTE: hooks don't really need a type parser, as all zod can actually
|
290
|
-
// check at runtime is the fact that the values are functions. The only way
|
291
|
-
// we would benefit from zod here would be to wrap the functions with a
|
292
|
-
// validator for the provided function's return types, which we do not add
|
293
|
-
// because it would impact runtime performance and we trust the users of
|
294
|
-
// this lib (basically ourselves) to rely on the typing system to ensure the
|
295
|
-
// correct types are returned.
|
296
|
-
const hooks: OAuthHooks = rest
|
297
|
-
|
298
290
|
// @NOTE: validation of super params (if we wanted to implement it) should
|
299
291
|
// be the responsibility of the super class.
|
300
292
|
const superOptions: OAuthVerifierOptions = rest
|
301
293
|
|
302
294
|
super({ replayStore, ...superOptions })
|
303
295
|
|
296
|
+
// @NOTE: hooks don't really need a type parser, as all zod can actually
|
297
|
+
// check at runtime is the fact that the values are functions. The only way
|
298
|
+
// we would benefit from zod here would be to wrap the functions with a
|
299
|
+
// validator for the provided function's return types, which we don't
|
300
|
+
// really need if types are respected.
|
301
|
+
this.hooks = rest
|
302
|
+
|
304
303
|
this.accessTokenMode = accessTokenMode
|
305
304
|
this.authenticationMaxAge = authenticationMaxAge
|
306
305
|
this.metadata = buildMetadata(this.issuer, this.keyset, metadata)
|
@@ -310,13 +309,13 @@ export class OAuthProvider extends OAuthVerifier {
|
|
310
309
|
this.accountManager = new AccountManager(
|
311
310
|
this.issuer,
|
312
311
|
accountStore,
|
313
|
-
hooks,
|
312
|
+
this.hooks,
|
314
313
|
this.customization,
|
315
314
|
)
|
316
315
|
this.clientManager = new ClientManager(
|
317
316
|
this.metadata,
|
318
317
|
this.keyset,
|
319
|
-
hooks,
|
318
|
+
this.hooks,
|
320
319
|
clientStore || null,
|
321
320
|
loopbackMetadata || null,
|
322
321
|
safeFetch,
|
@@ -327,12 +326,12 @@ export class OAuthProvider extends OAuthVerifier {
|
|
327
326
|
requestStore,
|
328
327
|
this.signer,
|
329
328
|
this.metadata,
|
330
|
-
hooks,
|
329
|
+
this.hooks,
|
331
330
|
)
|
332
331
|
this.tokenManager = new TokenManager(
|
333
332
|
tokenStore,
|
334
333
|
this.signer,
|
335
|
-
hooks,
|
334
|
+
this.hooks,
|
336
335
|
this.accessTokenMode,
|
337
336
|
tokenMaxAge,
|
338
337
|
)
|
@@ -502,7 +501,7 @@ export class OAuthProvider extends OAuthVerifier {
|
|
502
501
|
}
|
503
502
|
}
|
504
503
|
|
505
|
-
const {
|
504
|
+
const { requestUri, expiresAt } =
|
506
505
|
await this.requestManager.createAuthorizationRequest(
|
507
506
|
client,
|
508
507
|
clientAuth,
|
@@ -511,7 +510,7 @@ export class OAuthProvider extends OAuthVerifier {
|
|
511
510
|
)
|
512
511
|
|
513
512
|
return {
|
514
|
-
request_uri:
|
513
|
+
request_uri: requestUri,
|
515
514
|
expires_in: dateToRelativeSeconds(expiresAt),
|
516
515
|
}
|
517
516
|
} catch (err) {
|
@@ -600,7 +599,7 @@ export class OAuthProvider extends OAuthVerifier {
|
|
600
599
|
.getClient(clientCredentials.client_id)
|
601
600
|
.catch(throwAuthorizationError)
|
602
601
|
|
603
|
-
const { parameters,
|
602
|
+
const { parameters, requestUri } = await this.processAuthorizationRequest(
|
604
603
|
client,
|
605
604
|
deviceId,
|
606
605
|
query,
|
@@ -627,7 +626,7 @@ export class OAuthProvider extends OAuthVerifier {
|
|
627
626
|
}
|
628
627
|
|
629
628
|
const code = await this.requestManager.setAuthorized(
|
630
|
-
|
629
|
+
requestUri,
|
631
630
|
client,
|
632
631
|
ssoSession.account,
|
633
632
|
deviceId,
|
@@ -644,7 +643,7 @@ export class OAuthProvider extends OAuthVerifier {
|
|
644
643
|
const ssoSession = ssoSessions[0]!
|
645
644
|
if (!ssoSession.loginRequired && !ssoSession.consentRequired) {
|
646
645
|
const code = await this.requestManager.setAuthorized(
|
647
|
-
|
646
|
+
requestUri,
|
648
647
|
client,
|
649
648
|
ssoSession.account,
|
650
649
|
deviceId,
|
@@ -660,7 +659,7 @@ export class OAuthProvider extends OAuthVerifier {
|
|
660
659
|
issuer,
|
661
660
|
client,
|
662
661
|
parameters,
|
663
|
-
|
662
|
+
requestUri,
|
664
663
|
sessions: sessions.map((session) => ({
|
665
664
|
// Map to avoid leaking other data that might be present in the session
|
666
665
|
account: session.account,
|
@@ -668,20 +667,10 @@ export class OAuthProvider extends OAuthVerifier {
|
|
668
667
|
loginRequired: session.loginRequired,
|
669
668
|
consentRequired: session.consentRequired,
|
670
669
|
})),
|
671
|
-
scopeDetails: parameters.scope
|
672
|
-
?.split(/\s+/)
|
673
|
-
.filter(Boolean)
|
674
|
-
.sort((a, b) => a.localeCompare(b))
|
675
|
-
.map((scope) => ({
|
676
|
-
scope,
|
677
|
-
// @TODO Allow to customize the scope descriptions (e.g.
|
678
|
-
// using a hook)
|
679
|
-
description: undefined,
|
680
|
-
})),
|
681
670
|
}
|
682
671
|
} catch (err) {
|
683
672
|
try {
|
684
|
-
await this.requestManager.delete(
|
673
|
+
await this.requestManager.delete(requestUri)
|
685
674
|
} catch {
|
686
675
|
// There are two error here. Better keep the outer one.
|
687
676
|
//
|
package/src/oauth-verifier.ts
CHANGED
@@ -26,6 +26,8 @@ import {
|
|
26
26
|
verifyTokenClaims,
|
27
27
|
} from './token/verify-token-claims.js'
|
28
28
|
|
29
|
+
export type * from './token/verify-token-claims.js'
|
30
|
+
|
29
31
|
export type OAuthVerifierOptions = Override<
|
30
32
|
DpopManagerOptions,
|
31
33
|
{
|
@@ -51,7 +53,7 @@ export type OAuthVerifierOptions = Override<
|
|
51
53
|
>
|
52
54
|
|
53
55
|
export { DpopNonce, Key, Keyset }
|
54
|
-
export type { DpopProof, RedisOptions, ReplayStore
|
56
|
+
export type { DpopProof, RedisOptions, ReplayStore }
|
55
57
|
|
56
58
|
export class OAuthVerifier {
|
57
59
|
public readonly issuer: OAuthIssuerIdentifier
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import { isAtprotoDid } from '@atproto/did'
|
2
2
|
import type { Account } from '@atproto/oauth-provider-api'
|
3
|
+
import { isValidAtprotoOauthScope } from '@atproto/oauth-scopes'
|
3
4
|
import {
|
4
5
|
OAuthAuthorizationRequestParameters,
|
5
6
|
OAuthAuthorizationServerMetadata,
|
@@ -60,10 +61,16 @@ export class RequestManager {
|
|
60
61
|
) {
|
61
62
|
const parameters = await this.validate(client, clientAuth, input)
|
62
63
|
|
64
|
+
await callAsync(this.hooks.onAuthorizationRequest, {
|
65
|
+
client,
|
66
|
+
clientAuth,
|
67
|
+
parameters,
|
68
|
+
})
|
69
|
+
|
63
70
|
const expiresAt = new Date(Date.now() + PAR_EXPIRES_IN)
|
64
|
-
const
|
71
|
+
const requestId = await generateRequestId()
|
65
72
|
|
66
|
-
await this.store.createRequest(
|
73
|
+
await this.store.createRequest(requestId, {
|
67
74
|
clientId: client.id,
|
68
75
|
clientAuth,
|
69
76
|
parameters,
|
@@ -73,8 +80,8 @@ export class RequestManager {
|
|
73
80
|
code: null,
|
74
81
|
})
|
75
82
|
|
76
|
-
const
|
77
|
-
return {
|
83
|
+
const requestUri = encodeRequestUri(requestId)
|
84
|
+
return { requestUri, expiresAt, parameters }
|
78
85
|
}
|
79
86
|
|
80
87
|
protected async validate(
|
@@ -124,20 +131,6 @@ export class RequestManager {
|
|
124
131
|
)
|
125
132
|
}
|
126
133
|
|
127
|
-
if (parameters.scope) {
|
128
|
-
for (const scope of parameters.scope.split(' ')) {
|
129
|
-
// Currently, the implementation requires all the scopes to be statically
|
130
|
-
// defined in the server metadata. In the future, we might add support
|
131
|
-
// for dynamic scopes.
|
132
|
-
if (!this.metadata.scopes_supported?.includes(scope)) {
|
133
|
-
throw new AuthorizationError(
|
134
|
-
parameters,
|
135
|
-
`Scope "${scope}" is not supported by this server`,
|
136
|
-
)
|
137
|
-
}
|
138
|
-
}
|
139
|
-
}
|
140
|
-
|
141
134
|
if (parameters.authorization_details) {
|
142
135
|
for (const detail of parameters.authorization_details) {
|
143
136
|
if (
|
@@ -177,8 +170,16 @@ export class RequestManager {
|
|
177
170
|
// > server MUST include the scope response parameter in the token response
|
178
171
|
// > (Section 3.2.3) to inform the client of the actual scope granted.
|
179
172
|
|
180
|
-
// Let's make sure the scopes are unique (to reduce the token & storage
|
181
|
-
|
173
|
+
// Let's make sure the scopes are unique (to reduce the token & storage
|
174
|
+
// size) & are indeed supported.
|
175
|
+
|
176
|
+
// @NOTE An app requesting a not yet supported list of scopes will need to
|
177
|
+
// re-authenticate the user once the scopes are supported. This is due to
|
178
|
+
// the fact that the AS does not know how to properly display those scopes
|
179
|
+
// to the user, so it cannot properly ask for consent.
|
180
|
+
const scopes = new Set(
|
181
|
+
parameters.scope?.split(' ')?.filter(isValidAtprotoOauthScope),
|
182
|
+
)
|
182
183
|
|
183
184
|
parameters = { ...parameters, scope: [...scopes].join(' ') || undefined }
|
184
185
|
|
@@ -290,10 +291,10 @@ export class RequestManager {
|
|
290
291
|
return parameters
|
291
292
|
}
|
292
293
|
|
293
|
-
async get(
|
294
|
-
const
|
294
|
+
async get(requestUri: RequestUri, deviceId: DeviceId, clientId?: ClientId) {
|
295
|
+
const requestId = decodeRequestUri(requestUri)
|
295
296
|
|
296
|
-
const data = await this.store.readRequest(
|
297
|
+
const data = await this.store.readRequest(requestId)
|
297
298
|
if (!data) throw new InvalidRequestError('Unknown request_uri')
|
298
299
|
|
299
300
|
const updates: UpdateRequestData = {}
|
@@ -332,16 +333,16 @@ export class RequestManager {
|
|
332
333
|
)
|
333
334
|
}
|
334
335
|
} catch (err) {
|
335
|
-
await this.store.deleteRequest(
|
336
|
+
await this.store.deleteRequest(requestId)
|
336
337
|
throw err
|
337
338
|
}
|
338
339
|
|
339
340
|
if (Object.keys(updates).length > 0) {
|
340
|
-
await this.store.updateRequest(
|
341
|
+
await this.store.updateRequest(requestId, updates)
|
341
342
|
}
|
342
343
|
|
343
344
|
return {
|
344
|
-
|
345
|
+
requestUri,
|
345
346
|
expiresAt: updates.expiresAt || data.expiresAt,
|
346
347
|
parameters: data.parameters,
|
347
348
|
clientId: data.clientId,
|
@@ -349,40 +350,65 @@ export class RequestManager {
|
|
349
350
|
}
|
350
351
|
|
351
352
|
async setAuthorized(
|
352
|
-
|
353
|
+
requestUri: RequestUri,
|
353
354
|
client: Client,
|
354
355
|
account: Account,
|
355
356
|
deviceId: DeviceId,
|
356
357
|
deviceMetadata: RequestMetadata,
|
358
|
+
scopeOverride?: string,
|
357
359
|
): Promise<Code> {
|
358
|
-
const requestId = decodeRequestUri(
|
360
|
+
const requestId = decodeRequestUri(requestUri)
|
359
361
|
|
360
362
|
const data = await this.store.readRequest(requestId)
|
361
363
|
if (!data) throw new InvalidRequestError('Unknown request_uri')
|
362
364
|
|
365
|
+
let { parameters } = data
|
366
|
+
|
363
367
|
try {
|
364
368
|
if (data.expiresAt < new Date()) {
|
365
|
-
throw new AccessDeniedError(
|
369
|
+
throw new AccessDeniedError(parameters, 'This request has expired')
|
366
370
|
}
|
367
371
|
if (!data.deviceId) {
|
368
372
|
throw new AccessDeniedError(
|
369
|
-
|
373
|
+
parameters,
|
370
374
|
'This request was not initiated',
|
371
375
|
)
|
372
376
|
}
|
373
377
|
if (data.deviceId !== deviceId) {
|
374
378
|
throw new AccessDeniedError(
|
375
|
-
|
379
|
+
parameters,
|
376
380
|
'This request was initiated from another device',
|
377
381
|
)
|
378
382
|
}
|
379
383
|
if (data.sub || data.code) {
|
380
384
|
throw new AccessDeniedError(
|
381
|
-
|
385
|
+
parameters,
|
382
386
|
'This request was already authorized',
|
383
387
|
)
|
384
388
|
}
|
385
389
|
|
390
|
+
// If a new scope value is provided, update the parameters by ensuring
|
391
|
+
// that every existing scope in the parameters is also present in the
|
392
|
+
// override value. This allows the user to remove scopes from the request,
|
393
|
+
// but not to add new ones.
|
394
|
+
if (scopeOverride != null) {
|
395
|
+
const allowedScopes = new Set(scopeOverride.split(' '))
|
396
|
+
const existingScopes = parameters.scope?.split(' ')
|
397
|
+
|
398
|
+
// Compute the intersection of the existing scopes and the overrides.
|
399
|
+
const newScopes = existingScopes?.filter((s) => allowedScopes.has(s))
|
400
|
+
|
401
|
+
// Validate: make sure the new scopes are valid
|
402
|
+
if (!newScopes?.includes('atproto')) {
|
403
|
+
throw new AccessDeniedError(
|
404
|
+
parameters,
|
405
|
+
'The "atproto" scope is required',
|
406
|
+
)
|
407
|
+
}
|
408
|
+
|
409
|
+
parameters = { ...parameters, scope: newScopes.join(' ') }
|
410
|
+
}
|
411
|
+
|
386
412
|
// Only response_type=code is supported
|
387
413
|
const code = await generateCode()
|
388
414
|
|
@@ -392,12 +418,13 @@ export class RequestManager {
|
|
392
418
|
code,
|
393
419
|
// Allow the client to exchange the code for a token within the next 60 seconds.
|
394
420
|
expiresAt: new Date(Date.now() + AUTHORIZATION_INACTIVITY_TIMEOUT),
|
421
|
+
parameters,
|
395
422
|
})
|
396
423
|
|
397
424
|
await callAsync(this.hooks.onAuthorized, {
|
398
425
|
client,
|
399
426
|
account,
|
400
|
-
parameters
|
427
|
+
parameters,
|
401
428
|
deviceId,
|
402
429
|
deviceMetadata,
|
403
430
|
requestId,
|
@@ -418,12 +445,12 @@ export class RequestManager {
|
|
418
445
|
const result = await this.store.consumeRequestCode(code)
|
419
446
|
if (!result) throw new InvalidGrantError('Invalid code')
|
420
447
|
|
421
|
-
const {
|
448
|
+
const { requestId, data } = result
|
422
449
|
|
423
450
|
// Fool-proofing the store implementation against code replay attacks (in
|
424
451
|
// case consumeRequestCode() does not delete the request).
|
425
452
|
if (NODE_ENV !== 'production') {
|
426
|
-
const result = await this.store.readRequest(
|
453
|
+
const result = await this.store.readRequest(requestId)
|
427
454
|
if (result) {
|
428
455
|
throw new Error('Invalid store implementation: request not deleted')
|
429
456
|
}
|
@@ -441,8 +468,8 @@ export class RequestManager {
|
|
441
468
|
return data
|
442
469
|
}
|
443
470
|
|
444
|
-
async delete(
|
445
|
-
const
|
446
|
-
await this.store.deleteRequest(
|
471
|
+
async delete(requestUri: RequestUri): Promise<void> {
|
472
|
+
const requestId = decodeRequestUri(requestUri)
|
473
|
+
await this.store.deleteRequest(requestId)
|
447
474
|
}
|
448
475
|
}
|
@@ -12,25 +12,25 @@ export type { Awaitable }
|
|
12
12
|
|
13
13
|
export type UpdateRequestData = Pick<
|
14
14
|
Partial<RequestData>,
|
15
|
-
'sub' | 'code' | 'deviceId' | 'expiresAt'
|
15
|
+
'sub' | 'code' | 'deviceId' | 'expiresAt' | 'parameters'
|
16
16
|
>
|
17
17
|
|
18
18
|
export type FoundRequestResult = {
|
19
|
-
|
19
|
+
requestId: RequestId
|
20
20
|
data: RequestData
|
21
21
|
}
|
22
22
|
|
23
23
|
export { InvalidGrantError }
|
24
24
|
|
25
25
|
export interface RequestStore {
|
26
|
-
createRequest(
|
26
|
+
createRequest(requestId: RequestId, data: RequestData): Awaitable<void>
|
27
27
|
/**
|
28
28
|
* Note that expired requests **can** be returned to yield a different error
|
29
29
|
* message than if the request was not found.
|
30
30
|
*/
|
31
|
-
readRequest(
|
32
|
-
updateRequest(
|
33
|
-
deleteRequest(
|
31
|
+
readRequest(requestId: RequestId): Awaitable<RequestData | null>
|
32
|
+
updateRequest(requestId: RequestId, data: UpdateRequestData): Awaitable<void>
|
33
|
+
deleteRequest(requestId: RequestId): void | Awaitable<void>
|
34
34
|
/**
|
35
35
|
* @note it is **IMPORTANT** that this method prevents concurrent retrieval of
|
36
36
|
* the same code. If two requests are made with the same code, only one of
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import type {
|
1
|
+
import type { Session } from '@atproto/oauth-provider-api'
|
2
2
|
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
3
3
|
import { Client } from '../client/client.js'
|
4
4
|
import { RequestUri } from '../request/request-uri.js'
|
@@ -8,7 +8,6 @@ export type AuthorizationResultAuthorizePage = {
|
|
8
8
|
client: Client
|
9
9
|
parameters: OAuthAuthorizationRequestParameters
|
10
10
|
|
11
|
-
|
12
|
-
scopeDetails?: ScopeDetail[]
|
11
|
+
requestUri: RequestUri
|
13
12
|
sessions: readonly Session[]
|
14
13
|
}
|
@@ -36,14 +36,14 @@ export function sendAuthorizePageFactory(customization: Customization) {
|
|
36
36
|
const script = declareHydrationData<HydrationData['authorization-page']>({
|
37
37
|
__customizationData: customizationData,
|
38
38
|
__authorizeData: {
|
39
|
-
requestUri: data.
|
39
|
+
requestUri: data.requestUri,
|
40
40
|
|
41
41
|
clientId: data.client.id,
|
42
42
|
clientMetadata: data.client.metadata,
|
43
43
|
clientTrusted: data.client.info.isTrusted,
|
44
|
+
clientFirstParty: data.client.info.isFirstParty,
|
44
45
|
|
45
|
-
|
46
|
-
|
46
|
+
scope: data.parameters.scope,
|
47
47
|
uiLocales: data.parameters.ui_locales,
|
48
48
|
loginHint: data.parameters.login_hint,
|
49
49
|
},
|
@@ -398,8 +398,13 @@ export function createApiMiddleware<
|
|
398
398
|
router.use(
|
399
399
|
apiRoute({
|
400
400
|
method: 'POST',
|
401
|
-
endpoint: '/
|
402
|
-
schema: z
|
401
|
+
endpoint: '/consent',
|
402
|
+
schema: z
|
403
|
+
.object({
|
404
|
+
sub: z.union([subSchema, signedJwtSchema]),
|
405
|
+
scope: z.string().optional(),
|
406
|
+
})
|
407
|
+
.strict(),
|
403
408
|
async handler(req, res) {
|
404
409
|
if (!this.requestUri) {
|
405
410
|
throw new InvalidRequestError(
|
@@ -432,6 +437,7 @@ export function createApiMiddleware<
|
|
432
437
|
account,
|
433
438
|
this.deviceId,
|
434
439
|
this.deviceMetadata,
|
440
|
+
this.input.scope,
|
435
441
|
)
|
436
442
|
|
437
443
|
const clientData = authorizedClients.get(clientId)
|
@@ -459,7 +465,7 @@ export function createApiMiddleware<
|
|
459
465
|
throw AuthorizationError.from(parameters, err)
|
460
466
|
}
|
461
467
|
} catch (err) {
|
462
|
-
onError?.(req, res, err, 'Failed to
|
468
|
+
onError?.(req, res, err, 'Failed to consent authorization request')
|
463
469
|
|
464
470
|
// If any error happened (unauthenticated, invalid request, etc.),
|
465
471
|
// lets make sure the request can no longer be used.
|
@@ -10,6 +10,14 @@ import { TokenId } from './token-id.js'
|
|
10
10
|
const BEARER = 'Bearer' satisfies OAuthTokenType
|
11
11
|
const DPOP = 'DPoP' satisfies OAuthTokenType
|
12
12
|
|
13
|
+
export type {
|
14
|
+
DpopProof,
|
15
|
+
OAuthAccessToken,
|
16
|
+
OAuthTokenType,
|
17
|
+
SignedTokenPayload,
|
18
|
+
TokenId,
|
19
|
+
}
|
20
|
+
|
13
21
|
export type VerifyTokenClaimsOptions = {
|
14
22
|
/** One of these audience must be included in the token audience(s) */
|
15
23
|
audience?: [string, ...string[]]
|
@@ -1 +1 @@
|
|
1
|
-
{"root":["./src/constants.ts","./src/index.ts","./src/oauth-client.ts","./src/oauth-dpop.ts","./src/oauth-errors.ts","./src/oauth-hooks.ts","./src/oauth-middleware.ts","./src/oauth-provider.ts","./src/oauth-store.ts","./src/oauth-verifier.ts","./src/access-token/access-token-mode.ts","./src/account/account-manager.ts","./src/account/account-store.ts","./src/account/sign-in-data.ts","./src/account/sign-up-input.ts","./src/client/client-auth.ts","./src/client/client-data.ts","./src/client/client-id.ts","./src/client/client-info.ts","./src/client/client-manager.ts","./src/client/client-store.ts","./src/client/client-utils.ts","./src/client/client.ts","./src/customization/branding.ts","./src/customization/build-customization-css.ts","./src/customization/build-customization-data.ts","./src/customization/colors.ts","./src/customization/customization.ts","./src/customization/links.ts","./src/device/device-data.ts","./src/device/device-id.ts","./src/device/device-manager.ts","./src/device/device-store.ts","./src/device/session-id.ts","./src/dpop/dpop-manager.ts","./src/dpop/dpop-nonce.ts","./src/dpop/dpop-proof.ts","./src/errors/access-denied-error.ts","./src/errors/account-selection-required-error.ts","./src/errors/authorization-error.ts","./src/errors/consent-required-error.ts","./src/errors/error-parser.ts","./src/errors/handle-unavailable-error.ts","./src/errors/invalid-authorization-details-error.ts","./src/errors/invalid-client-error.ts","./src/errors/invalid-client-id-error.ts","./src/errors/invalid-client-metadata-error.ts","./src/errors/invalid-dpop-key-binding-error.ts","./src/errors/invalid-dpop-proof-error.ts","./src/errors/invalid-grant-error.ts","./src/errors/invalid-invite-code-error.ts","./src/errors/invalid-redirect-uri-error.ts","./src/errors/invalid-request-error.ts","./src/errors/invalid-scope-error.ts","./src/errors/invalid-token-error.ts","./src/errors/login-required-error.ts","./src/errors/oauth-error.ts","./src/errors/second-authentication-factor-required-error.ts","./src/errors/unauthorized-client-error.ts","./src/errors/use-dpop-nonce-error.ts","./src/errors/www-authenticate-error.ts","./src/lib/hcaptcha.ts","./src/lib/redis.ts","./src/lib/send-web-page.ts","./src/lib/csp/index.ts","./src/lib/html/build-document.ts","./src/lib/html/escapers.ts","./src/lib/html/html.ts","./src/lib/html/hydration-data.ts","./src/lib/html/index.ts","./src/lib/html/tags.ts","./src/lib/html/util.ts","./src/lib/http/accept.ts","./src/lib/http/context.ts","./src/lib/http/headers.ts","./src/lib/http/index.ts","./src/lib/http/method.ts","./src/lib/http/middleware.ts","./src/lib/http/parser.ts","./src/lib/http/path.ts","./src/lib/http/request.ts","./src/lib/http/response.ts","./src/lib/http/route.ts","./src/lib/http/router.ts","./src/lib/http/security-headers.ts","./src/lib/http/stream.ts","./src/lib/http/types.ts","./src/lib/http/url.ts","./src/lib/util/authorization-header.ts","./src/lib/util/cast.ts","./src/lib/util/color.ts","./src/lib/util/crypto.ts","./src/lib/util/date.ts","./src/lib/util/error.ts","./src/lib/util/function.ts","./src/lib/util/locale.ts","./src/lib/util/redirect-uri.ts","./src/lib/util/time.ts","./src/lib/util/type.ts","./src/lib/util/ui8.ts","./src/lib/util/well-known.ts","./src/lib/util/zod-error.ts","./src/metadata/build-metadata.ts","./src/oidc/sub.ts","./src/replay/replay-manager.ts","./src/replay/replay-store-memory.ts","./src/replay/replay-store-redis.ts","./src/replay/replay-store.ts","./src/request/code.ts","./src/request/request-data.ts","./src/request/request-id.ts","./src/request/request-
|
1
|
+
{"root":["./src/constants.ts","./src/index.ts","./src/oauth-client.ts","./src/oauth-dpop.ts","./src/oauth-errors.ts","./src/oauth-hooks.ts","./src/oauth-middleware.ts","./src/oauth-provider.ts","./src/oauth-store.ts","./src/oauth-verifier.ts","./src/access-token/access-token-mode.ts","./src/account/account-manager.ts","./src/account/account-store.ts","./src/account/sign-in-data.ts","./src/account/sign-up-input.ts","./src/client/client-auth.ts","./src/client/client-data.ts","./src/client/client-id.ts","./src/client/client-info.ts","./src/client/client-manager.ts","./src/client/client-store.ts","./src/client/client-utils.ts","./src/client/client.ts","./src/customization/branding.ts","./src/customization/build-customization-css.ts","./src/customization/build-customization-data.ts","./src/customization/colors.ts","./src/customization/customization.ts","./src/customization/links.ts","./src/device/device-data.ts","./src/device/device-id.ts","./src/device/device-manager.ts","./src/device/device-store.ts","./src/device/session-id.ts","./src/dpop/dpop-manager.ts","./src/dpop/dpop-nonce.ts","./src/dpop/dpop-proof.ts","./src/errors/access-denied-error.ts","./src/errors/account-selection-required-error.ts","./src/errors/authorization-error.ts","./src/errors/consent-required-error.ts","./src/errors/error-parser.ts","./src/errors/handle-unavailable-error.ts","./src/errors/invalid-authorization-details-error.ts","./src/errors/invalid-client-error.ts","./src/errors/invalid-client-id-error.ts","./src/errors/invalid-client-metadata-error.ts","./src/errors/invalid-dpop-key-binding-error.ts","./src/errors/invalid-dpop-proof-error.ts","./src/errors/invalid-grant-error.ts","./src/errors/invalid-invite-code-error.ts","./src/errors/invalid-redirect-uri-error.ts","./src/errors/invalid-request-error.ts","./src/errors/invalid-scope-error.ts","./src/errors/invalid-token-error.ts","./src/errors/login-required-error.ts","./src/errors/oauth-error.ts","./src/errors/second-authentication-factor-required-error.ts","./src/errors/unauthorized-client-error.ts","./src/errors/use-dpop-nonce-error.ts","./src/errors/www-authenticate-error.ts","./src/lib/hcaptcha.ts","./src/lib/redis.ts","./src/lib/send-web-page.ts","./src/lib/csp/index.ts","./src/lib/html/build-document.ts","./src/lib/html/escapers.ts","./src/lib/html/html.ts","./src/lib/html/hydration-data.ts","./src/lib/html/index.ts","./src/lib/html/tags.ts","./src/lib/html/util.ts","./src/lib/http/accept.ts","./src/lib/http/context.ts","./src/lib/http/headers.ts","./src/lib/http/index.ts","./src/lib/http/method.ts","./src/lib/http/middleware.ts","./src/lib/http/parser.ts","./src/lib/http/path.ts","./src/lib/http/request.ts","./src/lib/http/response.ts","./src/lib/http/route.ts","./src/lib/http/router.ts","./src/lib/http/security-headers.ts","./src/lib/http/stream.ts","./src/lib/http/types.ts","./src/lib/http/url.ts","./src/lib/util/authorization-header.ts","./src/lib/util/cast.ts","./src/lib/util/color.ts","./src/lib/util/crypto.ts","./src/lib/util/date.ts","./src/lib/util/error.ts","./src/lib/util/function.ts","./src/lib/util/locale.ts","./src/lib/util/redirect-uri.ts","./src/lib/util/time.ts","./src/lib/util/type.ts","./src/lib/util/ui8.ts","./src/lib/util/well-known.ts","./src/lib/util/zod-error.ts","./src/metadata/build-metadata.ts","./src/oidc/sub.ts","./src/replay/replay-manager.ts","./src/replay/replay-store-memory.ts","./src/replay/replay-store-redis.ts","./src/replay/replay-store.ts","./src/request/code.ts","./src/request/request-data.ts","./src/request/request-id.ts","./src/request/request-manager.ts","./src/request/request-store.ts","./src/request/request-uri.ts","./src/result/authorization-redirect-parameters.ts","./src/result/authorization-result-authorize-page.ts","./src/result/authorization-result-redirect.ts","./src/router/create-account-page-middleware.ts","./src/router/create-api-middleware.ts","./src/router/create-authorization-page-middleware.ts","./src/router/create-oauth-middleware.ts","./src/router/error-handler.ts","./src/router/middleware-options.ts","./src/router/send-redirect.ts","./src/router/assets/assets-manifest.ts","./src/router/assets/assets.ts","./src/router/assets/csrf.ts","./src/router/assets/send-account-page.ts","./src/router/assets/send-authorization-page.ts","./src/router/assets/send-error-page.ts","./src/signer/api-token-payload.ts","./src/signer/signed-token-payload.ts","./src/signer/signer.ts","./src/token/refresh-token.ts","./src/token/token-data.ts","./src/token/token-id.ts","./src/token/token-manager.ts","./src/token/token-store.ts","./src/token/verify-token-claims.ts","./src/types/authorization-response-error.ts","./src/types/color-hue.ts","./src/types/email-otp.ts","./src/types/email.ts","./src/types/handle.ts","./src/types/invite-code.ts","./src/types/par-response-error.ts","./src/types/password.ts","./src/types/rgb-color.ts"],"version":"5.8.3"}
|
@@ -1,14 +0,0 @@
|
|
1
|
-
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types';
|
2
|
-
import { ClientAuth } from '../client/client-auth.js';
|
3
|
-
import { ClientId } from '../client/client-id.js';
|
4
|
-
import { RequestId } from './request-id.js';
|
5
|
-
import { RequestUri } from './request-uri.js';
|
6
|
-
export type RequestInfo = {
|
7
|
-
id: RequestId;
|
8
|
-
uri: RequestUri;
|
9
|
-
parameters: Readonly<OAuthAuthorizationRequestParameters>;
|
10
|
-
expiresAt: Date;
|
11
|
-
clientId: ClientId;
|
12
|
-
clientAuth: null | ClientAuth;
|
13
|
-
};
|
14
|
-
//# sourceMappingURL=request-info.d.ts.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"request-info.d.ts","sourceRoot":"","sources":["../../src/request/request-info.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mCAAmC,EAAE,MAAM,sBAAsB,CAAA;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAA;AAE7C,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,SAAS,CAAA;IACb,GAAG,EAAE,UAAU,CAAA;IACf,UAAU,EAAE,QAAQ,CAAC,mCAAmC,CAAC,CAAA;IACzD,SAAS,EAAE,IAAI,CAAA;IACf,QAAQ,EAAE,QAAQ,CAAA;IAClB,UAAU,EAAE,IAAI,GAAG,UAAU,CAAA;CAC9B,CAAA"}
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"request-info.js","sourceRoot":"","sources":["../../src/request/request-info.ts"],"names":[],"mappings":""}
|
@@ -1,14 +0,0 @@
|
|
1
|
-
import { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
2
|
-
import { ClientAuth } from '../client/client-auth.js'
|
3
|
-
import { ClientId } from '../client/client-id.js'
|
4
|
-
import { RequestId } from './request-id.js'
|
5
|
-
import { RequestUri } from './request-uri.js'
|
6
|
-
|
7
|
-
export type RequestInfo = {
|
8
|
-
id: RequestId
|
9
|
-
uri: RequestUri
|
10
|
-
parameters: Readonly<OAuthAuthorizationRequestParameters>
|
11
|
-
expiresAt: Date
|
12
|
-
clientId: ClientId
|
13
|
-
clientAuth: null | ClientAuth
|
14
|
-
}
|