@atproto/oauth-provider 0.11.2 → 0.12.1
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 +30 -0
- package/dist/access-token/access-token-mode.d.ts +1 -1
- package/dist/access-token/access-token-mode.d.ts.map +1 -1
- package/dist/access-token/access-token-mode.js +1 -1
- package/dist/access-token/access-token-mode.js.map +1 -1
- package/dist/dpop/dpop-manager.d.ts.map +1 -1
- package/dist/dpop/dpop-manager.js +3 -1
- package/dist/dpop/dpop-manager.js.map +1 -1
- package/dist/dpop/dpop-proof.d.ts +2 -2
- package/dist/dpop/dpop-proof.d.ts.map +1 -1
- package/dist/lib/util/function.d.ts +1 -0
- package/dist/lib/util/function.d.ts.map +1 -1
- package/dist/lib/util/function.js +4 -0
- package/dist/lib/util/function.js.map +1 -1
- package/dist/oauth-hooks.d.ts +36 -3
- package/dist/oauth-hooks.d.ts.map +1 -1
- package/dist/oauth-hooks.js.map +1 -1
- package/dist/oauth-provider.d.ts +4 -4
- package/dist/oauth-provider.d.ts.map +1 -1
- package/dist/oauth-provider.js +11 -20
- package/dist/oauth-provider.js.map +1 -1
- package/dist/oauth-verifier.d.ts +22 -9
- package/dist/oauth-verifier.d.ts.map +1 -1
- package/dist/oauth-verifier.js +66 -6
- package/dist/oauth-verifier.js.map +1 -1
- package/dist/signer/{signed-token-payload.d.ts → access-token-payload.d.ts} +3 -3
- package/dist/signer/{signed-token-payload.d.ts.map → access-token-payload.d.ts.map} +1 -1
- package/dist/signer/{signed-token-payload.js → access-token-payload.js} +3 -3
- package/dist/signer/{signed-token-payload.js.map → access-token-payload.js.map} +1 -1
- package/dist/signer/signer.d.ts +3 -3
- package/dist/signer/signer.d.ts.map +1 -1
- package/dist/signer/signer.js +2 -2
- package/dist/signer/signer.js.map +1 -1
- package/dist/token/token-claims.d.ts +23 -0
- package/dist/token/token-claims.d.ts.map +1 -0
- package/dist/token/token-claims.js +3 -0
- package/dist/token/token-claims.js.map +1 -0
- package/dist/token/token-manager.d.ts +11 -6
- package/dist/token/token-manager.d.ts.map +1 -1
- package/dist/token/token-manager.js +39 -24
- package/dist/token/token-manager.js.map +1 -1
- package/package.json +7 -7
- package/src/access-token/access-token-mode.ts +1 -1
- package/src/dpop/dpop-manager.ts +3 -1
- package/src/dpop/dpop-proof.ts +2 -2
- package/src/lib/util/function.ts +4 -0
- package/src/oauth-hooks.ts +43 -1
- package/src/oauth-provider.ts +18 -36
- package/src/oauth-verifier.ts +131 -50
- package/src/signer/{signed-token-payload.ts → access-token-payload.ts} +2 -2
- package/src/signer/signer.ts +7 -7
- package/src/token/token-claims.ts +21 -0
- package/src/token/token-manager.ts +56 -51
- package/tsconfig.build.tsbuildinfo +1 -1
- package/dist/token/verify-token-claims.d.ts +0 -20
- package/dist/token/verify-token-claims.d.ts.map +0 -1
- package/dist/token/verify-token-claims.js +0 -53
- package/dist/token/verify-token-claims.js.map +0 -1
- package/src/token/verify-token-claims.ts +0 -101
@@ -4,6 +4,7 @@ import type { Account } from '@atproto/oauth-provider-api'
|
|
4
4
|
import {
|
5
5
|
OAuthAccessToken,
|
6
6
|
OAuthAuthorizationRequestParameters,
|
7
|
+
OAuthScope,
|
7
8
|
OAuthTokenResponse,
|
8
9
|
OAuthTokenType,
|
9
10
|
} from '@atproto/oauth-types'
|
@@ -20,26 +21,21 @@ import { RequestMetadata } from '../lib/http/request.js'
|
|
20
21
|
import { dateToEpoch, dateToRelativeSeconds } from '../lib/util/date.js'
|
21
22
|
import { callAsync } from '../lib/util/function.js'
|
22
23
|
import { OAuthHooks } from '../oauth-hooks.js'
|
23
|
-
import { DpopProof } from '../oauth-verifier.js'
|
24
24
|
import { Sub } from '../oidc/sub.js'
|
25
25
|
import { Code, isCode } from '../request/code.js'
|
26
|
-
import {
|
26
|
+
import { AccessTokenPayload } from '../signer/access-token-payload.js'
|
27
27
|
import { Signer } from '../signer/signer.js'
|
28
28
|
import {
|
29
29
|
RefreshToken,
|
30
30
|
generateRefreshToken,
|
31
31
|
isRefreshToken,
|
32
32
|
} from './refresh-token.js'
|
33
|
+
import { TokenClaims } from './token-claims.js'
|
33
34
|
import { TokenId, generateTokenId, isTokenId } from './token-id.js'
|
34
35
|
import { CreateTokenData, TokenInfo, TokenStore } from './token-store.js'
|
35
|
-
import {
|
36
|
-
VerifyTokenClaimsOptions,
|
37
|
-
VerifyTokenClaimsResult,
|
38
|
-
verifyTokenClaims,
|
39
|
-
} from './verify-token-claims.js'
|
40
36
|
|
41
37
|
export { AccessTokenMode, Signer }
|
42
|
-
export type { OAuthHooks, TokenStore
|
38
|
+
export type { OAuthHooks, TokenStore }
|
43
39
|
|
44
40
|
export class TokenManager {
|
45
41
|
constructor(
|
@@ -55,29 +51,44 @@ export class TokenManager {
|
|
55
51
|
return new Date(now.getTime() + this.tokenMaxAge)
|
56
52
|
}
|
57
53
|
|
58
|
-
protected async
|
54
|
+
protected async createAccessToken(
|
59
55
|
tokenId: TokenId,
|
60
|
-
account: Account,
|
61
56
|
client: Client,
|
57
|
+
account: Account,
|
62
58
|
parameters: OAuthAuthorizationRequestParameters,
|
63
|
-
|
59
|
+
issuedAt: Date,
|
64
60
|
expiresAt: Date,
|
65
|
-
scope:
|
61
|
+
scope: OAuthScope,
|
66
62
|
): Promise<OAuthAccessToken> {
|
67
|
-
|
63
|
+
const claims: TokenClaims = {
|
68
64
|
jti: tokenId,
|
69
65
|
sub: account.sub,
|
66
|
+
iat: dateToEpoch(issuedAt),
|
70
67
|
exp: dateToEpoch(expiresAt),
|
71
|
-
|
72
|
-
cnf: parameters.dpop_jkt ? { jkt: parameters.dpop_jkt } : undefined,
|
68
|
+
aud: account.aud,
|
73
69
|
|
70
|
+
...(parameters.dpop_jkt && {
|
71
|
+
cnf: { jkt: parameters.dpop_jkt },
|
72
|
+
}),
|
73
|
+
|
74
|
+
// Because tokens can end-up being quite big, we only include the scope in
|
75
|
+
// stateless mode.
|
74
76
|
...(this.accessTokenMode === AccessTokenMode.stateless && {
|
75
|
-
aud: account.aud,
|
76
77
|
scope,
|
77
|
-
// https://datatracker.ietf.org/doc/html/rfc8693#section-4.3
|
78
|
-
client_id: client.id,
|
79
78
|
}),
|
79
|
+
|
80
|
+
// https://datatracker.ietf.org/doc/html/rfc8693#section-4.3
|
81
|
+
client_id: client.id,
|
82
|
+
}
|
83
|
+
|
84
|
+
const claimsOverride = await callAsync(this.hooks.onCreateToken, {
|
85
|
+
client,
|
86
|
+
account,
|
87
|
+
parameters,
|
88
|
+
claims,
|
80
89
|
})
|
90
|
+
|
91
|
+
return this.signer.createAccessToken(claimsOverride ?? claims)
|
81
92
|
}
|
82
93
|
|
83
94
|
async createToken(
|
@@ -111,10 +122,10 @@ export class TokenManager {
|
|
111
122
|
throw err
|
112
123
|
})
|
113
124
|
|
114
|
-
const accessToken = await this.
|
125
|
+
const accessToken = await this.createAccessToken(
|
115
126
|
tokenId,
|
116
|
-
account,
|
117
127
|
client,
|
128
|
+
account,
|
118
129
|
parameters,
|
119
130
|
now,
|
120
131
|
expiresAt,
|
@@ -238,10 +249,10 @@ export class TokenManager {
|
|
238
249
|
scope,
|
239
250
|
})
|
240
251
|
|
241
|
-
const accessToken = await this.
|
252
|
+
const accessToken = await this.createAccessToken(
|
242
253
|
nextTokenId,
|
243
|
-
account,
|
244
254
|
client,
|
255
|
+
account,
|
245
256
|
parameters,
|
246
257
|
now,
|
247
258
|
expiresAt,
|
@@ -350,13 +361,16 @@ export class TokenManager {
|
|
350
361
|
return this.store.readToken(tokenId)
|
351
362
|
}
|
352
363
|
|
353
|
-
|
354
|
-
|
364
|
+
/**
|
365
|
+
* This method is called to when decoding a token that was encoded in
|
366
|
+
* {@link AccessTokenMode.light} mode, using data from the store to fill the
|
367
|
+
* data that was omitted in the token itself.
|
368
|
+
*/
|
369
|
+
async loadTokenClaims(
|
355
370
|
tokenType: OAuthTokenType,
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
): Promise<VerifyTokenClaimsResult> {
|
371
|
+
tokenPayload: AccessTokenPayload,
|
372
|
+
): Promise<TokenClaims> {
|
373
|
+
const tokenId = tokenPayload.jti
|
360
374
|
const tokenInfo = await this.getTokenInfo(tokenId).catch((err) => {
|
361
375
|
throw InvalidTokenError.from(err, tokenType)
|
362
376
|
})
|
@@ -365,40 +379,31 @@ export class TokenManager {
|
|
365
379
|
throw new InvalidTokenError(tokenType, `Invalid token`)
|
366
380
|
}
|
367
381
|
|
382
|
+
const { account, data } = tokenInfo
|
383
|
+
|
384
|
+
// Fool proof, make sure that the database & token payload are consistent.
|
385
|
+
// These should both be either undefined or a string so it's safe to compare
|
386
|
+
// the values directly.
|
387
|
+
if (tokenPayload.cnf?.jkt !== data.parameters.dpop_jkt) {
|
388
|
+
await this.deleteToken(tokenId)
|
389
|
+
throw new InvalidTokenError(tokenType, `Invalid token`)
|
390
|
+
}
|
391
|
+
|
368
392
|
if (isCurrentTokenExpired(tokenInfo)) {
|
369
393
|
await this.deleteToken(tokenId)
|
370
394
|
throw new InvalidTokenError(tokenType, `Token expired`)
|
371
395
|
}
|
372
396
|
|
373
|
-
|
374
|
-
const { parameters } = data
|
375
|
-
|
376
|
-
// Construct a list of claim, as if the token was a JWT.
|
377
|
-
const tokenClaims: SignedTokenPayload = {
|
378
|
-
iss: this.signer.issuer,
|
397
|
+
return {
|
379
398
|
jti: tokenId,
|
380
399
|
sub: account.sub,
|
381
|
-
exp: dateToEpoch(data.expiresAt),
|
382
400
|
iat: dateToEpoch(data.updatedAt),
|
383
|
-
|
384
|
-
|
385
|
-
// These are not stored in the JWT access token in "light" access token
|
386
|
-
// mode. See `buildAccessToken`.
|
401
|
+
exp: dateToEpoch(data.expiresAt),
|
387
402
|
aud: account.aud,
|
388
|
-
|
389
|
-
//
|
390
|
-
scope: data.scope ?? parameters.scope,
|
403
|
+
scope: data.scope ?? data.parameters.scope,
|
404
|
+
// https://datatracker.ietf.org/doc/html/rfc8693#section-4.3
|
391
405
|
client_id: data.clientId,
|
392
406
|
}
|
393
|
-
|
394
|
-
return verifyTokenClaims(
|
395
|
-
token,
|
396
|
-
tokenId,
|
397
|
-
tokenType,
|
398
|
-
tokenClaims,
|
399
|
-
dpopProof,
|
400
|
-
verifyOptions,
|
401
|
-
)
|
402
407
|
}
|
403
408
|
|
404
409
|
async listAccountTokens(sub: Sub): Promise<TokenInfo[]> {
|
@@ -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/lexicon/lexicon-data.ts","./src/lexicon/lexicon-getter.ts","./src/lexicon/lexicon-manager.ts","./src/lexicon/lexicon-store.ts","./src/lib/hcaptcha.ts","./src/lib/nsid.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/
|
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/lexicon/lexicon-data.ts","./src/lexicon/lexicon-getter.ts","./src/lexicon/lexicon-manager.ts","./src/lexicon/lexicon-store.ts","./src/lib/hcaptcha.ts","./src/lib/nsid.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/access-token-payload.ts","./src/signer/api-token-payload.ts","./src/signer/signer.ts","./src/token/refresh-token.ts","./src/token/token-claims.ts","./src/token/token-data.ts","./src/token/token-id.ts","./src/token/token-manager.ts","./src/token/token-store.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,20 +0,0 @@
|
|
1
|
-
import { OAuthAccessToken, OAuthTokenType } from '@atproto/oauth-types';
|
2
|
-
import { DpopProof } from '../oauth-verifier.js';
|
3
|
-
import { SignedTokenPayload } from '../signer/signed-token-payload.js';
|
4
|
-
import { TokenId } from './token-id.js';
|
5
|
-
export type { DpopProof, OAuthAccessToken, OAuthTokenType, SignedTokenPayload, TokenId, };
|
6
|
-
export type VerifyTokenClaimsOptions = {
|
7
|
-
/** One of these audience must be included in the token audience(s) */
|
8
|
-
audience?: [string, ...string[]];
|
9
|
-
/** One of these scope must be included in the token scope(s) */
|
10
|
-
scope?: [string, ...string[]];
|
11
|
-
};
|
12
|
-
export type VerifyTokenClaimsResult = {
|
13
|
-
token: OAuthAccessToken;
|
14
|
-
tokenId: TokenId;
|
15
|
-
tokenType: OAuthTokenType;
|
16
|
-
tokenClaims: SignedTokenPayload;
|
17
|
-
dpopProof: null | DpopProof;
|
18
|
-
};
|
19
|
-
export declare function verifyTokenClaims(token: OAuthAccessToken, tokenId: TokenId, tokenType: OAuthTokenType, tokenClaims: SignedTokenPayload, dpopProof: null | DpopProof, options?: VerifyTokenClaimsOptions): VerifyTokenClaimsResult;
|
20
|
-
//# sourceMappingURL=verify-token-claims.d.ts.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"verify-token-claims.d.ts","sourceRoot":"","sources":["../../src/token/verify-token-claims.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAKvE,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAA;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAKvC,YAAY,EACV,SAAS,EACT,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,OAAO,GACR,CAAA;AAED,MAAM,MAAM,wBAAwB,GAAG;IACrC,sEAAsE;IACtE,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAA;IAChC,gEAAgE;IAChE,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC,CAAA;CAC9B,CAAA;AAED,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,gBAAgB,CAAA;IACvB,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,cAAc,CAAA;IACzB,WAAW,EAAE,kBAAkB,CAAA;IAC/B,SAAS,EAAE,IAAI,GAAG,SAAS,CAAA;CAC5B,CAAA;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,gBAAgB,EACvB,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,cAAc,EACzB,WAAW,EAAE,kBAAkB,EAC/B,SAAS,EAAE,IAAI,GAAG,SAAS,EAC3B,OAAO,CAAC,EAAE,wBAAwB,GACjC,uBAAuB,CA0DzB"}
|
@@ -1,53 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
-
exports.verifyTokenClaims = verifyTokenClaims;
|
4
|
-
const invalid_dpop_key_binding_error_js_1 = require("../errors/invalid-dpop-key-binding-error.js");
|
5
|
-
const invalid_dpop_proof_error_js_1 = require("../errors/invalid-dpop-proof-error.js");
|
6
|
-
const cast_js_1 = require("../lib/util/cast.js");
|
7
|
-
const oauth_errors_js_1 = require("../oauth-errors.js");
|
8
|
-
const BEARER = 'Bearer';
|
9
|
-
const DPOP = 'DPoP';
|
10
|
-
function verifyTokenClaims(token, tokenId, tokenType, tokenClaims, dpopProof, options) {
|
11
|
-
const dateReference = Date.now();
|
12
|
-
if (tokenClaims.cnf?.jkt) {
|
13
|
-
// An access token with a cnf.jkt claim must be a DPoP token
|
14
|
-
if (tokenType !== DPOP) {
|
15
|
-
throw new oauth_errors_js_1.InvalidTokenError(DPOP, `Access token is bound to a DPoP proof, but token type is ${tokenType}`);
|
16
|
-
}
|
17
|
-
// DPoP token type must be used with a DPoP proof
|
18
|
-
if (!dpopProof) {
|
19
|
-
throw new invalid_dpop_proof_error_js_1.InvalidDpopProofError(`DPoP proof required`);
|
20
|
-
}
|
21
|
-
// DPoP proof must be signed with the key that matches the "cnf" claim
|
22
|
-
if (tokenClaims.cnf.jkt !== dpopProof.jkt) {
|
23
|
-
throw new invalid_dpop_key_binding_error_js_1.InvalidDpopKeyBindingError();
|
24
|
-
}
|
25
|
-
}
|
26
|
-
else {
|
27
|
-
// An access token without a cnf.jkt claim must be a Bearer token
|
28
|
-
if (tokenType !== BEARER) {
|
29
|
-
throw new oauth_errors_js_1.InvalidTokenError(BEARER, `Bearer token type must be used without a DPoP proof`);
|
30
|
-
}
|
31
|
-
// Unexpected DPoP proof received for a Bearer token
|
32
|
-
if (dpopProof) {
|
33
|
-
throw new oauth_errors_js_1.InvalidTokenError(BEARER, `DPoP proof not expected for Bearer token type`);
|
34
|
-
}
|
35
|
-
}
|
36
|
-
if (options?.audience) {
|
37
|
-
const aud = (0, cast_js_1.asArray)(tokenClaims.aud);
|
38
|
-
if (!options.audience.some((v) => aud.includes(v))) {
|
39
|
-
throw new oauth_errors_js_1.InvalidTokenError(tokenType, `Invalid audience`);
|
40
|
-
}
|
41
|
-
}
|
42
|
-
if (options?.scope) {
|
43
|
-
const scopes = tokenClaims.scope?.split(' ');
|
44
|
-
if (!scopes || !options.scope.some((v) => scopes.includes(v))) {
|
45
|
-
throw new oauth_errors_js_1.InvalidTokenError(tokenType, `Invalid scope`);
|
46
|
-
}
|
47
|
-
}
|
48
|
-
if (tokenClaims.exp != null && tokenClaims.exp * 1000 <= dateReference) {
|
49
|
-
throw new oauth_errors_js_1.InvalidTokenError(tokenType, `Token expired`);
|
50
|
-
}
|
51
|
-
return { token, tokenId, tokenType, tokenClaims, dpopProof };
|
52
|
-
}
|
53
|
-
//# sourceMappingURL=verify-token-claims.js.map
|
@@ -1 +0,0 @@
|
|
1
|
-
{"version":3,"file":"verify-token-claims.js","sourceRoot":"","sources":["../../src/token/verify-token-claims.ts"],"names":[],"mappings":";;AAmCA,8CAiEC;AAnGD,mGAAwF;AACxF,uFAA6E;AAC7E,iDAA6C;AAC7C,wDAAsD;AAKtD,MAAM,MAAM,GAAG,QAAiC,CAAA;AAChD,MAAM,IAAI,GAAG,MAA+B,CAAA;AAyB5C,SAAgB,iBAAiB,CAC/B,KAAuB,EACvB,OAAgB,EAChB,SAAyB,EACzB,WAA+B,EAC/B,SAA2B,EAC3B,OAAkC;IAElC,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IAEhC,IAAI,WAAW,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC;QACzB,4DAA4D;QAC5D,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,MAAM,IAAI,mCAAiB,CACzB,IAAI,EACJ,4DAA4D,SAAS,EAAE,CACxE,CAAA;QACH,CAAC;QAED,iDAAiD;QACjD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,mDAAqB,CAAC,qBAAqB,CAAC,CAAA;QACxD,CAAC;QAED,sEAAsE;QACtE,IAAI,WAAW,CAAC,GAAG,CAAC,GAAG,KAAK,SAAS,CAAC,GAAG,EAAE,CAAC;YAC1C,MAAM,IAAI,8DAA0B,EAAE,CAAA;QACxC,CAAC;IACH,CAAC;SAAM,CAAC;QACN,iEAAiE;QACjE,IAAI,SAAS,KAAK,MAAM,EAAE,CAAC;YACzB,MAAM,IAAI,mCAAiB,CACzB,MAAM,EACN,qDAAqD,CACtD,CAAA;QACH,CAAC;QAED,oDAAoD;QACpD,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,IAAI,mCAAiB,CACzB,MAAM,EACN,+CAA+C,CAChD,CAAA;QACH,CAAC;IACH,CAAC;IAED,IAAI,OAAO,EAAE,QAAQ,EAAE,CAAC;QACtB,MAAM,GAAG,GAAG,IAAA,iBAAO,EAAC,WAAW,CAAC,GAAG,CAAC,CAAA;QACpC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACnD,MAAM,IAAI,mCAAiB,CAAC,SAAS,EAAE,kBAAkB,CAAC,CAAA;QAC5D,CAAC;IACH,CAAC;IAED,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;QAC5C,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC9D,MAAM,IAAI,mCAAiB,CAAC,SAAS,EAAE,eAAe,CAAC,CAAA;QACzD,CAAC;IACH,CAAC;IAED,IAAI,WAAW,CAAC,GAAG,IAAI,IAAI,IAAI,WAAW,CAAC,GAAG,GAAG,IAAI,IAAI,aAAa,EAAE,CAAC;QACvE,MAAM,IAAI,mCAAiB,CAAC,SAAS,EAAE,eAAe,CAAC,CAAA;IACzD,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,CAAA;AAC9D,CAAC"}
|
@@ -1,101 +0,0 @@
|
|
1
|
-
import { OAuthAccessToken, OAuthTokenType } from '@atproto/oauth-types'
|
2
|
-
import { InvalidDpopKeyBindingError } from '../errors/invalid-dpop-key-binding-error.js'
|
3
|
-
import { InvalidDpopProofError } from '../errors/invalid-dpop-proof-error.js'
|
4
|
-
import { asArray } from '../lib/util/cast.js'
|
5
|
-
import { InvalidTokenError } from '../oauth-errors.js'
|
6
|
-
import { DpopProof } from '../oauth-verifier.js'
|
7
|
-
import { SignedTokenPayload } from '../signer/signed-token-payload.js'
|
8
|
-
import { TokenId } from './token-id.js'
|
9
|
-
|
10
|
-
const BEARER = 'Bearer' satisfies OAuthTokenType
|
11
|
-
const DPOP = 'DPoP' satisfies OAuthTokenType
|
12
|
-
|
13
|
-
export type {
|
14
|
-
DpopProof,
|
15
|
-
OAuthAccessToken,
|
16
|
-
OAuthTokenType,
|
17
|
-
SignedTokenPayload,
|
18
|
-
TokenId,
|
19
|
-
}
|
20
|
-
|
21
|
-
export type VerifyTokenClaimsOptions = {
|
22
|
-
/** One of these audience must be included in the token audience(s) */
|
23
|
-
audience?: [string, ...string[]]
|
24
|
-
/** One of these scope must be included in the token scope(s) */
|
25
|
-
scope?: [string, ...string[]]
|
26
|
-
}
|
27
|
-
|
28
|
-
export type VerifyTokenClaimsResult = {
|
29
|
-
token: OAuthAccessToken
|
30
|
-
tokenId: TokenId
|
31
|
-
tokenType: OAuthTokenType
|
32
|
-
tokenClaims: SignedTokenPayload
|
33
|
-
dpopProof: null | DpopProof
|
34
|
-
}
|
35
|
-
|
36
|
-
export function verifyTokenClaims(
|
37
|
-
token: OAuthAccessToken,
|
38
|
-
tokenId: TokenId,
|
39
|
-
tokenType: OAuthTokenType,
|
40
|
-
tokenClaims: SignedTokenPayload,
|
41
|
-
dpopProof: null | DpopProof,
|
42
|
-
options?: VerifyTokenClaimsOptions,
|
43
|
-
): VerifyTokenClaimsResult {
|
44
|
-
const dateReference = Date.now()
|
45
|
-
|
46
|
-
if (tokenClaims.cnf?.jkt) {
|
47
|
-
// An access token with a cnf.jkt claim must be a DPoP token
|
48
|
-
if (tokenType !== DPOP) {
|
49
|
-
throw new InvalidTokenError(
|
50
|
-
DPOP,
|
51
|
-
`Access token is bound to a DPoP proof, but token type is ${tokenType}`,
|
52
|
-
)
|
53
|
-
}
|
54
|
-
|
55
|
-
// DPoP token type must be used with a DPoP proof
|
56
|
-
if (!dpopProof) {
|
57
|
-
throw new InvalidDpopProofError(`DPoP proof required`)
|
58
|
-
}
|
59
|
-
|
60
|
-
// DPoP proof must be signed with the key that matches the "cnf" claim
|
61
|
-
if (tokenClaims.cnf.jkt !== dpopProof.jkt) {
|
62
|
-
throw new InvalidDpopKeyBindingError()
|
63
|
-
}
|
64
|
-
} else {
|
65
|
-
// An access token without a cnf.jkt claim must be a Bearer token
|
66
|
-
if (tokenType !== BEARER) {
|
67
|
-
throw new InvalidTokenError(
|
68
|
-
BEARER,
|
69
|
-
`Bearer token type must be used without a DPoP proof`,
|
70
|
-
)
|
71
|
-
}
|
72
|
-
|
73
|
-
// Unexpected DPoP proof received for a Bearer token
|
74
|
-
if (dpopProof) {
|
75
|
-
throw new InvalidTokenError(
|
76
|
-
BEARER,
|
77
|
-
`DPoP proof not expected for Bearer token type`,
|
78
|
-
)
|
79
|
-
}
|
80
|
-
}
|
81
|
-
|
82
|
-
if (options?.audience) {
|
83
|
-
const aud = asArray(tokenClaims.aud)
|
84
|
-
if (!options.audience.some((v) => aud.includes(v))) {
|
85
|
-
throw new InvalidTokenError(tokenType, `Invalid audience`)
|
86
|
-
}
|
87
|
-
}
|
88
|
-
|
89
|
-
if (options?.scope) {
|
90
|
-
const scopes = tokenClaims.scope?.split(' ')
|
91
|
-
if (!scopes || !options.scope.some((v) => scopes.includes(v))) {
|
92
|
-
throw new InvalidTokenError(tokenType, `Invalid scope`)
|
93
|
-
}
|
94
|
-
}
|
95
|
-
|
96
|
-
if (tokenClaims.exp != null && tokenClaims.exp * 1000 <= dateReference) {
|
97
|
-
throw new InvalidTokenError(tokenType, `Token expired`)
|
98
|
-
}
|
99
|
-
|
100
|
-
return { token, tokenId, tokenType, tokenClaims, dpopProof }
|
101
|
-
}
|