@atproto/oauth-provider 0.18.4 → 0.19.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.
Files changed (91) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/account/account-manager.d.ts +11 -7
  3. package/dist/account/account-manager.d.ts.map +1 -1
  4. package/dist/account/account-manager.js +82 -19
  5. package/dist/account/account-manager.js.map +1 -1
  6. package/dist/account/account-store.d.ts +47 -13
  7. package/dist/account/account-store.d.ts.map +1 -1
  8. package/dist/account/account-store.js +4 -1
  9. package/dist/account/account-store.js.map +1 -1
  10. package/dist/account/sign-up-input.d.ts +2 -2
  11. package/dist/errors/invalid-credentials-error.d.ts +9 -9
  12. package/dist/errors/invalid-credentials-error.d.ts.map +1 -1
  13. package/dist/errors/invalid-credentials-error.js +8 -8
  14. package/dist/errors/invalid-credentials-error.js.map +1 -1
  15. package/dist/lib/util/function.js +1 -1
  16. package/dist/lib/util/function.js.map +1 -1
  17. package/dist/oauth-hooks.d.ts +77 -4
  18. package/dist/oauth-hooks.d.ts.map +1 -1
  19. package/dist/oauth-hooks.js.map +1 -1
  20. package/dist/oauth-provider.d.ts.map +1 -1
  21. package/dist/oauth-provider.js +15 -9
  22. package/dist/oauth-provider.js.map +1 -1
  23. package/dist/request/request-data.d.ts +4 -4
  24. package/dist/request/request-data.d.ts.map +1 -1
  25. package/dist/request/request-data.js +1 -1
  26. package/dist/request/request-data.js.map +1 -1
  27. package/dist/request/request-manager.d.ts.map +1 -1
  28. package/dist/request/request-manager.js +7 -4
  29. package/dist/request/request-manager.js.map +1 -1
  30. package/dist/request/request-store.d.ts +1 -1
  31. package/dist/request/request-store.js.map +1 -1
  32. package/dist/result/authorization-result-authorize-page.d.ts +1 -1
  33. package/dist/result/authorization-result-authorize-page.js.map +1 -1
  34. package/dist/router/assets/send-authorization-page.js +1 -1
  35. package/dist/router/assets/send-authorization-page.js.map +1 -1
  36. package/dist/router/create-api-middleware.d.ts.map +1 -1
  37. package/dist/router/create-api-middleware.js +87 -51
  38. package/dist/router/create-api-middleware.js.map +1 -1
  39. package/dist/signer/access-token-payload.d.ts +15 -15
  40. package/dist/signer/access-token-payload.js +2 -2
  41. package/dist/signer/access-token-payload.js.map +1 -1
  42. package/dist/signer/api-token-payload.d.ts +15 -15
  43. package/dist/signer/api-token-payload.js +2 -2
  44. package/dist/signer/api-token-payload.js.map +1 -1
  45. package/dist/signer/signer.d.ts +6 -187
  46. package/dist/signer/signer.d.ts.map +1 -1
  47. package/dist/signer/signer.js.map +1 -1
  48. package/dist/token/token-claims.d.ts +2 -1
  49. package/dist/token/token-claims.d.ts.map +1 -1
  50. package/dist/token/token-claims.js.map +1 -1
  51. package/dist/token/token-data.d.ts +3 -3
  52. package/dist/token/token-data.d.ts.map +1 -1
  53. package/dist/token/token-data.js.map +1 -1
  54. package/dist/token/token-manager.d.ts +3 -3
  55. package/dist/token/token-manager.d.ts.map +1 -1
  56. package/dist/token/token-manager.js +14 -14
  57. package/dist/token/token-manager.js.map +1 -1
  58. package/dist/token/token-store.d.ts +3 -3
  59. package/dist/token/token-store.d.ts.map +1 -1
  60. package/dist/token/token-store.js.map +1 -1
  61. package/dist/types/handle.d.ts +5 -1
  62. package/dist/types/handle.d.ts.map +1 -1
  63. package/dist/types/handle.js +9 -8
  64. package/dist/types/handle.js.map +1 -1
  65. package/package.json +8 -8
  66. package/src/account/account-manager.ts +123 -20
  67. package/src/account/account-store.ts +59 -11
  68. package/src/errors/invalid-credentials-error.ts +8 -8
  69. package/src/lib/util/function.ts +2 -2
  70. package/src/oauth-hooks.ts +85 -3
  71. package/src/oauth-provider.ts +17 -9
  72. package/src/request/request-data.ts +5 -5
  73. package/src/request/request-manager.ts +11 -4
  74. package/src/request/request-store.ts +1 -1
  75. package/src/result/authorization-result-authorize-page.ts +1 -1
  76. package/src/router/assets/send-authorization-page.ts +1 -1
  77. package/src/router/create-api-middleware.ts +128 -67
  78. package/src/signer/access-token-payload.ts +2 -2
  79. package/src/signer/api-token-payload.ts +2 -2
  80. package/src/signer/signer.ts +13 -4
  81. package/src/token/token-claims.ts +2 -1
  82. package/src/token/token-data.ts +3 -3
  83. package/src/token/token-manager.ts +15 -15
  84. package/src/token/token-store.ts +3 -3
  85. package/src/types/handle.ts +21 -16
  86. package/tsconfig.build.tsbuildinfo +1 -1
  87. package/dist/oidc/sub.d.ts +0 -4
  88. package/dist/oidc/sub.d.ts.map +0 -1
  89. package/dist/oidc/sub.js +0 -3
  90. package/dist/oidc/sub.js.map +0 -1
  91. package/src/oidc/sub.ts +0 -4
@@ -1,3 +1,4 @@
1
+ import type { Did } from '@atproto/did';
1
2
  import { SignedJwt } from '@atproto/jwk';
2
3
  import type { Account } from '@atproto/oauth-provider-api';
3
4
  import { OAuthAccessToken, OAuthAuthorizationRequestParameters, OAuthScope, OAuthTokenResponse, OAuthTokenType } from '@atproto/oauth-types';
@@ -8,7 +9,6 @@ import { DeviceId } from '../device/device-id.js';
8
9
  import { LexiconManager } from '../lexicon/lexicon-manager.js';
9
10
  import { RequestMetadata } from '../lib/http/request.js';
10
11
  import { OAuthHooks } from '../oauth-hooks.js';
11
- import { Sub } from '../oidc/sub.js';
12
12
  import { Code } from '../request/code.js';
13
13
  import { AccessTokenPayload } from '../signer/access-token-payload.js';
14
14
  import { Signer } from '../signer/signer.js';
@@ -30,7 +30,7 @@ export declare class TokenManager {
30
30
  protected createAccessToken(tokenId: TokenId, client: Client, account: Account, parameters: OAuthAuthorizationRequestParameters, issuedAt: Date, expiresAt: Date, scope: OAuthScope): Promise<OAuthAccessToken>;
31
31
  createToken(client: Client, clientAuth: ClientAuth, clientMetadata: RequestMetadata, account: Account, deviceId: null | DeviceId, parameters: OAuthAuthorizationRequestParameters, code: Code): Promise<OAuthTokenResponse>;
32
32
  protected validateTokenParams(client: Client, clientAuth: ClientAuth, parameters: OAuthAuthorizationRequestParameters): Promise<void>;
33
- protected buildTokenResponse(tokenType: OAuthTokenType, accessToken: OAuthAccessToken, refreshToken: string | undefined, expiresAt: Date, sub: Sub, scope: string): OAuthTokenResponse;
33
+ protected buildTokenResponse(tokenType: OAuthTokenType, accessToken: OAuthAccessToken, refreshToken: string | undefined, expiresAt: Date, did: Did, scope: string): OAuthTokenResponse;
34
34
  rotateToken(client: Client, clientAuth: ClientAuth, clientMetadata: RequestMetadata, tokenInfo: TokenInfo): Promise<OAuthTokenResponse>;
35
35
  /**
36
36
  * @note The token validity is not guaranteed. The caller must ensure that the
@@ -49,6 +49,6 @@ export declare class TokenManager {
49
49
  * data that was omitted in the token itself.
50
50
  */
51
51
  loadTokenClaims(tokenType: OAuthTokenType, tokenPayload: AccessTokenPayload): Promise<TokenClaims>;
52
- listAccountTokens(sub: Sub): Promise<TokenInfo[]>;
52
+ listAccountTokens(did: Did): Promise<TokenInfo[]>;
53
53
  }
54
54
  //# sourceMappingURL=token-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"token-manager.d.ts","sourceRoot":"","sources":["../../src/token/token-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"token-manager.d.ts","sourceRoot":"","sources":["../../src/token/token-manager.ts"],"names":[],"mappings":"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"}
@@ -26,10 +26,10 @@ export class TokenManager {
26
26
  async createAccessToken(tokenId, client, account, parameters, issuedAt, expiresAt, scope) {
27
27
  const claims = {
28
28
  jti: tokenId,
29
- sub: account.sub,
29
+ sub: account.did,
30
30
  iat: dateToEpoch(issuedAt),
31
31
  exp: dateToEpoch(expiresAt),
32
- aud: account.aud,
32
+ aud: account.pds,
33
33
  ...(parameters.dpop_jkt && {
34
34
  cnf: { jkt: parameters.dpop_jkt },
35
35
  }),
@@ -68,7 +68,7 @@ export class TokenManager {
68
68
  throw err;
69
69
  });
70
70
  const accessToken = await this.createAccessToken(tokenId, client, account, parameters, now, expiresAt, scope);
71
- const response = this.buildTokenResponse(inferTokenType(parameters), accessToken, refreshToken, expiresAt, account.sub, scope);
71
+ const response = this.buildTokenResponse(inferTokenType(parameters), accessToken, refreshToken, expiresAt, account.did, scope);
72
72
  const tokenData = {
73
73
  createdAt: now,
74
74
  updatedAt: now,
@@ -76,7 +76,7 @@ export class TokenManager {
76
76
  clientId: client.id,
77
77
  clientAuth,
78
78
  deviceId,
79
- sub: account.sub,
79
+ did: account.did,
80
80
  parameters,
81
81
  details: null,
82
82
  scope,
@@ -105,7 +105,7 @@ export class TokenManager {
105
105
  throw new InvalidGrantError(`DPoP JKT is required for DPoP bound access tokens`);
106
106
  }
107
107
  }
108
- buildTokenResponse(tokenType, accessToken, refreshToken, expiresAt, sub, scope) {
108
+ buildTokenResponse(tokenType, accessToken, refreshToken, expiresAt, did, scope) {
109
109
  return {
110
110
  access_token: accessToken,
111
111
  token_type: tokenType,
@@ -119,7 +119,7 @@ export class TokenManager {
119
119
  // ATPROTO extension: add the sub claim to the token response to allow
120
120
  // clients to resolve the PDS url (audience) using the did resolution
121
121
  // mechanism.
122
- sub,
122
+ sub: did,
123
123
  };
124
124
  }
125
125
  async rotateToken(client, clientAuth, clientMetadata, tokenInfo) {
@@ -147,7 +147,7 @@ export class TokenManager {
147
147
  scope,
148
148
  });
149
149
  const accessToken = await this.createAccessToken(nextTokenId, client, account, parameters, now, expiresAt, scope);
150
- const response = this.buildTokenResponse(inferTokenType(parameters), accessToken, nextRefreshToken, expiresAt, account.sub, scope);
150
+ const response = this.buildTokenResponse(inferTokenType(parameters), accessToken, nextRefreshToken, expiresAt, account.did, scope);
151
151
  await this.hooks.onTokenRefreshed?.call(null, {
152
152
  client,
153
153
  clientAuth,
@@ -186,9 +186,9 @@ export class TokenManager {
186
186
  if (!tokenInfo)
187
187
  return null;
188
188
  // Fool-proof: Invalid store implementation ?
189
- if (payload.sub !== tokenInfo.account.sub) {
189
+ if (payload.sub !== tokenInfo.account.did) {
190
190
  await this.deleteToken(tokenInfo.id);
191
- throw new Error(`Account sub (${tokenInfo.account.sub}) does not match token sub (${payload.sub})`);
191
+ throw new Error(`Account sub (${tokenInfo.account.did}) does not match token sub (${payload.sub})`);
192
192
  }
193
193
  return tokenInfo;
194
194
  }
@@ -253,19 +253,19 @@ export class TokenManager {
253
253
  }
254
254
  return {
255
255
  jti: tokenId,
256
- sub: account.sub,
256
+ sub: account.did,
257
257
  iat: dateToEpoch(data.updatedAt),
258
258
  exp: dateToEpoch(data.expiresAt),
259
- aud: account.aud,
259
+ aud: account.pds,
260
260
  scope: data.scope ?? data.parameters.scope,
261
261
  // https://datatracker.ietf.org/doc/html/rfc8693#section-4.3
262
262
  client_id: data.clientId,
263
263
  };
264
264
  }
265
- async listAccountTokens(sub) {
266
- const results = await this.store.listAccountTokens(sub);
265
+ async listAccountTokens(did) {
266
+ const results = await this.store.listAccountTokens(did);
267
267
  return results
268
- .filter((tokenInfo) => tokenInfo.account.sub === sub) // Fool proof
268
+ .filter((tokenInfo) => tokenInfo.account.did === did) // Fool proof
269
269
  .filter((tokenInfo) => !isCurrentTokenExpired(tokenInfo));
270
270
  }
271
271
  }
@@ -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 { 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"]}
1
+ {"version":3,"file":"token-manager.js","sourceRoot":"","sources":["../../src/token/token-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Did } from '@atproto/did'\nimport { 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 { 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.did,\n iat: dateToEpoch(issuedAt),\n exp: dateToEpoch(expiresAt),\n aud: account.pds,\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.did,\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 did: account.did,\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 did: Did,\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: did,\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.did,\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.did) {\n await this.deleteToken(tokenInfo.id)\n throw new Error(\n `Account sub (${tokenInfo.account.did}) 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.did,\n iat: dateToEpoch(data.updatedAt),\n exp: dateToEpoch(data.expiresAt),\n aud: account.pds,\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(did: Did): Promise<TokenInfo[]> {\n const results = await this.store.listAccountTokens(did)\n return results\n .filter((tokenInfo) => tokenInfo.account.did === did) // 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,6 +1,6 @@
1
+ import type { Did } from '@atproto/did';
1
2
  import type { Account } from '@atproto/oauth-provider-api';
2
3
  import { Awaitable } from '../lib/util/type.js';
3
- import { Sub } from '../oidc/sub.js';
4
4
  import { Code } from '../request/code.js';
5
5
  import { RefreshToken } from './refresh-token.js';
6
6
  import { TokenData } from './token-data.js';
@@ -8,7 +8,7 @@ import { TokenId } from './token-id.js';
8
8
  export * from './refresh-token.js';
9
9
  export * from './token-data.js';
10
10
  export * from './token-id.js';
11
- export type { Account, Awaitable, Sub };
11
+ export type { Account, Awaitable, Did };
12
12
  export type TokenInfo = {
13
13
  id: TokenId;
14
14
  data: TokenData;
@@ -42,7 +42,7 @@ export interface TokenStore {
42
42
  */
43
43
  findTokenByRefreshToken(refreshToken: RefreshToken): Awaitable<null | TokenInfo>;
44
44
  findTokenByCode(code: Code): Awaitable<null | TokenInfo>;
45
- listAccountTokens(sub: Sub): Awaitable<TokenInfo[]>;
45
+ listAccountTokens(did: Did): Awaitable<TokenInfo[]>;
46
46
  }
47
47
  export declare const isTokenStore: <V extends Partial<TokenStore>>(value: V) => value is V & import("../lib/util/type.js").RequiredDefined<TokenStore>;
48
48
  export declare function asTokenStore<V extends Partial<TokenStore>>(implementation?: V): V & TokenStore;
@@ -1 +1 @@
1
- {"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/token/token-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAA;AAC1D,OAAO,EAAE,SAAS,EAAyB,MAAM,qBAAqB,CAAA;AACtE,OAAO,EAAE,GAAG,EAAE,MAAM,gBAAgB,CAAA;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAGvC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,CAAA;AAEvC,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,OAAO,CAAA;IACX,IAAI,EAAE,SAAS,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,mBAAmB,EAAE,IAAI,GAAG,YAAY,CAAA;CACzC,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;IACnC,SAAS,EAAE,SAAS,CAAC,WAAW,CAAC,CAAA;IACjC,SAAS,EAAE,SAAS,CAAC,WAAW,CAAC,CAAA;IACjC,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG;IACxC,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;CACvC,CAAA;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,WAAW,CACT,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,eAAe,EACrB,YAAY,CAAC,EAAE,YAAY,GAC1B,SAAS,CAAC,IAAI,CAAC,CAAA;IAElB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC,IAAI,GAAG,SAAS,CAAC,CAAA;IAExD,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAE9C,WAAW,CACT,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,OAAO,EACnB,eAAe,EAAE,YAAY,EAC7B,OAAO,EAAE,YAAY,GACpB,SAAS,CAAC,IAAI,CAAC,CAAA;IAElB;;;;OAIG;IACH,uBAAuB,CACrB,YAAY,EAAE,YAAY,GACzB,SAAS,CAAC,IAAI,GAAG,SAAS,CAAC,CAAA;IAE9B,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC,IAAI,GAAG,SAAS,CAAC,CAAA;IAExD,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,SAAS,CAAC,SAAS,EAAE,CAAC,CAAA;CACpD;AAED,eAAO,MAAM,YAAY,qHAQvB,CAAA;AAEF,wBAAgB,YAAY,CAAC,CAAC,SAAS,OAAO,CAAC,UAAU,CAAC,EACxD,cAAc,CAAC,EAAE,CAAC,GACjB,CAAC,GAAG,UAAU,CAKhB"}
1
+ {"version":3,"file":"token-store.d.ts","sourceRoot":"","sources":["../../src/token/token-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,cAAc,CAAA;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAA;AAC1D,OAAO,EAAE,SAAS,EAAyB,MAAM,qBAAqB,CAAA;AACtE,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAGvC,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AAC7B,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,CAAA;AAEvC,MAAM,MAAM,SAAS,GAAG;IACtB,EAAE,EAAE,OAAO,CAAA;IACX,IAAI,EAAE,SAAS,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,mBAAmB,EAAE,IAAI,GAAG,YAAY,CAAA;CACzC,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,SAAS,CAAC,YAAY,CAAC,CAAA;IACnC,SAAS,EAAE,SAAS,CAAC,WAAW,CAAC,CAAA;IACjC,SAAS,EAAE,SAAS,CAAC,WAAW,CAAC,CAAA;IACjC,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;CACvC,CAAA;AAED,MAAM,MAAM,eAAe,GAAG,SAAS,GAAG;IACxC,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAA;CACvC,CAAA;AAED;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,WAAW,CACT,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,eAAe,EACrB,YAAY,CAAC,EAAE,YAAY,GAC1B,SAAS,CAAC,IAAI,CAAC,CAAA;IAElB,SAAS,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC,IAAI,GAAG,SAAS,CAAC,CAAA;IAExD,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;IAE9C,WAAW,CACT,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,OAAO,EACnB,eAAe,EAAE,YAAY,EAC7B,OAAO,EAAE,YAAY,GACpB,SAAS,CAAC,IAAI,CAAC,CAAA;IAElB;;;;OAIG;IACH,uBAAuB,CACrB,YAAY,EAAE,YAAY,GACzB,SAAS,CAAC,IAAI,GAAG,SAAS,CAAC,CAAA;IAE9B,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC,IAAI,GAAG,SAAS,CAAC,CAAA;IAExD,iBAAiB,CAAC,GAAG,EAAE,GAAG,GAAG,SAAS,CAAC,SAAS,EAAE,CAAC,CAAA;CACpD;AAED,eAAO,MAAM,YAAY,qHAQvB,CAAA;AAEF,wBAAgB,YAAY,CAAC,CAAC,SAAS,OAAO,CAAC,UAAU,CAAC,EACxD,cAAc,CAAC,EAAE,CAAC,GACjB,CAAC,GAAG,UAAU,CAKhB"}
@@ -1 +1 @@
1
- {"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../src/token/token-store.ts"],"names":[],"mappings":"AACA,OAAO,EAAa,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAOtE,gEAAgE;AAChE,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AA2D7B,MAAM,CAAC,MAAM,YAAY,GAAG,qBAAqB,CAAa;IAC5D,aAAa;IACb,aAAa;IACb,iBAAiB;IACjB,yBAAyB;IACzB,mBAAmB;IACnB,WAAW;IACX,aAAa;CACd,CAAC,CAAA;AAEF,MAAM,UAAU,YAAY,CAC1B,cAAkB;IAElB,IAAI,CAAC,cAAc,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;IACtD,CAAC;IACD,OAAO,cAAc,CAAA;AACvB,CAAC","sourcesContent":["import type { Account } from '@atproto/oauth-provider-api'\nimport { Awaitable, buildInterfaceChecker } from '../lib/util/type.js'\nimport { Sub } from '../oidc/sub.js'\nimport { Code } from '../request/code.js'\nimport { RefreshToken } from './refresh-token.js'\nimport { TokenData } from './token-data.js'\nimport { TokenId } from './token-id.js'\n\n// Export all types needed to implement the TokenStore interface\nexport * from './refresh-token.js'\nexport * from './token-data.js'\nexport * from './token-id.js'\nexport type { Account, Awaitable, Sub }\n\nexport type TokenInfo = {\n id: TokenId\n data: TokenData\n account: Account\n currentRefreshToken: null | RefreshToken\n}\n\nexport type NewTokenData = {\n clientAuth: TokenData['clientAuth']\n expiresAt: TokenData['expiresAt']\n updatedAt: TokenData['updatedAt']\n scope: NonNullable<TokenData['scope']>\n}\n\nexport type CreateTokenData = TokenData & {\n scope: NonNullable<TokenData['scope']>\n}\n\n/**\n * @param data historically, {@link TokenData.scope} was not present in\n * {@link TokenData}, causing it to be \"nullable\" when returned from\n * {@link TokenStore.readToken}. We use {@link CreateTokenData} here to allow\n * the store implementation to expect its presence.\n */\nexport interface TokenStore {\n createToken(\n tokenId: TokenId,\n data: CreateTokenData,\n refreshToken?: RefreshToken,\n ): Awaitable<void>\n\n readToken(tokenId: TokenId): Awaitable<null | TokenInfo>\n\n deleteToken(tokenId: TokenId): Awaitable<void>\n\n rotateToken(\n tokenId: TokenId,\n newTokenId: TokenId,\n newRefreshToken: RefreshToken,\n newData: NewTokenData,\n ): Awaitable<void>\n\n /**\n * Find a token by its refresh token. Note that previous refresh tokens\n * should also return the token. The data model is responsible for storing\n * old refresh tokens when a new one is issued.\n */\n findTokenByRefreshToken(\n refreshToken: RefreshToken,\n ): Awaitable<null | TokenInfo>\n\n findTokenByCode(code: Code): Awaitable<null | TokenInfo>\n\n listAccountTokens(sub: Sub): Awaitable<TokenInfo[]>\n}\n\nexport const isTokenStore = buildInterfaceChecker<TokenStore>([\n 'createToken',\n 'deleteToken',\n 'findTokenByCode',\n 'findTokenByRefreshToken',\n 'listAccountTokens',\n 'readToken',\n 'rotateToken',\n])\n\nexport function asTokenStore<V extends Partial<TokenStore>>(\n implementation?: V,\n): V & TokenStore {\n if (!implementation || !isTokenStore(implementation)) {\n throw new Error('Invalid TokenStore implementation')\n }\n return implementation\n}\n"]}
1
+ {"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../src/token/token-store.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAMtE,gEAAgE;AAChE,cAAc,oBAAoB,CAAA;AAClC,cAAc,iBAAiB,CAAA;AAC/B,cAAc,eAAe,CAAA;AA2D7B,MAAM,CAAC,MAAM,YAAY,GAAG,qBAAqB,CAAa;IAC5D,aAAa;IACb,aAAa;IACb,iBAAiB;IACjB,yBAAyB;IACzB,mBAAmB;IACnB,WAAW;IACX,aAAa;CACd,CAAC,CAAA;AAEF,MAAM,UAAU,YAAY,CAC1B,cAAkB;IAElB,IAAI,CAAC,cAAc,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAA;IACtD,CAAC;IACD,OAAO,cAAc,CAAA;AACvB,CAAC","sourcesContent":["import type { Did } from '@atproto/did'\nimport type { Account } from '@atproto/oauth-provider-api'\nimport { Awaitable, buildInterfaceChecker } from '../lib/util/type.js'\nimport { Code } from '../request/code.js'\nimport { RefreshToken } from './refresh-token.js'\nimport { TokenData } from './token-data.js'\nimport { TokenId } from './token-id.js'\n\n// Export all types needed to implement the TokenStore interface\nexport * from './refresh-token.js'\nexport * from './token-data.js'\nexport * from './token-id.js'\nexport type { Account, Awaitable, Did }\n\nexport type TokenInfo = {\n id: TokenId\n data: TokenData\n account: Account\n currentRefreshToken: null | RefreshToken\n}\n\nexport type NewTokenData = {\n clientAuth: TokenData['clientAuth']\n expiresAt: TokenData['expiresAt']\n updatedAt: TokenData['updatedAt']\n scope: NonNullable<TokenData['scope']>\n}\n\nexport type CreateTokenData = TokenData & {\n scope: NonNullable<TokenData['scope']>\n}\n\n/**\n * @param data historically, {@link TokenData.scope} was not present in\n * {@link TokenData}, causing it to be \"nullable\" when returned from\n * {@link TokenStore.readToken}. We use {@link CreateTokenData} here to allow\n * the store implementation to expect its presence.\n */\nexport interface TokenStore {\n createToken(\n tokenId: TokenId,\n data: CreateTokenData,\n refreshToken?: RefreshToken,\n ): Awaitable<void>\n\n readToken(tokenId: TokenId): Awaitable<null | TokenInfo>\n\n deleteToken(tokenId: TokenId): Awaitable<void>\n\n rotateToken(\n tokenId: TokenId,\n newTokenId: TokenId,\n newRefreshToken: RefreshToken,\n newData: NewTokenData,\n ): Awaitable<void>\n\n /**\n * Find a token by its refresh token. Note that previous refresh tokens\n * should also return the token. The data model is responsible for storing\n * old refresh tokens when a new one is issued.\n */\n findTokenByRefreshToken(\n refreshToken: RefreshToken,\n ): Awaitable<null | TokenInfo>\n\n findTokenByCode(code: Code): Awaitable<null | TokenInfo>\n\n listAccountTokens(did: Did): Awaitable<TokenInfo[]>\n}\n\nexport const isTokenStore = buildInterfaceChecker<TokenStore>([\n 'createToken',\n 'deleteToken',\n 'findTokenByCode',\n 'findTokenByRefreshToken',\n 'listAccountTokens',\n 'readToken',\n 'rotateToken',\n])\n\nexport function asTokenStore<V extends Partial<TokenStore>>(\n implementation?: V,\n): V & TokenStore {\n if (!implementation || !isTokenStore(implementation)) {\n throw new Error('Invalid TokenStore implementation')\n }\n return implementation\n}\n"]}
@@ -1,3 +1,7 @@
1
1
  import { z } from 'zod';
2
- export declare const handleSchema: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>;
2
+ /**
3
+ * @note Only validates again AT Protocol's syntax. Additional rules (specific
4
+ * domains, slurs, etc.) may be imposed through the store implementation.
5
+ */
6
+ export declare const handleSchema: z.ZodEffects<z.ZodString, `${string}.${string}`, string>;
3
7
  //# sourceMappingURL=handle.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../../src/types/handle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAGvB,eAAO,MAAM,YAAY,yEAcI,CAAA"}
1
+ {"version":3,"file":"handle.d.ts","sourceRoot":"","sources":["../../src/types/handle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAOvB;;;GAGG;AACH,eAAO,MAAM,YAAY,0DAWvB,CAAA"}
@@ -1,19 +1,20 @@
1
1
  import { z } from 'zod';
2
- import { ensureValidHandle, normalizeHandle } from '@atproto/syntax';
3
- export const handleSchema = z
4
- .string()
5
- // @NOTE: We only check against validity towards ATProto's syntax. Additional
6
- // rules may be imposed by the store implementation.
7
- .superRefine((value, ctx) => {
2
+ import { ensureValidHandle, normalizeHandle, } from '@atproto/syntax';
3
+ /**
4
+ * @note Only validates again AT Protocol's syntax. Additional rules (specific
5
+ * domains, slurs, etc.) may be imposed through the store implementation.
6
+ */
7
+ export const handleSchema = z.string().transform((value, ctx) => {
8
8
  try {
9
9
  ensureValidHandle(value);
10
+ return normalizeHandle(value);
10
11
  }
11
12
  catch (err) {
12
13
  ctx.addIssue({
13
14
  code: z.ZodIssueCode.custom,
14
15
  message: err instanceof Error ? err.message : 'Invalid handle',
15
16
  });
17
+ return z.NEVER;
16
18
  }
17
- })
18
- .transform(normalizeHandle);
19
+ });
19
20
  //# sourceMappingURL=handle.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"handle.js","sourceRoot":"","sources":["../../src/types/handle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAEpE,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC;KAC1B,MAAM,EAAE;IACT,6EAA6E;IAC7E,oDAAoD;KACnD,WAAW,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;IAC1B,IAAI,CAAC;QACH,iBAAiB,CAAC,KAAK,CAAC,CAAA;IAC1B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,MAAM;YAC3B,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB;SAC/D,CAAC,CAAA;IACJ,CAAC;AACH,CAAC,CAAC;KACD,SAAS,CAAC,eAAe,CAAC,CAAA","sourcesContent":["import { z } from 'zod'\nimport { ensureValidHandle, normalizeHandle } from '@atproto/syntax'\n\nexport const handleSchema = z\n .string()\n // @NOTE: We only check against validity towards ATProto's syntax. Additional\n // rules may be imposed by the store implementation.\n .superRefine((value, ctx) => {\n try {\n ensureValidHandle(value)\n } catch (err) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: err instanceof Error ? err.message : 'Invalid handle',\n })\n }\n })\n .transform(normalizeHandle)\n"]}
1
+ {"version":3,"file":"handle.js","sourceRoot":"","sources":["../../src/types/handle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EAEL,iBAAiB,EACjB,eAAe,GAChB,MAAM,iBAAiB,CAAA;AAExB;;;GAGG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,GAAG,EAAgB,EAAE;IAC5E,IAAI,CAAC;QACH,iBAAiB,CAAC,KAAK,CAAC,CAAA;QACxB,OAAO,eAAe,CAAC,KAAK,CAAC,CAAA;IAC/B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,GAAG,CAAC,QAAQ,CAAC;YACX,IAAI,EAAE,CAAC,CAAC,YAAY,CAAC,MAAM;YAC3B,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,gBAAgB;SAC/D,CAAC,CAAA;QACF,OAAO,CAAC,CAAC,KAAK,CAAA;IAChB,CAAC;AACH,CAAC,CAAC,CAAA","sourcesContent":["import { z } from 'zod'\nimport {\n HandleString,\n ensureValidHandle,\n normalizeHandle,\n} from '@atproto/syntax'\n\n/**\n * @note Only validates again AT Protocol's syntax. Additional rules (specific\n * domains, slurs, etc.) may be imposed through the store implementation.\n */\nexport const handleSchema = z.string().transform((value, ctx): HandleString => {\n try {\n ensureValidHandle(value)\n return normalizeHandle(value)\n } catch (err) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: err instanceof Error ? err.message : 'Invalid handle',\n })\n return z.NEVER\n }\n})\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/oauth-provider",
3
- "version": "0.18.4",
3
+ "version": "0.19.1",
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": [
@@ -40,20 +40,20 @@
40
40
  "ioredis": "^5.3.2",
41
41
  "jose": "^5.2.0",
42
42
  "zod": "^3.23.8",
43
- "@atproto-labs/fetch-node": "^0.3.1",
44
43
  "@atproto-labs/fetch": "^0.3.1",
44
+ "@atproto-labs/pipe": "^0.2.1",
45
+ "@atproto-labs/fetch-node": "^0.3.2",
45
46
  "@atproto-labs/simple-store": "^0.4.1",
46
47
  "@atproto-labs/simple-store-memory": "^0.2.1",
47
- "@atproto-labs/pipe": "^0.2.1",
48
+ "@atproto/did": "^0.5.1",
49
+ "@atproto/jwk": "^0.7.1",
48
50
  "@atproto/common": "^0.6.3",
49
51
  "@atproto/jwk-jose": "^0.2.1",
50
- "@atproto/did": "^0.5.1",
51
- "@atproto/oauth-types": "^0.7.2",
52
52
  "@atproto/lex-document": "^0.1.1",
53
- "@atproto/jwk": "^0.7.1",
54
53
  "@atproto/lex-resolver": "^0.1.1",
55
- "@atproto/oauth-provider-ui": "0.7.4",
56
- "@atproto/oauth-provider-api": "0.6.2",
54
+ "@atproto/oauth-types": "^0.7.2",
55
+ "@atproto/oauth-provider-api": "0.7.0",
56
+ "@atproto/oauth-provider-ui": "0.8.1",
57
57
  "@atproto/oauth-scopes": "^0.5.1",
58
58
  "@atproto/syntax": "^0.6.2"
59
59
  },
@@ -1,3 +1,4 @@
1
+ import { Did } from '@atproto/did'
1
2
  import {
2
3
  OAuthIssuerIdentifier,
3
4
  isOAuthClientIdLoopback,
@@ -12,11 +13,12 @@ import { callAsync } from '../lib/util/function.js'
12
13
  import { constantTime } from '../lib/util/time.js'
13
14
  import { OAuthHooks, RequestMetadata } from '../oauth-hooks.js'
14
15
  import { Customization } from '../oauth-provider.js'
15
- import { Sub } from '../oidc/sub.js'
16
16
  import {
17
17
  Account,
18
18
  AccountStore,
19
19
  AuthorizedClientData,
20
+ DeleteAccountConfirmInput,
21
+ DeleteAccountRequestInput,
20
22
  DeviceAccount,
21
23
  ResetPasswordConfirmInput,
22
24
  ResetPasswordRequestInput,
@@ -152,7 +154,7 @@ export class AccountManager {
152
154
 
153
155
  return account
154
156
  } catch (err) {
155
- await this.removeDeviceAccount(deviceId, account.sub)
157
+ await this.removeDeviceAccount(deviceId, account.did)
156
158
 
157
159
  throw InvalidRequestError.from(
158
160
  err,
@@ -190,7 +192,7 @@ export class AccountManager {
190
192
  // This information is only exposed to the hook and is never
191
193
  // surfaced to the client.
192
194
  const isCredentialsError = err instanceof InvalidCredentialsError
193
- const sub = isCredentialsError ? err.sub ?? null : null
195
+ const did = isCredentialsError ? err.did ?? null : null
194
196
 
195
197
  // Swallow any error from the hook itself so that it does not mask
196
198
  // the underlying authentication failure being reported.
@@ -198,7 +200,7 @@ export class AccountManager {
198
200
  await this.hooks.onSignInFailed?.call(null, {
199
201
  data,
200
202
  error: err,
201
- sub,
203
+ did,
202
204
  deviceId,
203
205
  deviceMetadata,
204
206
  clientId,
@@ -235,16 +237,16 @@ export class AccountManager {
235
237
 
236
238
  public async upsertDeviceAccount(
237
239
  deviceId: DeviceId,
238
- sub: Sub,
240
+ did: Did,
239
241
  ): Promise<void> {
240
- await this.store.upsertDeviceAccount(deviceId, sub)
242
+ await this.store.upsertDeviceAccount(deviceId, did)
241
243
  }
242
244
 
243
245
  public async getDeviceAccount(
244
246
  deviceId: DeviceId,
245
- sub: Sub,
247
+ did: Did,
246
248
  ): Promise<DeviceAccount> {
247
- const deviceAccount = await this.store.getDeviceAccount(deviceId, sub)
249
+ const deviceAccount = await this.store.getDeviceAccount(deviceId, did)
248
250
  if (!deviceAccount) throw new InvalidRequestError(`Account not found`)
249
251
 
250
252
  return deviceAccount
@@ -258,15 +260,15 @@ export class AccountManager {
258
260
  // "Loopback" clients are not distinguishable from one another.
259
261
  if (isOAuthClientIdLoopback(client.id)) return
260
262
 
261
- await this.store.setAuthorizedClient(account.sub, client.id, data)
263
+ await this.store.setAuthorizedClient(account.did, client.id, data)
262
264
  }
263
265
 
264
- public async getAccount(sub: Sub) {
265
- return this.store.getAccount(sub)
266
+ public async getAccount(did: Did) {
267
+ return this.store.getAccount(did)
266
268
  }
267
269
 
268
- public async removeDeviceAccount(deviceId: DeviceId, sub: Sub) {
269
- return this.store.removeDeviceAccount(deviceId, sub)
270
+ public async removeDeviceAccount(deviceId: DeviceId, did: Did) {
271
+ return this.store.removeDeviceAccount(deviceId, did)
270
272
  }
271
273
 
272
274
  public async listDeviceAccounts(
@@ -280,13 +282,13 @@ export class AccountManager {
280
282
  .filter((deviceAccount) => deviceAccount.deviceId === deviceId)
281
283
  }
282
284
 
283
- public async listAccountDevices(sub: Sub): Promise<DeviceAccount[]> {
285
+ public async listAccountDevices(did: Did): Promise<DeviceAccount[]> {
284
286
  const deviceAccounts = await this.store.listDeviceAccounts({
285
- sub,
287
+ did,
286
288
  })
287
289
 
288
290
  return deviceAccounts // Fool proof
289
- .filter((deviceAccount) => deviceAccount.account.sub === sub)
291
+ .filter((deviceAccount) => deviceAccount.account.did === did)
290
292
  }
291
293
 
292
294
  public async resetPasswordRequest(
@@ -412,8 +414,8 @@ export class AccountManager {
412
414
  await this.hooks.onVerifyEmailRequest?.call(null, {
413
415
  deviceId,
414
416
  deviceMetadata,
415
- input,
416
417
  account,
418
+ input,
417
419
  })
418
420
 
419
421
  await this.store.verifyEmailRequest(input)
@@ -421,8 +423,8 @@ export class AccountManager {
421
423
  await this.hooks.onVerifyEmailRequested?.call(null, {
422
424
  deviceId,
423
425
  deviceMetadata,
424
- input,
425
426
  account,
427
+ input,
426
428
  })
427
429
  }
428
430
 
@@ -435,8 +437,8 @@ export class AccountManager {
435
437
  await this.hooks.onVerifyEmailConfirm?.call(null, {
436
438
  deviceId,
437
439
  deviceMetadata,
438
- input,
439
440
  account,
441
+ input,
440
442
  })
441
443
 
442
444
  const updatedAccount = await this.store.verifyEmailConfirm(input)
@@ -448,8 +450,8 @@ export class AccountManager {
448
450
  await this.hooks.onVerifyEmailConfirmed?.call(null, {
449
451
  deviceId,
450
452
  deviceMetadata,
451
- input,
452
453
  account: updatedAccount,
454
+ input,
453
455
  })
454
456
 
455
457
  return updatedAccount
@@ -479,4 +481,105 @@ export class AccountManager {
479
481
 
480
482
  return updatedAccount
481
483
  }
484
+
485
+ public async deactivateAccount(
486
+ deviceId: DeviceId,
487
+ deviceMetadata: RequestMetadata,
488
+ account: Account,
489
+ ): Promise<Account> {
490
+ await this.hooks.onDeactivateAccount?.call(null, {
491
+ deviceId,
492
+ deviceMetadata,
493
+ account,
494
+ })
495
+
496
+ const updatedAccount = await callAsync(() =>
497
+ this.store.deactivateAccount({ did: account.did }),
498
+ ).catch((err) => {
499
+ throw InvalidRequestError.from(err, 'Account deactivation failed')
500
+ })
501
+
502
+ await this.hooks.onDeactivatedAccount?.call(null, {
503
+ deviceId,
504
+ deviceMetadata,
505
+ account: updatedAccount,
506
+ })
507
+
508
+ return updatedAccount
509
+ }
510
+
511
+ public async reactivateAccount(
512
+ deviceId: DeviceId,
513
+ deviceMetadata: RequestMetadata,
514
+ account: Account,
515
+ ): Promise<Account> {
516
+ await this.hooks.onReactivateAccount?.call(null, {
517
+ deviceId,
518
+ deviceMetadata,
519
+ account,
520
+ })
521
+
522
+ const updatedAccount = await callAsync(() =>
523
+ this.store.reactivateAccount({ did: account.did }),
524
+ ).catch((err) => {
525
+ throw InvalidRequestError.from(err, 'Account reactivation failed')
526
+ })
527
+
528
+ await this.hooks.onReactivatedAccount?.call(null, {
529
+ deviceId,
530
+ deviceMetadata,
531
+ account: updatedAccount,
532
+ })
533
+
534
+ return updatedAccount
535
+ }
536
+
537
+ public async deleteAccountRequest(
538
+ deviceId: DeviceId,
539
+ deviceMetadata: RequestMetadata,
540
+ input: DeleteAccountRequestInput,
541
+ account: Account,
542
+ ): Promise<void> {
543
+ await this.hooks.onDeleteAccountRequest?.call(null, {
544
+ deviceId,
545
+ deviceMetadata,
546
+ account,
547
+ })
548
+
549
+ await this.store.deleteAccountRequest(input)
550
+
551
+ await this.hooks.onDeleteAccountRequested?.call(null, {
552
+ deviceId,
553
+ deviceMetadata,
554
+ account,
555
+ })
556
+ }
557
+
558
+ public async deleteAccountConfirm(
559
+ deviceId: DeviceId,
560
+ deviceMetadata: RequestMetadata,
561
+ input: DeleteAccountConfirmInput,
562
+ account: Account,
563
+ ): Promise<void> {
564
+ return constantTime(BRUTE_FORCE_MITIGATION_DELAY, async () => {
565
+ await this.hooks.onDeleteAccountConfirm?.call(null, {
566
+ deviceId,
567
+ deviceMetadata,
568
+ account,
569
+ })
570
+
571
+ await callAsync(() => this.store.deleteAccountConfirm(input)).catch(
572
+ (err) => {
573
+ throw InvalidRequestError.from(err, 'Account deletion failed')
574
+ },
575
+ )
576
+
577
+ await this.hooks.onDeleteAccountConfirmed?.call(null, {
578
+ deviceId,
579
+ deviceMetadata,
580
+ account,
581
+ input,
582
+ })
583
+ })
584
+ }
482
585
  }