@atproto/oauth-provider 0.13.5 → 0.14.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 +26 -0
- package/dist/lexicon/lexicon-data.d.ts +3 -2
- package/dist/lexicon/lexicon-data.d.ts.map +1 -1
- package/dist/lexicon/lexicon-data.js.map +1 -1
- package/dist/lexicon/lexicon-getter.d.ts +2 -2
- package/dist/lexicon/lexicon-getter.d.ts.map +1 -1
- package/dist/lexicon/lexicon-getter.js +4 -7
- package/dist/lexicon/lexicon-getter.js.map +1 -1
- package/dist/lexicon/lexicon-manager.d.ts +36 -21
- package/dist/lexicon/lexicon-manager.d.ts.map +1 -1
- package/dist/lexicon/lexicon-manager.js +9 -6
- package/dist/lexicon/lexicon-manager.js.map +1 -1
- package/dist/lexicon/lexicon-store.d.ts +2 -3
- package/dist/lexicon/lexicon-store.d.ts.map +1 -1
- package/dist/lexicon/lexicon-store.js.map +1 -1
- package/dist/oauth-provider.d.ts +10 -23
- package/dist/oauth-provider.d.ts.map +1 -1
- package/dist/oauth-provider.js +6 -3
- package/dist/oauth-provider.js.map +1 -1
- package/dist/request/request-manager.d.ts.map +1 -1
- package/dist/request/request-manager.js +2 -2
- package/dist/request/request-manager.js.map +1 -1
- package/dist/result/authorization-result-authorize-page.d.ts +2 -2
- package/dist/result/authorization-result-authorize-page.d.ts.map +1 -1
- package/dist/result/authorization-result-authorize-page.js.map +1 -1
- package/dist/token/token-manager.js +2 -2
- package/dist/token/token-manager.js.map +1 -1
- package/package.json +11 -11
- package/src/lexicon/lexicon-data.ts +4 -2
- package/src/lexicon/lexicon-getter.ts +4 -11
- package/src/lexicon/lexicon-manager.ts +15 -15
- package/src/lexicon/lexicon-store.ts +2 -3
- package/src/oauth-provider.ts +9 -10
- package/src/request/request-manager.ts +2 -2
- package/src/result/authorization-result-authorize-page.ts +2 -2
- package/src/token/token-manager.ts +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"token-manager.js","sourceRoot":"","sources":["../../src/token/token-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { SignedJwt, isSignedJwt } from '@atproto/jwk'\nimport { LexiconResolutionError } from '@atproto/lexicon-resolver'\nimport type { Account } from '@atproto/oauth-provider-api'\nimport {\n OAuthAccessToken,\n OAuthAuthorizationRequestParameters,\n OAuthScope,\n OAuthTokenResponse,\n OAuthTokenType,\n} from '@atproto/oauth-types'\nimport { AccessTokenMode } from '../access-token/access-token-mode.js'\nimport { ClientAuth } from '../client/client-auth.js'\nimport { Client } from '../client/client.js'\nimport { TOKEN_MAX_AGE } from '../constants.js'\nimport { DeviceId } from '../device/device-id.js'\nimport { InvalidGrantError } from '../errors/invalid-grant-error.js'\nimport { InvalidRequestError } from '../errors/invalid-request-error.js'\nimport { InvalidTokenError } from '../errors/invalid-token-error.js'\nimport { LexiconManager } from '../lexicon/lexicon-manager.js'\nimport { RequestMetadata } from '../lib/http/request.js'\nimport { dateToEpoch, dateToRelativeSeconds } from '../lib/util/date.js'\nimport { OAuthHooks } from '../oauth-hooks.js'\nimport { Sub } from '../oidc/sub.js'\nimport { Code, isCode } from '../request/code.js'\nimport { AccessTokenPayload } from '../signer/access-token-payload.js'\nimport { Signer } from '../signer/signer.js'\nimport {\n RefreshToken,\n generateRefreshToken,\n isRefreshToken,\n} from './refresh-token.js'\nimport { TokenClaims } from './token-claims.js'\nimport { TokenId, generateTokenId, isTokenId } from './token-id.js'\nimport { CreateTokenData, TokenInfo, TokenStore } from './token-store.js'\n\nexport { AccessTokenMode, Signer }\nexport type { OAuthHooks, TokenStore }\n\nexport class TokenManager {\n constructor(\n protected readonly store: TokenStore,\n protected readonly lexiconManager: LexiconManager,\n protected readonly signer: Signer,\n protected readonly hooks: OAuthHooks,\n protected readonly accessTokenMode: AccessTokenMode,\n protected readonly tokenMaxAge = TOKEN_MAX_AGE,\n ) {}\n\n protected createTokenExpiry(now = new Date()) {\n return new Date(now.getTime() + this.tokenMaxAge)\n }\n\n protected async createAccessToken(\n tokenId: TokenId,\n client: Client,\n account: Account,\n parameters: OAuthAuthorizationRequestParameters,\n issuedAt: Date,\n expiresAt: Date,\n scope: OAuthScope,\n ): Promise<OAuthAccessToken> {\n const claims: TokenClaims = {\n jti: tokenId,\n sub: account.sub,\n iat: dateToEpoch(issuedAt),\n exp: dateToEpoch(expiresAt),\n aud: account.aud,\n\n ...(parameters.dpop_jkt && {\n cnf: { jkt: parameters.dpop_jkt },\n }),\n\n // Because tokens can end-up being quite big, we only include the scope in\n // stateless mode.\n ...(this.accessTokenMode === AccessTokenMode.stateless && {\n scope,\n }),\n\n // https://datatracker.ietf.org/doc/html/rfc8693#section-4.3\n client_id: client.id,\n }\n\n const claimsOverride = await this.hooks.onCreateToken?.call(null, {\n client,\n account,\n parameters,\n claims,\n })\n\n return this.signer.createAccessToken(claimsOverride ?? claims)\n }\n\n async createToken(\n client: Client,\n clientAuth: ClientAuth,\n clientMetadata: RequestMetadata,\n account: Account,\n deviceId: null | DeviceId,\n parameters: OAuthAuthorizationRequestParameters,\n code: Code,\n ): Promise<OAuthTokenResponse> {\n await this.validateTokenParams(client, clientAuth, parameters)\n\n const tokenId = await generateTokenId()\n const refreshToken = client.metadata.grant_types.includes('refresh_token')\n ? await generateRefreshToken()\n : undefined\n\n const now = new Date()\n const expiresAt = this.createTokenExpiry(now)\n\n const scope = await this.lexiconManager\n .buildTokenScope(parameters.scope!)\n .catch((err) => {\n // Parse expected errors\n if (err instanceof LexiconResolutionError) {\n throw new InvalidRequestError(err.message, err)\n }\n\n // Unexpected error\n throw err\n })\n\n const accessToken = await this.createAccessToken(\n tokenId,\n client,\n account,\n parameters,\n now,\n expiresAt,\n scope,\n )\n\n const response = this.buildTokenResponse(\n inferTokenType(parameters),\n accessToken,\n refreshToken,\n expiresAt,\n account.sub,\n scope,\n )\n\n const tokenData: CreateTokenData = {\n createdAt: now,\n updatedAt: now,\n expiresAt,\n clientId: client.id,\n clientAuth,\n deviceId,\n sub: account.sub,\n parameters,\n details: null,\n scope,\n code,\n }\n\n await this.store.createToken(tokenId, tokenData, refreshToken)\n\n try {\n await this.hooks.onTokenCreated?.call(null, {\n client,\n clientAuth,\n clientMetadata,\n account,\n parameters,\n })\n\n return response\n } catch (err) {\n // If the hook fails, we delete the token to avoid leaving a dangling\n // token in the store.\n await this.deleteToken(tokenId)\n throw err\n }\n }\n\n protected async validateTokenParams(\n client: Client,\n clientAuth: ClientAuth,\n parameters: OAuthAuthorizationRequestParameters,\n ): Promise<void> {\n if (client.metadata.dpop_bound_access_tokens && !parameters.dpop_jkt) {\n throw new InvalidGrantError(\n `DPoP JKT is required for DPoP bound access tokens`,\n )\n }\n }\n\n protected buildTokenResponse(\n tokenType: OAuthTokenType,\n accessToken: OAuthAccessToken,\n refreshToken: string | undefined,\n expiresAt: Date,\n sub: Sub,\n scope: string,\n ): OAuthTokenResponse {\n return {\n access_token: accessToken,\n token_type: tokenType,\n refresh_token: refreshToken,\n scope,\n\n // @NOTE using a getter so that the value gets computed when the JSON\n // response is generated, allowing to value to be as accurate as possible.\n get expires_in() {\n return dateToRelativeSeconds(expiresAt)\n },\n\n // ATPROTO extension: add the sub claim to the token response to allow\n // clients to resolve the PDS url (audience) using the did resolution\n // mechanism.\n sub,\n }\n }\n\n async rotateToken(\n client: Client,\n clientAuth: ClientAuth,\n clientMetadata: RequestMetadata,\n tokenInfo: TokenInfo,\n ): Promise<OAuthTokenResponse> {\n const { account, data } = tokenInfo\n const { parameters } = data\n\n await this.validateTokenParams(client, clientAuth, parameters)\n\n const nextTokenId = await generateTokenId()\n const nextRefreshToken = await generateRefreshToken()\n\n const now = new Date()\n const expiresAt = this.createTokenExpiry(now)\n\n // @NOTE since the permission sets are stored in a persistent store,\n // it's fine to propagate a 500 (server_error) here as the values should\n // be retrievable from the store.\n const scope = await this.lexiconManager.buildTokenScope(parameters.scope!)\n\n await this.store.rotateToken(tokenInfo.id, nextTokenId, nextRefreshToken, {\n updatedAt: now,\n expiresAt,\n // @NOTE Normally, the clientAuth not change over time. There are two\n // exceptions:\n // - Upgrade from a legacy representation of client authentication to\n // a modern one.\n // - Allow clients to become \"confidential\" if they were previously\n // \"public\"\n clientAuth,\n scope,\n })\n\n const accessToken = await this.createAccessToken(\n nextTokenId,\n client,\n account,\n parameters,\n now,\n expiresAt,\n scope,\n )\n\n const response = this.buildTokenResponse(\n inferTokenType(parameters),\n accessToken,\n nextRefreshToken,\n expiresAt,\n account.sub,\n scope,\n )\n\n await this.hooks.onTokenRefreshed?.call(null, {\n client,\n clientAuth,\n clientMetadata,\n account,\n parameters,\n })\n\n return response\n }\n\n /**\n * @note The token validity is not guaranteed. The caller must ensure that the\n * token is valid before using the returned token info.\n */\n public async findToken(token: string): Promise<null | TokenInfo> {\n if (isTokenId(token)) {\n return this.getTokenInfo(token)\n } else if (isCode(token)) {\n return this.findByCode(token)\n } else if (isRefreshToken(token)) {\n return this.findByRefreshToken(token)\n } else if (isSignedJwt(token)) {\n return this.findByAccessToken(token)\n } else {\n throw new InvalidRequestError(`Invalid token`)\n }\n }\n\n public async findByAccessToken(token: SignedJwt): Promise<null | TokenInfo> {\n const { payload } = await this.signer.verifyAccessToken(token, {\n clockTolerance: Infinity,\n })\n\n const tokenInfo = await this.getTokenInfo(payload.jti)\n if (!tokenInfo) return null\n\n // Fool-proof: Invalid store implementation ?\n if (payload.sub !== tokenInfo.account.sub) {\n await this.deleteToken(tokenInfo.id)\n throw new Error(\n `Account sub (${tokenInfo.account.sub}) does not match token sub (${payload.sub})`,\n )\n }\n\n return tokenInfo\n }\n\n protected async findByRefreshToken(\n token: RefreshToken,\n ): Promise<null | TokenInfo> {\n return this.store.findTokenByRefreshToken(token)\n }\n\n public async consumeRefreshToken(token: RefreshToken): Promise<TokenInfo> {\n // @NOTE concurrent refreshes of the same refresh token could theoretically\n // lead to two new tokens (access & refresh) being created. This is deemed\n // acceptable for now (as the mechanism can only be used once since only one\n // of the two refresh token created will be valid, and any future refresh\n // attempts from outdated tokens will cause the entire session to be\n // invalidated). Ideally, the store should be able to handle this case by\n // atomically consuming the refresh token and returning the token info.\n\n // @TODO Add another store method that atomically consumes the refresh token\n // with a lock.\n const tokenInfo = await this.findByRefreshToken(token).catch((err) => {\n throw InvalidGrantError.from(err, `Invalid refresh token`)\n })\n\n if (!tokenInfo) {\n throw new InvalidGrantError(`Invalid refresh token`)\n }\n\n if (tokenInfo.currentRefreshToken !== token) {\n await this.deleteToken(tokenInfo.id)\n throw new InvalidGrantError(`Refresh token replayed`)\n }\n\n return tokenInfo\n }\n\n public async findByCode(code: Code): Promise<null | TokenInfo> {\n return this.store.findTokenByCode(code)\n }\n\n public async deleteToken(tokenId: TokenId): Promise<void> {\n return this.store.deleteToken(tokenId)\n }\n\n async getTokenInfo(tokenId: TokenId): Promise<null | TokenInfo> {\n return this.store.readToken(tokenId)\n }\n\n /**\n * This method is called to when decoding a token that was encoded in\n * {@link AccessTokenMode.light} mode, using data from the store to fill the\n * data that was omitted in the token itself.\n */\n async loadTokenClaims(\n tokenType: OAuthTokenType,\n tokenPayload: AccessTokenPayload,\n ): Promise<TokenClaims> {\n const tokenId = tokenPayload.jti\n const tokenInfo = await this.getTokenInfo(tokenId).catch((err) => {\n throw InvalidTokenError.from(err, tokenType)\n })\n\n if (!tokenInfo) {\n throw new InvalidTokenError(tokenType, `Invalid token`)\n }\n\n const { account, data } = tokenInfo\n\n // Fool proof, make sure that the database & token payload are consistent.\n // These should both be either undefined or a string so it's safe to compare\n // the values directly.\n if (tokenPayload.cnf?.jkt !== data.parameters.dpop_jkt) {\n await this.deleteToken(tokenId)\n throw new InvalidTokenError(tokenType, `Invalid token`)\n }\n\n if (isCurrentTokenExpired(tokenInfo)) {\n await this.deleteToken(tokenId)\n throw new InvalidTokenError(tokenType, `Token expired`)\n }\n\n return {\n jti: tokenId,\n sub: account.sub,\n iat: dateToEpoch(data.updatedAt),\n exp: dateToEpoch(data.expiresAt),\n aud: account.aud,\n scope: data.scope ?? data.parameters.scope,\n // https://datatracker.ietf.org/doc/html/rfc8693#section-4.3\n client_id: data.clientId,\n }\n }\n\n async listAccountTokens(sub: Sub): Promise<TokenInfo[]> {\n const results = await this.store.listAccountTokens(sub)\n return results\n .filter((tokenInfo) => tokenInfo.account.sub === sub) // Fool proof\n .filter((tokenInfo) => !isCurrentTokenExpired(tokenInfo))\n }\n}\n\nfunction isCurrentTokenExpired(tokenInfo: TokenInfo): boolean {\n return tokenInfo.data.expiresAt.getTime() < Date.now()\n}\n\nfunction inferTokenType(\n parameters: OAuthAuthorizationRequestParameters,\n): OAuthTokenType {\n if (parameters.dpop_jkt) {\n return 'DPoP'\n }\n return 'Bearer'\n}\n"]}
|
|
1
|
+
{"version":3,"file":"token-manager.js","sourceRoot":"","sources":["../../src/token/token-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { SignedJwt, isSignedJwt } from '@atproto/jwk'\nimport { LexResolverError } from '@atproto/lex-resolver'\nimport type { Account } from '@atproto/oauth-provider-api'\nimport {\n OAuthAccessToken,\n OAuthAuthorizationRequestParameters,\n OAuthScope,\n OAuthTokenResponse,\n OAuthTokenType,\n} from '@atproto/oauth-types'\nimport { AccessTokenMode } from '../access-token/access-token-mode.js'\nimport { ClientAuth } from '../client/client-auth.js'\nimport { Client } from '../client/client.js'\nimport { TOKEN_MAX_AGE } from '../constants.js'\nimport { DeviceId } from '../device/device-id.js'\nimport { InvalidGrantError } from '../errors/invalid-grant-error.js'\nimport { InvalidRequestError } from '../errors/invalid-request-error.js'\nimport { InvalidTokenError } from '../errors/invalid-token-error.js'\nimport { LexiconManager } from '../lexicon/lexicon-manager.js'\nimport { RequestMetadata } from '../lib/http/request.js'\nimport { dateToEpoch, dateToRelativeSeconds } from '../lib/util/date.js'\nimport { OAuthHooks } from '../oauth-hooks.js'\nimport { Sub } from '../oidc/sub.js'\nimport { Code, isCode } from '../request/code.js'\nimport { AccessTokenPayload } from '../signer/access-token-payload.js'\nimport { Signer } from '../signer/signer.js'\nimport {\n RefreshToken,\n generateRefreshToken,\n isRefreshToken,\n} from './refresh-token.js'\nimport { TokenClaims } from './token-claims.js'\nimport { TokenId, generateTokenId, isTokenId } from './token-id.js'\nimport { CreateTokenData, TokenInfo, TokenStore } from './token-store.js'\n\nexport { AccessTokenMode, Signer }\nexport type { OAuthHooks, TokenStore }\n\nexport class TokenManager {\n constructor(\n protected readonly store: TokenStore,\n protected readonly lexiconManager: LexiconManager,\n protected readonly signer: Signer,\n protected readonly hooks: OAuthHooks,\n protected readonly accessTokenMode: AccessTokenMode,\n protected readonly tokenMaxAge = TOKEN_MAX_AGE,\n ) {}\n\n protected createTokenExpiry(now = new Date()) {\n return new Date(now.getTime() + this.tokenMaxAge)\n }\n\n protected async createAccessToken(\n tokenId: TokenId,\n client: Client,\n account: Account,\n parameters: OAuthAuthorizationRequestParameters,\n issuedAt: Date,\n expiresAt: Date,\n scope: OAuthScope,\n ): Promise<OAuthAccessToken> {\n const claims: TokenClaims = {\n jti: tokenId,\n sub: account.sub,\n iat: dateToEpoch(issuedAt),\n exp: dateToEpoch(expiresAt),\n aud: account.aud,\n\n ...(parameters.dpop_jkt && {\n cnf: { jkt: parameters.dpop_jkt },\n }),\n\n // Because tokens can end-up being quite big, we only include the scope in\n // stateless mode.\n ...(this.accessTokenMode === AccessTokenMode.stateless && {\n scope,\n }),\n\n // https://datatracker.ietf.org/doc/html/rfc8693#section-4.3\n client_id: client.id,\n }\n\n const claimsOverride = await this.hooks.onCreateToken?.call(null, {\n client,\n account,\n parameters,\n claims,\n })\n\n return this.signer.createAccessToken(claimsOverride ?? claims)\n }\n\n async createToken(\n client: Client,\n clientAuth: ClientAuth,\n clientMetadata: RequestMetadata,\n account: Account,\n deviceId: null | DeviceId,\n parameters: OAuthAuthorizationRequestParameters,\n code: Code,\n ): Promise<OAuthTokenResponse> {\n await this.validateTokenParams(client, clientAuth, parameters)\n\n const tokenId = await generateTokenId()\n const refreshToken = client.metadata.grant_types.includes('refresh_token')\n ? await generateRefreshToken()\n : undefined\n\n const now = new Date()\n const expiresAt = this.createTokenExpiry(now)\n\n const scope = await this.lexiconManager\n .buildTokenScope(parameters.scope!)\n .catch((err) => {\n // Parse expected errors\n if (err instanceof LexResolverError) {\n throw new InvalidRequestError(err.message, err)\n }\n\n // Unexpected error\n throw err\n })\n\n const accessToken = await this.createAccessToken(\n tokenId,\n client,\n account,\n parameters,\n now,\n expiresAt,\n scope,\n )\n\n const response = this.buildTokenResponse(\n inferTokenType(parameters),\n accessToken,\n refreshToken,\n expiresAt,\n account.sub,\n scope,\n )\n\n const tokenData: CreateTokenData = {\n createdAt: now,\n updatedAt: now,\n expiresAt,\n clientId: client.id,\n clientAuth,\n deviceId,\n sub: account.sub,\n parameters,\n details: null,\n scope,\n code,\n }\n\n await this.store.createToken(tokenId, tokenData, refreshToken)\n\n try {\n await this.hooks.onTokenCreated?.call(null, {\n client,\n clientAuth,\n clientMetadata,\n account,\n parameters,\n })\n\n return response\n } catch (err) {\n // If the hook fails, we delete the token to avoid leaving a dangling\n // token in the store.\n await this.deleteToken(tokenId)\n throw err\n }\n }\n\n protected async validateTokenParams(\n client: Client,\n clientAuth: ClientAuth,\n parameters: OAuthAuthorizationRequestParameters,\n ): Promise<void> {\n if (client.metadata.dpop_bound_access_tokens && !parameters.dpop_jkt) {\n throw new InvalidGrantError(\n `DPoP JKT is required for DPoP bound access tokens`,\n )\n }\n }\n\n protected buildTokenResponse(\n tokenType: OAuthTokenType,\n accessToken: OAuthAccessToken,\n refreshToken: string | undefined,\n expiresAt: Date,\n sub: Sub,\n scope: string,\n ): OAuthTokenResponse {\n return {\n access_token: accessToken,\n token_type: tokenType,\n refresh_token: refreshToken,\n scope,\n\n // @NOTE using a getter so that the value gets computed when the JSON\n // response is generated, allowing to value to be as accurate as possible.\n get expires_in() {\n return dateToRelativeSeconds(expiresAt)\n },\n\n // ATPROTO extension: add the sub claim to the token response to allow\n // clients to resolve the PDS url (audience) using the did resolution\n // mechanism.\n sub,\n }\n }\n\n async rotateToken(\n client: Client,\n clientAuth: ClientAuth,\n clientMetadata: RequestMetadata,\n tokenInfo: TokenInfo,\n ): Promise<OAuthTokenResponse> {\n const { account, data } = tokenInfo\n const { parameters } = data\n\n await this.validateTokenParams(client, clientAuth, parameters)\n\n const nextTokenId = await generateTokenId()\n const nextRefreshToken = await generateRefreshToken()\n\n const now = new Date()\n const expiresAt = this.createTokenExpiry(now)\n\n // @NOTE since the permission sets are stored in a persistent store,\n // it's fine to propagate a 500 (server_error) here as the values should\n // be retrievable from the store.\n const scope = await this.lexiconManager.buildTokenScope(parameters.scope!)\n\n await this.store.rotateToken(tokenInfo.id, nextTokenId, nextRefreshToken, {\n updatedAt: now,\n expiresAt,\n // @NOTE Normally, the clientAuth not change over time. There are two\n // exceptions:\n // - Upgrade from a legacy representation of client authentication to\n // a modern one.\n // - Allow clients to become \"confidential\" if they were previously\n // \"public\"\n clientAuth,\n scope,\n })\n\n const accessToken = await this.createAccessToken(\n nextTokenId,\n client,\n account,\n parameters,\n now,\n expiresAt,\n scope,\n )\n\n const response = this.buildTokenResponse(\n inferTokenType(parameters),\n accessToken,\n nextRefreshToken,\n expiresAt,\n account.sub,\n scope,\n )\n\n await this.hooks.onTokenRefreshed?.call(null, {\n client,\n clientAuth,\n clientMetadata,\n account,\n parameters,\n })\n\n return response\n }\n\n /**\n * @note The token validity is not guaranteed. The caller must ensure that the\n * token is valid before using the returned token info.\n */\n public async findToken(token: string): Promise<null | TokenInfo> {\n if (isTokenId(token)) {\n return this.getTokenInfo(token)\n } else if (isCode(token)) {\n return this.findByCode(token)\n } else if (isRefreshToken(token)) {\n return this.findByRefreshToken(token)\n } else if (isSignedJwt(token)) {\n return this.findByAccessToken(token)\n } else {\n throw new InvalidRequestError(`Invalid token`)\n }\n }\n\n public async findByAccessToken(token: SignedJwt): Promise<null | TokenInfo> {\n const { payload } = await this.signer.verifyAccessToken(token, {\n clockTolerance: Infinity,\n })\n\n const tokenInfo = await this.getTokenInfo(payload.jti)\n if (!tokenInfo) return null\n\n // Fool-proof: Invalid store implementation ?\n if (payload.sub !== tokenInfo.account.sub) {\n await this.deleteToken(tokenInfo.id)\n throw new Error(\n `Account sub (${tokenInfo.account.sub}) does not match token sub (${payload.sub})`,\n )\n }\n\n return tokenInfo\n }\n\n protected async findByRefreshToken(\n token: RefreshToken,\n ): Promise<null | TokenInfo> {\n return this.store.findTokenByRefreshToken(token)\n }\n\n public async consumeRefreshToken(token: RefreshToken): Promise<TokenInfo> {\n // @NOTE concurrent refreshes of the same refresh token could theoretically\n // lead to two new tokens (access & refresh) being created. This is deemed\n // acceptable for now (as the mechanism can only be used once since only one\n // of the two refresh token created will be valid, and any future refresh\n // attempts from outdated tokens will cause the entire session to be\n // invalidated). Ideally, the store should be able to handle this case by\n // atomically consuming the refresh token and returning the token info.\n\n // @TODO Add another store method that atomically consumes the refresh token\n // with a lock.\n const tokenInfo = await this.findByRefreshToken(token).catch((err) => {\n throw InvalidGrantError.from(err, `Invalid refresh token`)\n })\n\n if (!tokenInfo) {\n throw new InvalidGrantError(`Invalid refresh token`)\n }\n\n if (tokenInfo.currentRefreshToken !== token) {\n await this.deleteToken(tokenInfo.id)\n throw new InvalidGrantError(`Refresh token replayed`)\n }\n\n return tokenInfo\n }\n\n public async findByCode(code: Code): Promise<null | TokenInfo> {\n return this.store.findTokenByCode(code)\n }\n\n public async deleteToken(tokenId: TokenId): Promise<void> {\n return this.store.deleteToken(tokenId)\n }\n\n async getTokenInfo(tokenId: TokenId): Promise<null | TokenInfo> {\n return this.store.readToken(tokenId)\n }\n\n /**\n * This method is called to when decoding a token that was encoded in\n * {@link AccessTokenMode.light} mode, using data from the store to fill the\n * data that was omitted in the token itself.\n */\n async loadTokenClaims(\n tokenType: OAuthTokenType,\n tokenPayload: AccessTokenPayload,\n ): Promise<TokenClaims> {\n const tokenId = tokenPayload.jti\n const tokenInfo = await this.getTokenInfo(tokenId).catch((err) => {\n throw InvalidTokenError.from(err, tokenType)\n })\n\n if (!tokenInfo) {\n throw new InvalidTokenError(tokenType, `Invalid token`)\n }\n\n const { account, data } = tokenInfo\n\n // Fool proof, make sure that the database & token payload are consistent.\n // These should both be either undefined or a string so it's safe to compare\n // the values directly.\n if (tokenPayload.cnf?.jkt !== data.parameters.dpop_jkt) {\n await this.deleteToken(tokenId)\n throw new InvalidTokenError(tokenType, `Invalid token`)\n }\n\n if (isCurrentTokenExpired(tokenInfo)) {\n await this.deleteToken(tokenId)\n throw new InvalidTokenError(tokenType, `Token expired`)\n }\n\n return {\n jti: tokenId,\n sub: account.sub,\n iat: dateToEpoch(data.updatedAt),\n exp: dateToEpoch(data.expiresAt),\n aud: account.aud,\n scope: data.scope ?? data.parameters.scope,\n // https://datatracker.ietf.org/doc/html/rfc8693#section-4.3\n client_id: data.clientId,\n }\n }\n\n async listAccountTokens(sub: Sub): Promise<TokenInfo[]> {\n const results = await this.store.listAccountTokens(sub)\n return results\n .filter((tokenInfo) => tokenInfo.account.sub === sub) // Fool proof\n .filter((tokenInfo) => !isCurrentTokenExpired(tokenInfo))\n }\n}\n\nfunction isCurrentTokenExpired(tokenInfo: TokenInfo): boolean {\n return tokenInfo.data.expiresAt.getTime() < Date.now()\n}\n\nfunction inferTokenType(\n parameters: OAuthAuthorizationRequestParameters,\n): OAuthTokenType {\n if (parameters.dpop_jkt) {\n return 'DPoP'\n }\n return 'Bearer'\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atproto/oauth-provider",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Generic OAuth2 and OpenID Connect provider for Node.js. Currently only supports features needed for Atproto.",
|
|
6
6
|
"keywords": [
|
|
@@ -47,18 +47,18 @@
|
|
|
47
47
|
"@atproto-labs/pipe": "0.1.1",
|
|
48
48
|
"@atproto-labs/simple-store": "0.3.0",
|
|
49
49
|
"@atproto-labs/simple-store-memory": "0.1.4",
|
|
50
|
-
"@atproto/common": "^0.5.
|
|
51
|
-
"@atproto/did": "0.2.
|
|
50
|
+
"@atproto/common": "^0.5.2",
|
|
51
|
+
"@atproto/did": "0.2.3",
|
|
52
52
|
"@atproto/jwk": "0.6.0",
|
|
53
53
|
"@atproto/jwk-jose": "0.1.11",
|
|
54
|
-
"@atproto/
|
|
55
|
-
"@atproto/
|
|
56
|
-
"@atproto/oauth-
|
|
57
|
-
"@atproto/oauth-provider-
|
|
58
|
-
"@atproto/oauth-
|
|
59
|
-
"@atproto/oauth-provider-
|
|
60
|
-
"@atproto/
|
|
61
|
-
"@atproto/
|
|
54
|
+
"@atproto/lex-document": "0.0.4",
|
|
55
|
+
"@atproto/lex-resolver": "0.0.4",
|
|
56
|
+
"@atproto/oauth-types": "0.5.2",
|
|
57
|
+
"@atproto/oauth-provider-api": "0.3.4",
|
|
58
|
+
"@atproto/oauth-provider-frontend": "0.2.5",
|
|
59
|
+
"@atproto/oauth-provider-ui": "0.3.6",
|
|
60
|
+
"@atproto/oauth-scopes": "0.3.0",
|
|
61
|
+
"@atproto/syntax": "0.4.2"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
64
|
"@types/cookie": "^0.6.0",
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LexiconDocument } from '@atproto/lex-document'
|
|
2
|
+
|
|
3
|
+
export type { LexiconDocument }
|
|
2
4
|
|
|
3
5
|
export type LexiconData = {
|
|
4
6
|
createdAt: Date
|
|
5
7
|
updatedAt: Date
|
|
6
8
|
lastSucceededAt: null | Date
|
|
7
9
|
uri: null | string
|
|
8
|
-
lexicon: null |
|
|
10
|
+
lexicon: null | LexiconDocument
|
|
9
11
|
}
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
LexiconResolutionError,
|
|
3
|
-
LexiconResolver,
|
|
4
|
-
resolveLexicon,
|
|
5
|
-
} from '@atproto/lexicon-resolver'
|
|
1
|
+
import { LexResolver, LexResolverError } from '@atproto/lex-resolver'
|
|
6
2
|
import { Nsid } from '@atproto/oauth-scopes'
|
|
7
3
|
import { CachedGetter } from '@atproto-labs/simple-store'
|
|
8
4
|
import { LEXICON_REFRESH_FREQUENCY } from '../constants.js'
|
|
@@ -16,19 +12,16 @@ import { LexiconData, LexiconStore } from './lexicon-store.js'
|
|
|
16
12
|
* @private
|
|
17
13
|
*/
|
|
18
14
|
export class LexiconGetter extends CachedGetter<Nsid, LexiconData> {
|
|
19
|
-
constructor(store: LexiconStore,
|
|
15
|
+
constructor(store: LexiconStore, lexResolver: LexResolver) {
|
|
20
16
|
super(
|
|
21
17
|
async (input, options, storedData) => {
|
|
22
18
|
const now = new Date()
|
|
23
|
-
|
|
24
|
-
// to be fresh, which is not possible yet with the current interface
|
|
25
|
-
// of LexiconResolver.
|
|
26
|
-
const result = await resolver(input).catch((err) => {
|
|
19
|
+
const result = await lexResolver.get(input, options).catch((err) => {
|
|
27
20
|
// We swallow LexiconResolutionError errors, returning potentially
|
|
28
21
|
// "null" values here to avoid hammering the resolver with requests
|
|
29
22
|
// for the same lexicon that is known to be unavailable. The getter
|
|
30
23
|
// should be called again based on the isStale() function below.
|
|
31
|
-
if (err instanceof
|
|
24
|
+
if (err instanceof LexResolverError) return undefined
|
|
32
25
|
|
|
33
26
|
// Unexpected error are propagated
|
|
34
27
|
throw err
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
LexiconResolutionError,
|
|
4
|
-
LexiconResolver,
|
|
5
|
-
} from '@atproto/lexicon-resolver'
|
|
1
|
+
import { LexiconPermissionSet } from '@atproto/lex-document'
|
|
2
|
+
import { LexResolver, LexResolverError } from '@atproto/lex-resolver'
|
|
6
3
|
import { IncludeScope, Nsid } from '@atproto/oauth-scopes'
|
|
7
4
|
import { LexiconGetter } from './lexicon-getter.js'
|
|
8
5
|
import { LexiconStore } from './lexicon-store.js'
|
|
@@ -12,8 +9,8 @@ export * from './lexicon-store.js'
|
|
|
12
9
|
export class LexiconManager {
|
|
13
10
|
protected readonly lexiconGetter: LexiconGetter
|
|
14
11
|
|
|
15
|
-
constructor(store: LexiconStore,
|
|
16
|
-
this.lexiconGetter = new LexiconGetter(store,
|
|
12
|
+
constructor(store: LexiconStore, lexResolver: LexResolver) {
|
|
13
|
+
this.lexiconGetter = new LexiconGetter(store, lexResolver)
|
|
17
14
|
}
|
|
18
15
|
|
|
19
16
|
public async getPermissionSetsFromScope(scope?: string) {
|
|
@@ -50,28 +47,28 @@ export class LexiconManager {
|
|
|
50
47
|
}
|
|
51
48
|
|
|
52
49
|
protected async getPermissionSets(nsids: Set<Nsid>) {
|
|
53
|
-
return new Map<string,
|
|
50
|
+
return new Map<string, LexiconPermissionSet>(
|
|
54
51
|
await Promise.all(Array.from(nsids, this.getPermissionSetEntry, this)),
|
|
55
52
|
)
|
|
56
53
|
}
|
|
57
54
|
|
|
58
55
|
protected async getPermissionSetEntry(
|
|
59
56
|
nsid: Nsid,
|
|
60
|
-
): Promise<[nsid: Nsid, permissionSet:
|
|
57
|
+
): Promise<[nsid: Nsid, permissionSet: LexiconPermissionSet]> {
|
|
61
58
|
const permissionSet = await this.getPermissionSet(nsid)
|
|
62
59
|
return [nsid, permissionSet]
|
|
63
60
|
}
|
|
64
61
|
|
|
65
|
-
protected async getPermissionSet(nsid: Nsid): Promise<
|
|
62
|
+
protected async getPermissionSet(nsid: Nsid): Promise<LexiconPermissionSet> {
|
|
66
63
|
const { lexicon } = await this.lexiconGetter.get(nsid)
|
|
67
64
|
|
|
68
65
|
if (!lexicon) {
|
|
69
|
-
throw
|
|
66
|
+
throw LexResolverError.from(nsid)
|
|
70
67
|
}
|
|
71
68
|
|
|
72
69
|
if (lexicon.defs.main?.type !== 'permission-set') {
|
|
73
70
|
const description = 'Lexicon document is not a permission set'
|
|
74
|
-
throw
|
|
71
|
+
throw LexResolverError.from(nsid, description)
|
|
75
72
|
}
|
|
76
73
|
|
|
77
74
|
return lexicon.defs.main
|
|
@@ -108,9 +105,12 @@ function extractNsid(nsidScope: IncludeScope): Nsid {
|
|
|
108
105
|
}
|
|
109
106
|
|
|
110
107
|
export function nsidToPermissionScopes(
|
|
111
|
-
this: Map<string,
|
|
108
|
+
this: Map<string, LexiconPermissionSet>,
|
|
112
109
|
includeScope: IncludeScope,
|
|
113
110
|
): string[] {
|
|
114
|
-
const permissionSet = this.get(includeScope.nsid)
|
|
115
|
-
return includeScope.
|
|
111
|
+
const permissionSet = this.get(includeScope.nsid)
|
|
112
|
+
if (permissionSet) return includeScope.toScopes(permissionSet)
|
|
113
|
+
|
|
114
|
+
// Should never happen (mostly there for type safety & future proofing)
|
|
115
|
+
throw new Error(`Missing permission set for NSID: ${includeScope.nsid}`)
|
|
116
116
|
}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { LexiconDoc } from '@atproto/lexicon'
|
|
2
1
|
import { Awaitable, buildInterfaceChecker } from '../lib/util/type.js'
|
|
3
|
-
import { LexiconData } from './lexicon-data.js'
|
|
2
|
+
import { LexiconData, LexiconDocument } from './lexicon-data.js'
|
|
4
3
|
|
|
5
|
-
export type { Awaitable, LexiconData,
|
|
4
|
+
export type { Awaitable, LexiconData, LexiconDocument }
|
|
6
5
|
|
|
7
6
|
export interface LexiconStore {
|
|
8
7
|
findLexicon(nsid: string): Awaitable<LexiconData | null>
|
package/src/oauth-provider.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto'
|
|
2
2
|
import type { Redis, RedisOptions } from 'ioredis'
|
|
3
3
|
import { Jwks, Keyset } from '@atproto/jwk'
|
|
4
|
-
import {
|
|
4
|
+
import { LexResolver } from '@atproto/lex-resolver'
|
|
5
5
|
import type { Account } from '@atproto/oauth-provider-api'
|
|
6
6
|
import {
|
|
7
7
|
CLIENT_ASSERTION_TYPE_JWT_BEARER,
|
|
@@ -106,7 +106,7 @@ import {
|
|
|
106
106
|
} from './token/token-store.js'
|
|
107
107
|
import { isPARResponseError } from './types/par-response-error.js'
|
|
108
108
|
|
|
109
|
-
export { AccessTokenMode, Keyset }
|
|
109
|
+
export { AccessTokenMode, Keyset, LexResolver }
|
|
110
110
|
export type {
|
|
111
111
|
AccessTokenPayload,
|
|
112
112
|
AuthorizationRedirectParameters,
|
|
@@ -119,7 +119,6 @@ export type {
|
|
|
119
119
|
CustomizationInput,
|
|
120
120
|
ErrorHandler,
|
|
121
121
|
HcaptchaConfig,
|
|
122
|
-
LexiconResolver,
|
|
123
122
|
MultiLangString,
|
|
124
123
|
OAuthAuthorizationServerMetadata,
|
|
125
124
|
VerifyTokenPayloadOptions,
|
|
@@ -158,6 +157,11 @@ type OAuthProviderConfig = {
|
|
|
158
157
|
*/
|
|
159
158
|
metadata?: CustomMetadata
|
|
160
159
|
|
|
160
|
+
/**
|
|
161
|
+
* A Lexicon resolver instance to use for fetching lexicon schemas.
|
|
162
|
+
*/
|
|
163
|
+
lexResolver?: LexResolver
|
|
164
|
+
|
|
161
165
|
/**
|
|
162
166
|
* A custom fetch function that can be used to fetch the client metadata from
|
|
163
167
|
* the internet. By default, the fetch function is a safeFetchWrap() function
|
|
@@ -167,11 +171,6 @@ type OAuthProviderConfig = {
|
|
|
167
171
|
*/
|
|
168
172
|
safeFetch?: typeof globalThis.fetch
|
|
169
173
|
|
|
170
|
-
/**
|
|
171
|
-
* A custom ATProto lexicon resolver
|
|
172
|
-
*/
|
|
173
|
-
lexiconResolver?: LexiconResolver
|
|
174
|
-
|
|
175
174
|
/**
|
|
176
175
|
* A redis instance to use for replay protection. If not provided, replay
|
|
177
176
|
* protection will use memory storage.
|
|
@@ -260,9 +259,9 @@ export class OAuthProvider extends OAuthVerifier {
|
|
|
260
259
|
|
|
261
260
|
metadata,
|
|
262
261
|
|
|
263
|
-
lexiconResolver,
|
|
264
262
|
safeFetch = safeFetchWrap(),
|
|
265
263
|
store, // compound store implementation
|
|
264
|
+
lexResolver = new LexResolver({ fetch: safeFetch }),
|
|
266
265
|
|
|
267
266
|
// Required stores
|
|
268
267
|
accountStore = asAccountStore(store),
|
|
@@ -326,7 +325,7 @@ export class OAuthProvider extends OAuthVerifier {
|
|
|
326
325
|
clientJwksCache,
|
|
327
326
|
clientMetadataCache,
|
|
328
327
|
)
|
|
329
|
-
this.lexiconManager = new LexiconManager(lexiconStore,
|
|
328
|
+
this.lexiconManager = new LexiconManager(lexiconStore, lexResolver)
|
|
330
329
|
this.requestManager = new RequestManager(
|
|
331
330
|
requestStore,
|
|
332
331
|
this.lexiconManager,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { isAtprotoDid } from '@atproto/did'
|
|
2
|
-
import {
|
|
2
|
+
import { LexResolverError } from '@atproto/lex-resolver'
|
|
3
3
|
import type { Account } from '@atproto/oauth-provider-api'
|
|
4
4
|
import { isAtprotoOauthScope } from '@atproto/oauth-scopes'
|
|
5
5
|
import {
|
|
@@ -296,7 +296,7 @@ export class RequestManager {
|
|
|
296
296
|
await this.lexiconManager.getPermissionSetsFromScope(parameters.scope)
|
|
297
297
|
} catch (err) {
|
|
298
298
|
// Parse expected errors
|
|
299
|
-
if (err instanceof
|
|
299
|
+
if (err instanceof LexResolverError) {
|
|
300
300
|
throw new AuthorizationError(
|
|
301
301
|
parameters,
|
|
302
302
|
err.message,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LexiconPermissionSet } from '@atproto/lex-document'
|
|
2
2
|
import type { Session } from '@atproto/oauth-provider-api'
|
|
3
3
|
import type { OAuthAuthorizationRequestParameters } from '@atproto/oauth-types'
|
|
4
4
|
import type { Client } from '../client/client.js'
|
|
@@ -8,7 +8,7 @@ export type AuthorizationResultAuthorizePage = {
|
|
|
8
8
|
issuer: string
|
|
9
9
|
client: Client
|
|
10
10
|
parameters: OAuthAuthorizationRequestParameters
|
|
11
|
-
permissionSets: Map<string,
|
|
11
|
+
permissionSets: Map<string, LexiconPermissionSet>
|
|
12
12
|
|
|
13
13
|
requestUri: RequestUri
|
|
14
14
|
sessions: readonly Session[]
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SignedJwt, isSignedJwt } from '@atproto/jwk'
|
|
2
|
-
import {
|
|
2
|
+
import { LexResolverError } from '@atproto/lex-resolver'
|
|
3
3
|
import type { Account } from '@atproto/oauth-provider-api'
|
|
4
4
|
import {
|
|
5
5
|
OAuthAccessToken,
|
|
@@ -113,7 +113,7 @@ export class TokenManager {
|
|
|
113
113
|
.buildTokenScope(parameters.scope!)
|
|
114
114
|
.catch((err) => {
|
|
115
115
|
// Parse expected errors
|
|
116
|
-
if (err instanceof
|
|
116
|
+
if (err instanceof LexResolverError) {
|
|
117
117
|
throw new InvalidRequestError(err.message, err)
|
|
118
118
|
}
|
|
119
119
|
|