@atproto/oauth-provider 0.13.2 → 0.13.4

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 CHANGED
@@ -1,5 +1,30 @@
1
1
  # @atproto/oauth-provider
2
2
 
3
+ ## 0.13.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#4301](https://github.com/bluesky-social/atproto/pull/4301) [`f496fa2c4`](https://github.com/bluesky-social/atproto/commit/f496fa2c4d9316229523454c691c75c269aba21e) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Set dark background on authorization page's `<body>` in dark mode
8
+
9
+ - Updated dependencies [[`f496fa2c4`](https://github.com/bluesky-social/atproto/commit/f496fa2c4d9316229523454c691c75c269aba21e)]:
10
+ - @atproto/oauth-provider-ui@0.3.4
11
+
12
+ ## 0.13.3
13
+
14
+ ### Patch Changes
15
+
16
+ - [#4293](https://github.com/bluesky-social/atproto/pull/4293) [`8c03d75b6`](https://github.com/bluesky-social/atproto/commit/8c03d75b6c11bed15b58bfa7ff4bf68199fc6511) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Remove un-implemented `introspect` endpoint from OAuth Authorization Server metadata
17
+
18
+ - [#4265](https://github.com/bluesky-social/atproto/pull/4265) [`1e702ea67`](https://github.com/bluesky-social/atproto/commit/1e702ea675e3697e050be1f28e54bb1298b56436) Thanks [@matthieusieben](https://github.com/matthieusieben)! - Add `onResetPasswordRequested` and `onResetPasswordConfirmed` hooks to be called after the respective actions are completed.
19
+
20
+ - Updated dependencies [[`8ff5ec4ca`](https://github.com/bluesky-social/atproto/commit/8ff5ec4caa9a1f5c1e453a416ba2af22d1ee4f58), [`8ff5ec4ca`](https://github.com/bluesky-social/atproto/commit/8ff5ec4caa9a1f5c1e453a416ba2af22d1ee4f58), [`8ff5ec4ca`](https://github.com/bluesky-social/atproto/commit/8ff5ec4caa9a1f5c1e453a416ba2af22d1ee4f58), [`8ff5ec4ca`](https://github.com/bluesky-social/atproto/commit/8ff5ec4caa9a1f5c1e453a416ba2af22d1ee4f58)]:
21
+ - @atproto/oauth-types@0.5.0
22
+ - @atproto-labs/fetch-node@0.2.0
23
+ - @atproto/oauth-provider-api@0.3.2
24
+ - @atproto/oauth-provider-frontend@0.2.3
25
+ - @atproto/oauth-provider-ui@0.3.3
26
+ - @atproto/lexicon-resolver@0.2.3
27
+
3
28
  ## 0.13.2
4
29
 
5
30
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"account-manager.d.ts","sourceRoot":"","sources":["../../src/account/account-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"account-manager.d.ts","sourceRoot":"","sources":["../../src/account/account-manager.ts"],"names":[],"mappings":"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"}
@@ -156,7 +156,16 @@ class AccountManager {
156
156
  deviceMetadata,
157
157
  });
158
158
  return (0, time_js_1.constantTime)(TIMING_ATTACK_MITIGATION_DELAY, async () => {
159
- await this.store.resetPasswordRequest(input);
159
+ const account = await this.store.resetPasswordRequest(input);
160
+ if (!account) {
161
+ return; // Silently ignore to prevent user enumeration
162
+ }
163
+ await this.hooks.onResetPasswordRequested?.call(null, {
164
+ input,
165
+ deviceId,
166
+ deviceMetadata,
167
+ account,
168
+ });
160
169
  });
161
170
  }
162
171
  async resetPasswordConfirm(deviceId, deviceMetadata, input) {
@@ -166,7 +175,16 @@ class AccountManager {
166
175
  deviceMetadata,
167
176
  });
168
177
  return (0, time_js_1.constantTime)(TIMING_ATTACK_MITIGATION_DELAY, async () => {
169
- await this.store.resetPasswordConfirm(input);
178
+ const account = await this.store.resetPasswordConfirm(input);
179
+ if (!account) {
180
+ throw new invalid_request_error_js_1.InvalidRequestError('Invalid token');
181
+ }
182
+ await this.hooks.onResetPasswordConfirmed?.call(null, {
183
+ input,
184
+ deviceId,
185
+ deviceMetadata,
186
+ account,
187
+ });
170
188
  });
171
189
  }
172
190
  async verifyHandleAvailability(handle) {
@@ -1 +1 @@
1
- {"version":3,"file":"account-manager.js","sourceRoot":"","sources":["../../src/account/account-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n OAuthIssuerIdentifier,\n isOAuthClientIdLoopback,\n} from '@atproto/oauth-types'\nimport { Client } from '../client/client.js'\nimport { DeviceId } from '../device/device-id.js'\nimport { InvalidRequestError } from '../errors/invalid-request-error.js'\nimport { HCaptchaClient, HcaptchaVerifyResult } from '../lib/hcaptcha.js'\nimport { constantTime } from '../lib/util/time.js'\nimport { OAuthHooks, RequestMetadata } from '../oauth-hooks.js'\nimport { Customization } from '../oauth-provider.js'\nimport { Sub } from '../oidc/sub.js'\nimport {\n Account,\n AccountStore,\n AuthorizedClientData,\n DeviceAccount,\n ResetPasswordConfirmInput,\n ResetPasswordRequestInput,\n SignUpData,\n} from './account-store.js'\nimport { SignInData } from './sign-in-data.js'\nimport { SignUpInput } from './sign-up-input.js'\n\nconst TIMING_ATTACK_MITIGATION_DELAY = 400\nconst BRUTE_FORCE_MITIGATION_DELAY = 300\n\nexport class AccountManager {\n protected readonly inviteCodeRequired: boolean\n protected readonly hcaptchaClient?: HCaptchaClient\n\n constructor(\n issuer: OAuthIssuerIdentifier,\n protected readonly store: AccountStore,\n protected readonly hooks: OAuthHooks,\n customization: Customization,\n ) {\n this.inviteCodeRequired = customization.inviteCodeRequired !== false\n this.hcaptchaClient = customization.hcaptcha\n ? new HCaptchaClient(new URL(issuer).hostname, customization.hcaptcha)\n : undefined\n }\n\n protected async processHcaptchaToken(\n input: SignUpInput,\n deviceId: DeviceId,\n deviceMetadata: RequestMetadata,\n ): Promise<HcaptchaVerifyResult | undefined> {\n if (!this.hcaptchaClient) {\n return undefined\n }\n\n if (!input.hcaptchaToken) {\n throw new InvalidRequestError('hCaptcha token is required')\n }\n\n const tokens = this.hcaptchaClient.buildClientTokens(\n deviceMetadata.ipAddress,\n input.handle,\n deviceMetadata.userAgent,\n )\n\n const result = await this.hcaptchaClient\n .verify('signup', input.hcaptchaToken, deviceMetadata.ipAddress, tokens)\n .catch((err) => {\n throw InvalidRequestError.from(err, 'hCaptcha verification failed')\n })\n\n await this.hooks.onHcaptchaResult?.call(null, {\n input,\n deviceId,\n deviceMetadata,\n tokens,\n result,\n })\n\n try {\n this.hcaptchaClient.checkVerifyResult(result, tokens)\n } catch (err) {\n throw InvalidRequestError.from(err, 'hCaptcha verification failed')\n }\n\n return result\n }\n\n protected async enforceInviteCode(\n input: SignUpInput,\n _deviceId: DeviceId,\n _deviceMetadata: RequestMetadata,\n ): Promise<string | undefined> {\n if (!this.inviteCodeRequired) {\n return undefined\n }\n\n if (!input.inviteCode) {\n throw new InvalidRequestError('Invite code is required')\n }\n\n return input.inviteCode\n }\n\n protected async buildSignupData(\n input: SignUpInput,\n deviceId: DeviceId,\n deviceMetadata: RequestMetadata,\n ): Promise<SignUpData> {\n const [hcaptchaResult, inviteCode] = await Promise.all([\n this.processHcaptchaToken(input, deviceId, deviceMetadata),\n this.enforceInviteCode(input, deviceId, deviceMetadata),\n ])\n\n return { ...input, hcaptchaResult, inviteCode }\n }\n\n public async createAccount(\n deviceId: DeviceId,\n deviceMetadata: RequestMetadata,\n input: SignUpInput,\n ): Promise<Account> {\n await this.hooks.onSignUpAttempt?.call(null, {\n input,\n deviceId,\n deviceMetadata,\n })\n\n const data = await this.buildSignupData(input, deviceId, deviceMetadata)\n\n // Mitigation against brute forcing email of users.\n // @TODO Add rate limit to all the OAuth routes.\n const account = await constantTime(\n BRUTE_FORCE_MITIGATION_DELAY,\n async () => {\n return this.store.createAccount(data)\n },\n ).catch((err) => {\n throw InvalidRequestError.from(err, 'Account creation failed')\n })\n\n try {\n await this.hooks.onSignedUp?.call(null, {\n data,\n account,\n deviceId,\n deviceMetadata,\n })\n\n return account\n } catch (err) {\n await this.removeDeviceAccount(deviceId, account.sub)\n\n throw InvalidRequestError.from(\n err,\n 'The account was successfully created but something went wrong, try signing-in.',\n )\n }\n }\n\n public async authenticateAccount(\n deviceId: DeviceId,\n deviceMetadata: RequestMetadata,\n data: SignInData,\n ): Promise<Account> {\n try {\n await this.hooks.onSignInAttempt?.call(null, {\n data,\n deviceId,\n deviceMetadata,\n })\n\n const account = await constantTime(\n TIMING_ATTACK_MITIGATION_DELAY,\n async () => {\n return this.store.authenticateAccount(data)\n },\n )\n\n await this.hooks.onSignedIn?.call(null, {\n data,\n account,\n deviceId,\n deviceMetadata,\n })\n\n return account\n } catch (err) {\n throw InvalidRequestError.from(\n err,\n 'Unable to sign-in due to an unexpected server error',\n )\n }\n }\n\n public async upsertDeviceAccount(\n deviceId: DeviceId,\n sub: Sub,\n ): Promise<void> {\n await this.store.upsertDeviceAccount(deviceId, sub)\n }\n\n public async getDeviceAccount(\n deviceId: DeviceId,\n sub: Sub,\n ): Promise<DeviceAccount> {\n const deviceAccount = await this.store.getDeviceAccount(deviceId, sub)\n if (!deviceAccount) throw new InvalidRequestError(`Account not found`)\n\n return deviceAccount\n }\n\n public async setAuthorizedClient(\n account: Account,\n client: Client,\n data: AuthorizedClientData,\n ): Promise<void> {\n // \"Loopback\" clients are not distinguishable from one another.\n if (isOAuthClientIdLoopback(client.id)) return\n\n await this.store.setAuthorizedClient(account.sub, client.id, data)\n }\n\n public async getAccount(sub: Sub) {\n return this.store.getAccount(sub)\n }\n\n public async removeDeviceAccount(deviceId: DeviceId, sub: Sub) {\n return this.store.removeDeviceAccount(deviceId, sub)\n }\n\n public async listDeviceAccounts(\n deviceId: DeviceId,\n ): Promise<DeviceAccount[]> {\n const deviceAccounts = await this.store.listDeviceAccounts({\n deviceId,\n })\n\n return deviceAccounts // Fool proof\n .filter((deviceAccount) => deviceAccount.deviceId === deviceId)\n }\n\n public async listAccountDevices(sub: Sub): Promise<DeviceAccount[]> {\n const deviceAccounts = await this.store.listDeviceAccounts({\n sub,\n })\n\n return deviceAccounts // Fool proof\n .filter((deviceAccount) => deviceAccount.account.sub === sub)\n }\n\n public async resetPasswordRequest(\n deviceId: DeviceId,\n deviceMetadata: RequestMetadata,\n input: ResetPasswordRequestInput,\n ) {\n await this.hooks.onResetPasswordRequest?.call(null, {\n input,\n deviceId,\n deviceMetadata,\n })\n\n return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {\n await this.store.resetPasswordRequest(input)\n })\n }\n\n public async resetPasswordConfirm(\n deviceId: DeviceId,\n deviceMetadata: RequestMetadata,\n input: ResetPasswordConfirmInput,\n ) {\n await this.hooks.onResetPasswordConfirm?.call(null, {\n input,\n deviceId,\n deviceMetadata,\n })\n\n return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {\n await this.store.resetPasswordConfirm(input)\n })\n }\n\n public async verifyHandleAvailability(handle: string): Promise<void> {\n return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {\n return this.store.verifyHandleAvailability(handle)\n })\n }\n}\n"]}
1
+ {"version":3,"file":"account-manager.js","sourceRoot":"","sources":["../../src/account/account-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n OAuthIssuerIdentifier,\n isOAuthClientIdLoopback,\n} from '@atproto/oauth-types'\nimport { Client } from '../client/client.js'\nimport { DeviceId } from '../device/device-id.js'\nimport { InvalidRequestError } from '../errors/invalid-request-error.js'\nimport { HCaptchaClient, HcaptchaVerifyResult } from '../lib/hcaptcha.js'\nimport { constantTime } from '../lib/util/time.js'\nimport { OAuthHooks, RequestMetadata } from '../oauth-hooks.js'\nimport { Customization } from '../oauth-provider.js'\nimport { Sub } from '../oidc/sub.js'\nimport {\n Account,\n AccountStore,\n AuthorizedClientData,\n DeviceAccount,\n ResetPasswordConfirmInput,\n ResetPasswordRequestInput,\n SignUpData,\n} from './account-store.js'\nimport { SignInData } from './sign-in-data.js'\nimport { SignUpInput } from './sign-up-input.js'\n\nconst TIMING_ATTACK_MITIGATION_DELAY = 400\nconst BRUTE_FORCE_MITIGATION_DELAY = 300\n\nexport class AccountManager {\n protected readonly inviteCodeRequired: boolean\n protected readonly hcaptchaClient?: HCaptchaClient\n\n constructor(\n issuer: OAuthIssuerIdentifier,\n protected readonly store: AccountStore,\n protected readonly hooks: OAuthHooks,\n customization: Customization,\n ) {\n this.inviteCodeRequired = customization.inviteCodeRequired !== false\n this.hcaptchaClient = customization.hcaptcha\n ? new HCaptchaClient(new URL(issuer).hostname, customization.hcaptcha)\n : undefined\n }\n\n protected async processHcaptchaToken(\n input: SignUpInput,\n deviceId: DeviceId,\n deviceMetadata: RequestMetadata,\n ): Promise<HcaptchaVerifyResult | undefined> {\n if (!this.hcaptchaClient) {\n return undefined\n }\n\n if (!input.hcaptchaToken) {\n throw new InvalidRequestError('hCaptcha token is required')\n }\n\n const tokens = this.hcaptchaClient.buildClientTokens(\n deviceMetadata.ipAddress,\n input.handle,\n deviceMetadata.userAgent,\n )\n\n const result = await this.hcaptchaClient\n .verify('signup', input.hcaptchaToken, deviceMetadata.ipAddress, tokens)\n .catch((err) => {\n throw InvalidRequestError.from(err, 'hCaptcha verification failed')\n })\n\n await this.hooks.onHcaptchaResult?.call(null, {\n input,\n deviceId,\n deviceMetadata,\n tokens,\n result,\n })\n\n try {\n this.hcaptchaClient.checkVerifyResult(result, tokens)\n } catch (err) {\n throw InvalidRequestError.from(err, 'hCaptcha verification failed')\n }\n\n return result\n }\n\n protected async enforceInviteCode(\n input: SignUpInput,\n _deviceId: DeviceId,\n _deviceMetadata: RequestMetadata,\n ): Promise<string | undefined> {\n if (!this.inviteCodeRequired) {\n return undefined\n }\n\n if (!input.inviteCode) {\n throw new InvalidRequestError('Invite code is required')\n }\n\n return input.inviteCode\n }\n\n protected async buildSignupData(\n input: SignUpInput,\n deviceId: DeviceId,\n deviceMetadata: RequestMetadata,\n ): Promise<SignUpData> {\n const [hcaptchaResult, inviteCode] = await Promise.all([\n this.processHcaptchaToken(input, deviceId, deviceMetadata),\n this.enforceInviteCode(input, deviceId, deviceMetadata),\n ])\n\n return { ...input, hcaptchaResult, inviteCode }\n }\n\n public async createAccount(\n deviceId: DeviceId,\n deviceMetadata: RequestMetadata,\n input: SignUpInput,\n ): Promise<Account> {\n await this.hooks.onSignUpAttempt?.call(null, {\n input,\n deviceId,\n deviceMetadata,\n })\n\n const data = await this.buildSignupData(input, deviceId, deviceMetadata)\n\n // Mitigation against brute forcing email of users.\n // @TODO Add rate limit to all the OAuth routes.\n const account = await constantTime(\n BRUTE_FORCE_MITIGATION_DELAY,\n async () => {\n return this.store.createAccount(data)\n },\n ).catch((err) => {\n throw InvalidRequestError.from(err, 'Account creation failed')\n })\n\n try {\n await this.hooks.onSignedUp?.call(null, {\n data,\n account,\n deviceId,\n deviceMetadata,\n })\n\n return account\n } catch (err) {\n await this.removeDeviceAccount(deviceId, account.sub)\n\n throw InvalidRequestError.from(\n err,\n 'The account was successfully created but something went wrong, try signing-in.',\n )\n }\n }\n\n public async authenticateAccount(\n deviceId: DeviceId,\n deviceMetadata: RequestMetadata,\n data: SignInData,\n ): Promise<Account> {\n try {\n await this.hooks.onSignInAttempt?.call(null, {\n data,\n deviceId,\n deviceMetadata,\n })\n\n const account = await constantTime(\n TIMING_ATTACK_MITIGATION_DELAY,\n async () => {\n return this.store.authenticateAccount(data)\n },\n )\n\n await this.hooks.onSignedIn?.call(null, {\n data,\n account,\n deviceId,\n deviceMetadata,\n })\n\n return account\n } catch (err) {\n throw InvalidRequestError.from(\n err,\n 'Unable to sign-in due to an unexpected server error',\n )\n }\n }\n\n public async upsertDeviceAccount(\n deviceId: DeviceId,\n sub: Sub,\n ): Promise<void> {\n await this.store.upsertDeviceAccount(deviceId, sub)\n }\n\n public async getDeviceAccount(\n deviceId: DeviceId,\n sub: Sub,\n ): Promise<DeviceAccount> {\n const deviceAccount = await this.store.getDeviceAccount(deviceId, sub)\n if (!deviceAccount) throw new InvalidRequestError(`Account not found`)\n\n return deviceAccount\n }\n\n public async setAuthorizedClient(\n account: Account,\n client: Client,\n data: AuthorizedClientData,\n ): Promise<void> {\n // \"Loopback\" clients are not distinguishable from one another.\n if (isOAuthClientIdLoopback(client.id)) return\n\n await this.store.setAuthorizedClient(account.sub, client.id, data)\n }\n\n public async getAccount(sub: Sub) {\n return this.store.getAccount(sub)\n }\n\n public async removeDeviceAccount(deviceId: DeviceId, sub: Sub) {\n return this.store.removeDeviceAccount(deviceId, sub)\n }\n\n public async listDeviceAccounts(\n deviceId: DeviceId,\n ): Promise<DeviceAccount[]> {\n const deviceAccounts = await this.store.listDeviceAccounts({\n deviceId,\n })\n\n return deviceAccounts // Fool proof\n .filter((deviceAccount) => deviceAccount.deviceId === deviceId)\n }\n\n public async listAccountDevices(sub: Sub): Promise<DeviceAccount[]> {\n const deviceAccounts = await this.store.listDeviceAccounts({\n sub,\n })\n\n return deviceAccounts // Fool proof\n .filter((deviceAccount) => deviceAccount.account.sub === sub)\n }\n\n public async resetPasswordRequest(\n deviceId: DeviceId,\n deviceMetadata: RequestMetadata,\n input: ResetPasswordRequestInput,\n ) {\n await this.hooks.onResetPasswordRequest?.call(null, {\n input,\n deviceId,\n deviceMetadata,\n })\n\n return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {\n const account = await this.store.resetPasswordRequest(input)\n\n if (!account) {\n return // Silently ignore to prevent user enumeration\n }\n\n await this.hooks.onResetPasswordRequested?.call(null, {\n input,\n deviceId,\n deviceMetadata,\n account,\n })\n })\n }\n\n public async resetPasswordConfirm(\n deviceId: DeviceId,\n deviceMetadata: RequestMetadata,\n input: ResetPasswordConfirmInput,\n ) {\n await this.hooks.onResetPasswordConfirm?.call(null, {\n input,\n deviceId,\n deviceMetadata,\n })\n\n return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {\n const account = await this.store.resetPasswordConfirm(input)\n\n if (!account) {\n throw new InvalidRequestError('Invalid token')\n }\n\n await this.hooks.onResetPasswordConfirmed?.call(null, {\n input,\n deviceId,\n deviceMetadata,\n account,\n })\n })\n }\n\n public async verifyHandleAvailability(handle: string): Promise<void> {\n return constantTime(TIMING_ATTACK_MITIGATION_DELAY, async () => {\n return this.store.verifyHandleAvailability(handle)\n })\n }\n}\n"]}
@@ -125,8 +125,8 @@ export interface AccountStore {
125
125
  } | {
126
126
  deviceId: DeviceId;
127
127
  }): Awaitable<DeviceAccount[]>;
128
- resetPasswordRequest(data: ResetPasswordRequestInput): Awaitable<void>;
129
- resetPasswordConfirm(data: ResetPasswordConfirmInput): Awaitable<void>;
128
+ resetPasswordRequest(data: ResetPasswordRequestInput): Awaitable<null | Account>;
129
+ resetPasswordConfirm(data: ResetPasswordConfirmInput): Awaitable<null | Account>;
130
130
  /**
131
131
  * @throws {HandleUnavailableError} - To indicate that the handle is already taken
132
132
  */
@@ -1 +1 @@
1
- {"version":3,"file":"account-store.d.ts","sourceRoot":"","sources":["../../src/account/account-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"account-store.d.ts","sourceRoot":"","sources":["../../src/account/account-store.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"account-store.js","sourceRoot":"","sources":["../../src/account/account-store.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAoMA,wCAKC;AA/LD,iDAAsE;AACtE,wDAI2B;AAsBzB,uGAzBA,wCAAsB,OAyBA;AACtB,oGAzBA,qCAAmB,OAyBA;AACnB,wHAzBA,yDAAuC,OAyBA;AAnBzC,kEAAkE;AAElE,yDAAsC;AACtC,2DAAwC;AACxC,yDAAsC;AACtC,iDAA8B;AAC9B,2DAAwC;AA4J3B,QAAA,cAAc,GAAG,IAAA,+BAAqB,EAAe;IAChE,eAAe;IACf,qBAAqB;IACrB,qBAAqB;IACrB,YAAY;IACZ,qBAAqB;IACrB,kBAAkB;IAClB,qBAAqB;IACrB,oBAAoB;IACpB,sBAAsB;IACtB,sBAAsB;IACtB,0BAA0B;CAC3B,CAAC,CAAA;AAEF,SAAgB,cAAc,CAAI,cAAiB;IACjD,IAAI,CAAC,cAAc,IAAI,CAAC,IAAA,sBAAc,EAAC,cAAc,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;IACxD,CAAC;IACD,OAAO,cAAc,CAAA;AACvB,CAAC","sourcesContent":["import {\n Account,\n ConfirmResetPasswordInput,\n InitiatePasswordResetInput,\n} from '@atproto/oauth-provider-api'\nimport { OAuthScope } from '@atproto/oauth-types'\nimport { ClientId } from '../client/client-id.js'\nimport { DeviceId } from '../device/device-id.js'\nimport { DeviceData } from '../device/device-store.js'\nimport { HcaptchaVerifyResult } from '../lib/hcaptcha.js'\nimport { Awaitable, buildInterfaceChecker } from '../lib/util/type.js'\nimport {\n HandleUnavailableError,\n InvalidRequestError,\n SecondAuthenticationFactorRequiredError,\n} from '../oauth-errors.js'\nimport { Sub } from '../oidc/sub.js'\nimport { InviteCode } from '../types/invite-code.js'\nimport { SignUpInput } from './sign-up-input.js'\n\n// Export all types needed to implement the AccountStore interface\n\nexport * from '../client/client-id.js'\nexport * from '../device/device-data.js'\nexport * from '../device/device-id.js'\nexport * from '../oidc/sub.js'\nexport * from '../request/request-id.js'\n\nexport type {\n Account,\n HcaptchaVerifyResult,\n InviteCode,\n OAuthScope,\n SignUpInput,\n}\n\nexport {\n HandleUnavailableError,\n InvalidRequestError,\n SecondAuthenticationFactorRequiredError,\n}\n\nexport type ResetPasswordRequestInput = InitiatePasswordResetInput\nexport type ResetPasswordConfirmInput = ConfirmResetPasswordInput\n\nexport type CreateAccountData = {\n locale: string\n email: string\n password: string\n handle: string\n inviteCode?: string | undefined\n}\n\nexport type AuthenticateAccountData = {\n locale: string\n password: string\n username: string\n emailOtp?: string | undefined\n}\n\nexport type AuthorizedClientData = { authorizedScopes: readonly string[] }\nexport type AuthorizedClients = Map<ClientId, AuthorizedClientData>\n\nexport type DeviceAccount = {\n deviceId: DeviceId\n\n /**\n * The data associated with the device, created through the\n * {@link DeviceStore}. This data is used to identify devices on which a user\n * has logged in.\n */\n deviceData: DeviceData\n\n /**\n * The account associated with the device account.\n */\n account: Account\n\n /**\n * The list of clients that are authorized by the account, as created through\n * the {@link AccountStore.setAuthorizedClient} method.\n */\n authorizedClients: AuthorizedClients\n\n /**\n * The date at which the device account was created. This value is currently\n * not used.\n */\n createdAt: Date\n\n /**\n * The date at which the device account was last updated. This value is used\n * to determine the date at which the user last authenticated on a device\n */\n updatedAt: Date\n}\n\nexport type SignUpData = SignUpInput & {\n hcaptchaResult?: HcaptchaVerifyResult\n inviteCode?: InviteCode\n}\n\nexport interface AccountStore {\n /**\n * @throws {HandleUnavailableError} - To indicate that the handle is already taken\n * @throws {InvalidRequestError} - To indicate that some data is invalid\n */\n createAccount(data: CreateAccountData): Awaitable<Account>\n\n /**\n * @throws {InvalidRequestError} - When the credentials are not valid\n * @throws {SecondAuthenticationFactorRequiredError} - To indicate that an {@link SecondAuthenticationFactorRequiredError.type} is required in the credentials\n */\n authenticateAccount(data: AuthenticateAccountData): Awaitable<Account>\n\n /**\n * Add a client & scopes to the list of authorized clients for the given account.\n */\n setAuthorizedClient(\n sub: Sub,\n clientId: ClientId,\n data: AuthorizedClientData,\n ): Awaitable<void>\n\n /**\n * @throws {InvalidRequestError} - When the credentials are not valid\n */\n getAccount(sub: Sub): Awaitable<{\n account: Account\n authorizedClients: AuthorizedClients\n }>\n\n /**\n * @param data.requestId - If provided, the inserted account must be bound to\n * that particular requestId.\n *\n * @note Whenever a particular device account is created, all **unbound**\n * device accounts for the same `deviceId` & `sub` should be deleted.\n *\n * @note When a particular request is deleted (through\n * {@link RequestStore.deleteRequest}), all accounts bound to that request\n * should be deleted as well.\n */\n upsertDeviceAccount(deviceId: DeviceId, sub: Sub): Awaitable<void>\n\n /**\n * @param requestId - If provided, the result must either have the same\n * requestId, or not be bound to a particular requestId. If `null`, the\n * result must not be bound to a particular requestId.\n * @throws {InvalidRequestError} - Instead of returning `null` in order to\n * provide a custom error message\n */\n getDeviceAccount(\n deviceId: DeviceId,\n sub: Sub,\n ): Awaitable<DeviceAccount | null>\n\n /**\n * Removes *all* the unbound device-accounts associated with the given device\n * & account.\n *\n * @note Noop if the device-account is not found.\n */\n removeDeviceAccount(deviceId: DeviceId, sub: Sub): Awaitable<void>\n\n /**\n * @returns **all** the device accounts that match the {@link requestId}\n * criteria and given {@link filter}.\n */\n listDeviceAccounts(\n filter: { sub: Sub } | { deviceId: DeviceId },\n ): Awaitable<DeviceAccount[]>\n\n resetPasswordRequest(data: ResetPasswordRequestInput): Awaitable<void>\n resetPasswordConfirm(data: ResetPasswordConfirmInput): Awaitable<void>\n\n /**\n * @throws {HandleUnavailableError} - To indicate that the handle is already taken\n */\n verifyHandleAvailability(handle: string): Awaitable<void>\n}\n\nexport const isAccountStore = buildInterfaceChecker<AccountStore>([\n 'createAccount',\n 'authenticateAccount',\n 'setAuthorizedClient',\n 'getAccount',\n 'upsertDeviceAccount',\n 'getDeviceAccount',\n 'removeDeviceAccount',\n 'listDeviceAccounts',\n 'resetPasswordRequest',\n 'resetPasswordConfirm',\n 'verifyHandleAvailability',\n])\n\nexport function asAccountStore<V>(implementation: V): V & AccountStore {\n if (!implementation || !isAccountStore(implementation)) {\n throw new Error('Invalid AccountStore implementation')\n }\n return implementation\n}\n"]}
1
+ {"version":3,"file":"account-store.js","sourceRoot":"","sources":["../../src/account/account-store.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAyMA,wCAKC;AApMD,iDAAsE;AACtE,wDAI2B;AAsBzB,uGAzBA,wCAAsB,OAyBA;AACtB,oGAzBA,qCAAmB,OAyBA;AACnB,wHAzBA,yDAAuC,OAyBA;AAnBzC,kEAAkE;AAElE,yDAAsC;AACtC,2DAAwC;AACxC,yDAAsC;AACtC,iDAA8B;AAC9B,2DAAwC;AAiK3B,QAAA,cAAc,GAAG,IAAA,+BAAqB,EAAe;IAChE,eAAe;IACf,qBAAqB;IACrB,qBAAqB;IACrB,YAAY;IACZ,qBAAqB;IACrB,kBAAkB;IAClB,qBAAqB;IACrB,oBAAoB;IACpB,sBAAsB;IACtB,sBAAsB;IACtB,0BAA0B;CAC3B,CAAC,CAAA;AAEF,SAAgB,cAAc,CAAI,cAAiB;IACjD,IAAI,CAAC,cAAc,IAAI,CAAC,IAAA,sBAAc,EAAC,cAAc,CAAC,EAAE,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;IACxD,CAAC;IACD,OAAO,cAAc,CAAA;AACvB,CAAC","sourcesContent":["import {\n Account,\n ConfirmResetPasswordInput,\n InitiatePasswordResetInput,\n} from '@atproto/oauth-provider-api'\nimport { OAuthScope } from '@atproto/oauth-types'\nimport { ClientId } from '../client/client-id.js'\nimport { DeviceId } from '../device/device-id.js'\nimport { DeviceData } from '../device/device-store.js'\nimport { HcaptchaVerifyResult } from '../lib/hcaptcha.js'\nimport { Awaitable, buildInterfaceChecker } from '../lib/util/type.js'\nimport {\n HandleUnavailableError,\n InvalidRequestError,\n SecondAuthenticationFactorRequiredError,\n} from '../oauth-errors.js'\nimport { Sub } from '../oidc/sub.js'\nimport { InviteCode } from '../types/invite-code.js'\nimport { SignUpInput } from './sign-up-input.js'\n\n// Export all types needed to implement the AccountStore interface\n\nexport * from '../client/client-id.js'\nexport * from '../device/device-data.js'\nexport * from '../device/device-id.js'\nexport * from '../oidc/sub.js'\nexport * from '../request/request-id.js'\n\nexport type {\n Account,\n HcaptchaVerifyResult,\n InviteCode,\n OAuthScope,\n SignUpInput,\n}\n\nexport {\n HandleUnavailableError,\n InvalidRequestError,\n SecondAuthenticationFactorRequiredError,\n}\n\nexport type ResetPasswordRequestInput = InitiatePasswordResetInput\nexport type ResetPasswordConfirmInput = ConfirmResetPasswordInput\n\nexport type CreateAccountData = {\n locale: string\n email: string\n password: string\n handle: string\n inviteCode?: string | undefined\n}\n\nexport type AuthenticateAccountData = {\n locale: string\n password: string\n username: string\n emailOtp?: string | undefined\n}\n\nexport type AuthorizedClientData = { authorizedScopes: readonly string[] }\nexport type AuthorizedClients = Map<ClientId, AuthorizedClientData>\n\nexport type DeviceAccount = {\n deviceId: DeviceId\n\n /**\n * The data associated with the device, created through the\n * {@link DeviceStore}. This data is used to identify devices on which a user\n * has logged in.\n */\n deviceData: DeviceData\n\n /**\n * The account associated with the device account.\n */\n account: Account\n\n /**\n * The list of clients that are authorized by the account, as created through\n * the {@link AccountStore.setAuthorizedClient} method.\n */\n authorizedClients: AuthorizedClients\n\n /**\n * The date at which the device account was created. This value is currently\n * not used.\n */\n createdAt: Date\n\n /**\n * The date at which the device account was last updated. This value is used\n * to determine the date at which the user last authenticated on a device\n */\n updatedAt: Date\n}\n\nexport type SignUpData = SignUpInput & {\n hcaptchaResult?: HcaptchaVerifyResult\n inviteCode?: InviteCode\n}\n\nexport interface AccountStore {\n /**\n * @throws {HandleUnavailableError} - To indicate that the handle is already taken\n * @throws {InvalidRequestError} - To indicate that some data is invalid\n */\n createAccount(data: CreateAccountData): Awaitable<Account>\n\n /**\n * @throws {InvalidRequestError} - When the credentials are not valid\n * @throws {SecondAuthenticationFactorRequiredError} - To indicate that an {@link SecondAuthenticationFactorRequiredError.type} is required in the credentials\n */\n authenticateAccount(data: AuthenticateAccountData): Awaitable<Account>\n\n /**\n * Add a client & scopes to the list of authorized clients for the given account.\n */\n setAuthorizedClient(\n sub: Sub,\n clientId: ClientId,\n data: AuthorizedClientData,\n ): Awaitable<void>\n\n /**\n * @throws {InvalidRequestError} - When the credentials are not valid\n */\n getAccount(sub: Sub): Awaitable<{\n account: Account\n authorizedClients: AuthorizedClients\n }>\n\n /**\n * @param data.requestId - If provided, the inserted account must be bound to\n * that particular requestId.\n *\n * @note Whenever a particular device account is created, all **unbound**\n * device accounts for the same `deviceId` & `sub` should be deleted.\n *\n * @note When a particular request is deleted (through\n * {@link RequestStore.deleteRequest}), all accounts bound to that request\n * should be deleted as well.\n */\n upsertDeviceAccount(deviceId: DeviceId, sub: Sub): Awaitable<void>\n\n /**\n * @param requestId - If provided, the result must either have the same\n * requestId, or not be bound to a particular requestId. If `null`, the\n * result must not be bound to a particular requestId.\n * @throws {InvalidRequestError} - Instead of returning `null` in order to\n * provide a custom error message\n */\n getDeviceAccount(\n deviceId: DeviceId,\n sub: Sub,\n ): Awaitable<DeviceAccount | null>\n\n /**\n * Removes *all* the unbound device-accounts associated with the given device\n * & account.\n *\n * @note Noop if the device-account is not found.\n */\n removeDeviceAccount(deviceId: DeviceId, sub: Sub): Awaitable<void>\n\n /**\n * @returns **all** the device accounts that match the {@link requestId}\n * criteria and given {@link filter}.\n */\n listDeviceAccounts(\n filter: { sub: Sub } | { deviceId: DeviceId },\n ): Awaitable<DeviceAccount[]>\n\n resetPasswordRequest(\n data: ResetPasswordRequestInput,\n ): Awaitable<null | Account>\n\n resetPasswordConfirm(\n data: ResetPasswordConfirmInput,\n ): Awaitable<null | Account>\n\n /**\n * @throws {HandleUnavailableError} - To indicate that the handle is already taken\n */\n verifyHandleAvailability(handle: string): Awaitable<void>\n}\n\nexport const isAccountStore = buildInterfaceChecker<AccountStore>([\n 'createAccount',\n 'authenticateAccount',\n 'setAuthorizedClient',\n 'getAccount',\n 'upsertDeviceAccount',\n 'getDeviceAccount',\n 'removeDeviceAccount',\n 'listDeviceAccounts',\n 'resetPasswordRequest',\n 'resetPasswordConfirm',\n 'verifyHandleAvailability',\n])\n\nexport function asAccountStore<V>(implementation: V): V & AccountStore {\n if (!implementation || !isAccountStore(implementation)) {\n throw new Error('Invalid AccountStore implementation')\n }\n return implementation\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"client-manager.d.ts","sourceRoot":"","sources":["../../src/client/client-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAiB,MAAM,cAAc,CAAA;AAC1D,OAAO,EACL,gCAAgC,EAChC,yBAAyB,EACzB,qBAAqB,EACrB,mBAAmB,EACnB,wBAAwB,EAKzB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,KAAK,EAKN,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EACL,YAAY,EAEZ,WAAW,EACZ,MAAM,4BAA4B,CAAA;AAInC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAepC,MAAM,MAAM,sBAAsB,GAAG,CACnC,GAAG,EAAE,MAAM,KACR,SAAS,CAAC,wBAAwB,CAAC,CAAA;AAExC,qBAAa,aAAa;IAKtB,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,gCAAgC;IACnE,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACjC,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,UAAU;IACpC,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAC5C,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,sBAAsB,GAAG,IAAI;IARpE,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACnD,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,YAAY,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;gBAGvD,cAAc,EAAE,gCAAgC,EAChD,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,WAAW,GAAG,IAAI,EACzB,gBAAgB,GAAE,sBAAsB,GAAG,IAAI,aAAO,EACzE,SAAS,EAAE,KAAK,EAChB,eAAe,EAAE,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,EAC1C,mBAAmB,EAAE,WAAW,CAAC,MAAM,EAAE,mBAAmB,CAAC;IAsB/D;;;OAGG;IACU,SAAS,CAAC,QAAQ,EAAE,QAAQ;IAiC5B,WAAW,CACtB,SAAS,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAC7B,EACE,OAEC,GACF,GAAE;QACD,OAAO,CAAC,EAAE,CACR,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,QAAQ,KACf,SAAS,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAA;KACrC,GACL,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;cAoBjB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,mBAAmB,CAAC;cAYf,yBAAyB,CACvC,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC,mBAAmB,CAAC;cA2Bf,6BAA6B,CAC3C,QAAQ,EAAE,yBAAyB,GAClC,OAAO,CAAC,mBAAmB,CAAC;cAYf,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,mBAAmB,CAAC;IAS/B;;;;;OAKG;IACH,SAAS,CAAC,sBAAsB,CAC9B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,mBAAmB,GAC5B,mBAAmB;IA8btB,8BAA8B,CAC5B,QAAQ,EAAE,qBAAqB,EAC/B,QAAQ,EAAE,mBAAmB,GAC5B,mBAAmB;IAuCtB,kCAAkC,CAChC,QAAQ,EAAE,yBAAyB,EACnC,QAAQ,EAAE,mBAAmB,GAC5B,mBAAmB;CAmEvB"}
1
+ {"version":3,"file":"client-manager.d.ts","sourceRoot":"","sources":["../../src/client/client-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAiB,MAAM,cAAc,CAAA;AAC1D,OAAO,EACL,gCAAgC,EAChC,yBAAyB,EACzB,qBAAqB,EACrB,mBAAmB,EACnB,wBAAwB,EAKzB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,KAAK,EAKN,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EACL,YAAY,EAEZ,WAAW,EACZ,MAAM,4BAA4B,CAAA;AAInC,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/C,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AAepC,MAAM,MAAM,sBAAsB,GAAG,CACnC,GAAG,EAAE,MAAM,KACR,SAAS,CAAC,wBAAwB,CAAC,CAAA;AAExC,qBAAa,aAAa;IAKtB,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,gCAAgC;IACnE,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACjC,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,UAAU;IACpC,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI;IAC5C,SAAS,CAAC,QAAQ,CAAC,gBAAgB,EAAE,sBAAsB,GAAG,IAAI;IARpE,SAAS,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACnD,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,YAAY,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAA;gBAGvD,cAAc,EAAE,gCAAgC,EAChD,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,WAAW,GAAG,IAAI,EACzB,gBAAgB,GAAE,sBAAsB,GAAG,IAAI,aAAO,EACzE,SAAS,EAAE,KAAK,EAChB,eAAe,EAAE,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,EAC1C,mBAAmB,EAAE,WAAW,CAAC,MAAM,EAAE,mBAAmB,CAAC;IAsB/D;;;OAGG;IACU,SAAS,CAAC,QAAQ,EAAE,QAAQ;IAiC5B,WAAW,CACtB,SAAS,EAAE,QAAQ,CAAC,QAAQ,CAAC,EAC7B,EACE,OAEC,GACF,GAAE;QACD,OAAO,CAAC,EAAE,CACR,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,QAAQ,KACf,SAAS,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAA;KACrC,GACL,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;cAoBjB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,mBAAmB,CAAC;cAYf,yBAAyB,CACvC,QAAQ,EAAE,qBAAqB,GAC9B,OAAO,CAAC,mBAAmB,CAAC;cA2Bf,6BAA6B,CAC3C,QAAQ,EAAE,yBAAyB,GAClC,OAAO,CAAC,mBAAmB,CAAC;cAYf,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,GACjB,OAAO,CAAC,mBAAmB,CAAC;IAS/B;;;;;OAKG;IACH,SAAS,CAAC,sBAAsB,CAC9B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,mBAAmB,GAC5B,mBAAmB;IAsZtB,8BAA8B,CAC5B,QAAQ,EAAE,qBAAqB,EAC/B,QAAQ,EAAE,mBAAmB,GAC5B,mBAAmB;IAuBtB,kCAAkC,CAChC,QAAQ,EAAE,yBAAyB,EACnC,QAAQ,EAAE,mBAAmB,GAC5B,mBAAmB;CAgFvB"}
@@ -4,7 +4,6 @@ exports.ClientManager = void 0;
4
4
  const jwk_1 = require("@atproto/jwk");
5
5
  const oauth_types_1 = require("@atproto/oauth-types");
6
6
  const fetch_1 = require("@atproto-labs/fetch");
7
- const fetch_node_1 = require("@atproto-labs/fetch-node");
8
7
  const pipe_1 = require("@atproto-labs/pipe");
9
8
  const simple_store_1 = require("@atproto-labs/simple-store");
10
9
  const invalid_client_metadata_error_js_1 = require("../errors/invalid-client-metadata-error.js");
@@ -13,7 +12,7 @@ const function_js_1 = require("../lib/util/function.js");
13
12
  const client_utils_js_1 = require("./client-utils.js");
14
13
  const client_js_1 = require("./client.js");
15
14
  const fetchMetadataHandler = (0, pipe_1.pipe)((0, fetch_1.fetchOkProcessor)(),
16
- // https://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html#section-4.1
15
+ // https://www.ietf.org/archive/id/draft-ietf-oauth-client-id-metadata-document-00.html#section-4.1
17
16
  (0, fetch_1.fetchJsonProcessor)('application/json', true), (0, fetch_1.fetchJsonZodProcessor)(oauth_types_1.oauthClientMetadataSchema));
18
17
  const fetchJwksHandler = (0, pipe_1.pipe)((0, fetch_1.fetchOkProcessor)(), (0, fetch_1.fetchJsonProcessor)('application/json', false), (0, fetch_1.fetchJsonZodProcessor)(jwk_1.jwksPubSchema));
19
18
  class ClientManager {
@@ -126,6 +125,9 @@ class ClientManager {
126
125
  * requirements.
127
126
  */
128
127
  validateClientMetadata(clientId, metadata) {
128
+ // @TODO This method should only check for rules that are specific to this
129
+ // implementation or the ATPROTO specification. All generic validation rules
130
+ // should be moved to the @atproto/oauth-types package.
129
131
  if (metadata.jwks && metadata.jwks_uri) {
130
132
  throw new invalid_client_metadata_error_js_1.InvalidClientMetadataError('jwks_uri and jwks are mutually exclusive');
131
133
  }
@@ -143,7 +145,7 @@ class ClientManager {
143
145
  const clientUriUrl = metadata.client_uri
144
146
  ? new URL(metadata.client_uri)
145
147
  : null;
146
- if (clientUriUrl && (0, fetch_node_1.isLocalHostname)(clientUriUrl.hostname)) {
148
+ if (clientUriUrl && (0, oauth_types_1.isLocalHostname)(clientUriUrl.hostname)) {
147
149
  throw new invalid_client_metadata_error_js_1.InvalidClientMetadataError('client_uri hostname is invalid');
148
150
  }
149
151
  const scopes = metadata.scope?.split(' ');
@@ -350,7 +352,7 @@ class ClientManager {
350
352
  throw new invalid_redirect_uri_error_js_1.InvalidRedirectUriError('Only loopback redirect URIs are allowed to use the "http" scheme');
351
353
  }
352
354
  case url.protocol === 'https:': {
353
- if ((0, fetch_node_1.isLocalHostname)(url.hostname)) {
355
+ if ((0, oauth_types_1.isLocalHostname)(url.hostname)) {
354
356
  throw new invalid_redirect_uri_error_js_1.InvalidRedirectUriError(`Redirect URI "${url}"'s domain name must not be a local hostname`);
355
357
  }
356
358
  // https://datatracker.ietf.org/doc/html/rfc8252#section-8.4
@@ -386,42 +388,9 @@ class ClientManager {
386
388
  break;
387
389
  }
388
390
  case isPrivateUseUriScheme(url): {
389
- // https://datatracker.ietf.org/doc/html/rfc8252#section-7.1
390
- //
391
- // > When choosing a URI scheme to associate with the app, apps MUST
392
- // > use a URI scheme based on a domain name under their control,
393
- // > expressed in reverse order, as recommended by Section 3.8 of
394
- // > [RFC7595] for private-use URI schemes.
395
391
  if (metadata.application_type !== 'native') {
396
392
  throw new invalid_redirect_uri_error_js_1.InvalidRedirectUriError(`Private-Use URI Scheme redirect URI are only allowed for native apps`);
397
393
  }
398
- // https://datatracker.ietf.org/doc/html/rfc8252#section-8.4
399
- //
400
- // > In addition to the collision-resistant properties, requiring a
401
- // > URI scheme based on a domain name that is under the control of
402
- // > the app can help to prove ownership in the event of a dispute
403
- // > where two apps claim the same private-use URI scheme (where one
404
- // > app is acting maliciously).
405
- //
406
- // We can't check for ownership here (as there is no concept of
407
- // proven ownership in the generic client validation), but we can
408
- // check that the domain is a valid domain name.
409
- const urlDomain = reverseDomain(url.protocol.slice(0, -1));
410
- if ((0, fetch_node_1.isLocalHostname)(urlDomain)) {
411
- throw new invalid_redirect_uri_error_js_1.InvalidRedirectUriError(`Private-use URI Scheme redirect URI must not be a local hostname`);
412
- }
413
- // https://datatracker.ietf.org/doc/html/rfc8252#section-7.1
414
- //
415
- // > Following the requirements of Section 3.2 of [RFC3986], as there
416
- // > is no naming authority for private-use URI scheme redirects, only
417
- // > a single slash ("/") appears after the scheme component.
418
- if (url.href.startsWith(`${url.protocol}//`) ||
419
- url.username ||
420
- url.password ||
421
- url.hostname ||
422
- url.port) {
423
- throw new invalid_redirect_uri_error_js_1.InvalidRedirectUriError(`Private-Use URI Scheme must be in the form ${url.protocol}/<path>`);
424
- }
425
394
  break;
426
395
  }
427
396
  default:
@@ -453,25 +422,16 @@ class ClientManager {
453
422
  if (method !== 'none') {
454
423
  throw new invalid_client_metadata_error_js_1.InvalidClientMetadataError(`Loopback clients are not allowed to use "token_endpoint_auth_method" ${method}`);
455
424
  }
456
- for (const redirectUri of metadata.redirect_uris) {
457
- const url = (0, client_utils_js_1.parseRedirectUri)(redirectUri);
458
- if (url.protocol !== 'http:') {
459
- throw new invalid_redirect_uri_error_js_1.InvalidRedirectUriError(`Loopback clients must use HTTP redirect URIs`);
460
- }
461
- if (!(0, oauth_types_1.isLoopbackHost)(url.hostname)) {
462
- throw new invalid_redirect_uri_error_js_1.InvalidRedirectUriError(`Loopback clients must use loopback redirect URIs`);
463
- }
464
- }
465
425
  return metadata;
466
426
  }
467
427
  validateDiscoverableClientMetadata(clientId, metadata) {
468
428
  if (!metadata.client_id) {
469
- // https://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html
429
+ // https://www.ietf.org/archive/id/draft-ietf-oauth-client-id-metadata-document-00.html
470
430
  throw new invalid_client_metadata_error_js_1.InvalidClientMetadataError(`client_id is required for discoverable clients`);
471
431
  }
472
432
  const clientIdUrl = (0, client_utils_js_1.parseDiscoverableClientId)(clientId);
473
433
  if (metadata.client_uri) {
474
- // https://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html
434
+ // https://www.ietf.org/archive/id/draft-ietf-oauth-client-id-metadata-document-00.html
475
435
  //
476
436
  // The client_uri must be a parent of the client_id URL. This might be
477
437
  // relaxed in the future.
@@ -488,8 +448,16 @@ class ClientManager {
488
448
  }
489
449
  }
490
450
  for (const redirectUri of metadata.redirect_uris) {
451
+ // @NOTE at this point, all redirect URIs have already been validated by
452
+ // oauthRedirectUriSchema
491
453
  const url = (0, client_utils_js_1.parseRedirectUri)(redirectUri);
492
454
  if (isPrivateUseUriScheme(url)) {
455
+ // https://datatracker.ietf.org/doc/html/rfc8252#section-7.1
456
+ //
457
+ // > When choosing a URI scheme to associate with the app, apps MUST use
458
+ // > a URI scheme based on a domain name under their control, expressed
459
+ // > in reverse order, as recommended by Section 3.8 of [RFC7595] for
460
+ // > private-use URI schemes.
493
461
  // https://datatracker.ietf.org/doc/html/rfc8252#section-8.4
494
462
  //
495
463
  // > In addition to the collision-resistant properties, requiring a
@@ -497,11 +465,14 @@ class ClientManager {
497
465
  // > the app can help to prove ownership in the event of a dispute
498
466
  // > where two apps claim the same private-use URI scheme (where one
499
467
  // > app is acting maliciously).
500
- // https://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html
468
+ // https://atproto.com/specs/oauth
501
469
  //
502
- // Fully qualified domain name (FQDN) of the client_id, in reverse
503
- // order. This could be relaxed to allow same apex domain names, or
504
- // parent domains, but for now we require an exact match.
470
+ // > Any custom scheme must match the client_id hostname in
471
+ // > reverse-domain order. The URI scheme must be followed by a single
472
+ // > colon (:) then a single forward slash (/) and then a URI path
473
+ // > component. For example, an app with client_id
474
+ // > https://app.example.com/client-metadata.json could have a
475
+ // > redirect_uri of com.example.app:/callback.
505
476
  const protocol = `${reverseDomain(clientIdUrl.hostname)}:`;
506
477
  if (url.protocol !== protocol) {
507
478
  throw new invalid_redirect_uri_error_js_1.InvalidRedirectUriError(`Private-Use URI Scheme redirect URI, for discoverable client metadata, must be the fully qualified domain name (FQDN) of the client_id, in reverse order (${protocol})`);