@atproto/oauth-provider 0.16.0 → 0.16.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 (37) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/account/account-manager.d.ts +2 -1
  3. package/dist/account/account-manager.d.ts.map +1 -1
  4. package/dist/account/account-manager.js +45 -4
  5. package/dist/account/account-manager.js.map +1 -1
  6. package/dist/account/account-store.d.ts +9 -3
  7. package/dist/account/account-store.d.ts.map +1 -1
  8. package/dist/account/account-store.js +2 -1
  9. package/dist/account/account-store.js.map +1 -1
  10. package/dist/errors/invalid-credentials-error.d.ts +24 -0
  11. package/dist/errors/invalid-credentials-error.d.ts.map +1 -0
  12. package/dist/errors/invalid-credentials-error.js +30 -0
  13. package/dist/errors/invalid-credentials-error.js.map +1 -0
  14. package/dist/oauth-errors.d.ts +1 -0
  15. package/dist/oauth-errors.d.ts.map +1 -1
  16. package/dist/oauth-errors.js +1 -0
  17. package/dist/oauth-errors.js.map +1 -1
  18. package/dist/oauth-hooks.d.ts +40 -1
  19. package/dist/oauth-hooks.d.ts.map +1 -1
  20. package/dist/oauth-hooks.js +3 -1
  21. package/dist/oauth-hooks.js.map +1 -1
  22. package/dist/request/request-manager.d.ts +7 -0
  23. package/dist/request/request-manager.d.ts.map +1 -1
  24. package/dist/request/request-manager.js +11 -0
  25. package/dist/request/request-manager.js.map +1 -1
  26. package/dist/router/create-api-middleware.d.ts.map +1 -1
  27. package/dist/router/create-api-middleware.js +6 -1
  28. package/dist/router/create-api-middleware.js.map +1 -1
  29. package/package.json +5 -5
  30. package/src/account/account-manager.ts +49 -6
  31. package/src/account/account-store.ts +9 -1
  32. package/src/errors/invalid-credentials-error.ts +29 -0
  33. package/src/oauth-errors.ts +1 -0
  34. package/src/oauth-hooks.ts +42 -0
  35. package/src/request/request-manager.ts +12 -0
  36. package/src/router/create-api-middleware.ts +7 -0
  37. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"request-manager.d.ts","sourceRoot":"","sources":["../../src/request/request-manager.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAA;AAE1D,OAAO,EACL,mCAAmC,EACnC,gCAAgC,EACjC,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AACrD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAO5C,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAQjD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAC5C,OAAO,EAAE,IAAI,EAAgB,MAAM,WAAW,CAAA;AAC9C,OAAO,EACL,qBAAqB,EAEtB,MAAM,mBAAmB,CAAA;AAE1B,OAAO,EAAE,YAAY,EAAqB,MAAM,oBAAoB,CAAA;AACpE,OAAO,EACL,UAAU,EAGX,MAAM,kBAAkB,CAAA;AAEzB,qBAAa,cAAc;IAEvB,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,YAAY;IACtC,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,cAAc;IACjD,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM;IACjC,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,gCAAgC;IAC7D,SAAS,CAAC,QAAQ,CAAC,KAAK,EAAE,UAAU;IACpC,SAAS,CAAC,QAAQ,CAAC,WAAW;gBALX,KAAK,EAAE,YAAY,EACnB,cAAc,EAAE,cAAc,EAC9B,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,gCAAgC,EAC1C,KAAK,EAAE,UAAU,EACjB,WAAW,SAAgB;IAGhD,SAAS,CAAC,iBAAiB;IAIrB,0BAA0B,CAC9B,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,IAAI,GAAG,UAAU,EAC7B,KAAK,EAAE,QAAQ,CAAC,mCAAmC,CAAC,EACpD,QAAQ,EAAE,IAAI,GAAG,QAAQ;;;;;;;;;;yBAqGV,CAAA;uBACqB,CAAC;yBAarC,CAVD;0BAA0C,CAAC;0BAU1C,CATD;;;;;;;;;;qBAY6C,CAAC;sBACvB,CAAC;yBACjB,CAAC;;;;;;;;;cA7FO,QAAQ,CACtB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,IAAI,GAAG,UAAU,EAC7B,UAAU,EAAE,QAAQ,CAAC,mCAAmC,CAAC,GACxD,OAAO,CAAC,QAAQ,CAAC,mCAAmC,CAAC,CAAC;IAkOnD,GAAG,CAAC,UAAU,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,QAAQ;;;;;;;;;;yBA5JzD,CAAA;uBACqB,CAAC;yBAarC,CAVD;0BAA0C,CAAC;0BAU1C,CATD;;;;;;;;;;qBAY6C,CAAC;sBACvB,CAAC;yBACjB,CAAC;;;;;;;;;;IAqMH,aAAa,CACjB,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,QAAQ,EAClB,cAAc,EAAE,eAAe,EAC/B,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,IAAI,CAAC;IAiFhB;;;OAGG;IACU,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,qBAAqB,CAAC;IA2B9D,MAAM,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;CAIpD"}
1
+ {"version":3,"file":"request-manager.d.ts","sourceRoot":"","sources":["../../src/request/request-manager.ts"],"names":[],"mappings":"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"}
@@ -210,6 +210,17 @@ class RequestManager {
210
210
  }
211
211
  return parameters;
212
212
  }
213
+ /**
214
+ * Reads the {@link ClientId} associated with a request URI without any of
215
+ * the validation or side-effects performed by {@link RequestManager.get}
216
+ *
217
+ * Returns `undefined` when no such request exists.
218
+ */
219
+ async peekClientId(requestUri) {
220
+ const requestId = (0, request_uri_js_1.decodeRequestUri)(requestUri);
221
+ const data = await this.store.readRequest(requestId);
222
+ return data?.clientId;
223
+ }
213
224
  async get(requestUri, deviceId, clientId) {
214
225
  const requestId = (0, request_uri_js_1.decodeRequestUri)(requestUri);
215
226
  const data = await this.store.readRequest(requestId);
@@ -1 +1 @@
1
- {"version":3,"file":"request-manager.js","sourceRoot":"","sources":["../../src/request/request-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAtprotoDid } from '@atproto/did'\nimport { LexResolverError } from '@atproto/lex-resolver'\nimport type { Account } from '@atproto/oauth-provider-api'\nimport { isAtprotoOauthScope } from '@atproto/oauth-scopes'\nimport {\n OAuthAuthorizationRequestParameters,\n OAuthAuthorizationServerMetadata,\n} from '@atproto/oauth-types'\nimport { isValidHandle } from '@atproto/syntax'\nimport { ClientAuth } from '../client/client-auth.js'\nimport { ClientId } from '../client/client-id.js'\nimport { Client } from '../client/client.js'\nimport {\n AUTHORIZATION_INACTIVITY_TIMEOUT,\n NODE_ENV,\n PAR_EXPIRES_IN,\n TOKEN_MAX_AGE,\n} from '../constants.js'\nimport { DeviceId } from '../device/device-id.js'\nimport { AccessDeniedError } from '../errors/access-denied-error.js'\nimport { AuthorizationError } from '../errors/authorization-error.js'\nimport { ConsentRequiredError } from '../errors/consent-required-error.js'\nimport { InvalidAuthorizationDetailsError } from '../errors/invalid-authorization-details-error.js'\nimport { InvalidGrantError } from '../errors/invalid-grant-error.js'\nimport { InvalidRequestError } from '../errors/invalid-request-error.js'\nimport { InvalidScopeError } from '../errors/invalid-scope-error.js'\nimport { LexiconManager } from '../lexicon/lexicon-manager.js'\nimport { RequestMetadata } from '../lib/http/request.js'\nimport { OAuthHooks } from '../oauth-hooks.js'\nimport { Signer } from '../signer/signer.js'\nimport { Code, generateCode } from './code.js'\nimport {\n RequestDataAuthorized,\n isRequestDataAuthorized,\n} from './request-data.js'\nimport { generateRequestId } from './request-id.js'\nimport { RequestStore, UpdateRequestData } from './request-store.js'\nimport {\n RequestUri,\n decodeRequestUri,\n encodeRequestUri,\n} from './request-uri.js'\n\nexport class RequestManager {\n constructor(\n protected readonly store: RequestStore,\n protected readonly lexiconManager: LexiconManager,\n protected readonly signer: Signer,\n protected readonly metadata: OAuthAuthorizationServerMetadata,\n protected readonly hooks: OAuthHooks,\n protected readonly tokenMaxAge = TOKEN_MAX_AGE,\n ) {}\n\n protected createTokenExpiry() {\n return new Date(Date.now() + this.tokenMaxAge)\n }\n\n async createAuthorizationRequest(\n client: Client,\n clientAuth: null | ClientAuth,\n input: Readonly<OAuthAuthorizationRequestParameters>,\n deviceId: null | DeviceId,\n ) {\n const parameters = await this.validate(client, clientAuth, input)\n\n await this.hooks.onAuthorizationRequest?.call(null, {\n client,\n clientAuth,\n parameters,\n })\n\n const expiresAt = new Date(Date.now() + PAR_EXPIRES_IN)\n const requestId = await generateRequestId()\n\n await this.store.createRequest(requestId, {\n clientId: client.id,\n clientAuth,\n parameters,\n expiresAt,\n deviceId,\n sub: null,\n code: null,\n })\n\n const requestUri = encodeRequestUri(requestId)\n return { requestUri, expiresAt, parameters }\n }\n\n protected async validate(\n client: Client,\n clientAuth: null | ClientAuth,\n parameters: Readonly<OAuthAuthorizationRequestParameters>,\n ): Promise<Readonly<OAuthAuthorizationRequestParameters>> {\n // -------------------------------\n // Validate unsupported parameters\n // -------------------------------\n\n for (const k of [\n // Known unsupported OIDC parameters\n 'claims',\n 'id_token_hint',\n 'nonce', // note that OIDC \"nonce\" is redundant with PKCE\n ] as const) {\n if (parameters[k] !== undefined) {\n throw new AuthorizationError(parameters, `Unsupported \"${k}\" parameter`)\n }\n }\n\n // -----------------------\n // Validate against server\n // -----------------------\n\n if (\n !this.metadata.response_types_supported?.includes(\n parameters.response_type,\n )\n ) {\n throw new AuthorizationError(\n parameters,\n `Unsupported response_type \"${parameters.response_type}\"`,\n 'unsupported_response_type',\n )\n }\n\n if (\n parameters.response_type === 'code' &&\n !this.metadata.grant_types_supported?.includes('authorization_code')\n ) {\n throw new AuthorizationError(\n parameters,\n `Unsupported grant_type \"authorization_code\"`,\n 'invalid_request',\n )\n }\n\n if (parameters.authorization_details) {\n for (const detail of parameters.authorization_details) {\n if (\n !this.metadata.authorization_details_types_supported?.includes(\n detail.type,\n )\n ) {\n throw new InvalidAuthorizationDetailsError(\n parameters,\n `Unsupported \"authorization_details\" type \"${detail.type}\"`,\n )\n }\n }\n }\n\n // -----------------------\n // Validate against client\n // -----------------------\n\n parameters = client.validateRequest(parameters)\n\n // -------------------\n // Validate parameters\n // -------------------\n\n if (!parameters.redirect_uri) {\n // Should already be ensured by client.validateRequest(). Adding here for\n // clarity & extra safety.\n throw new AuthorizationError(parameters, 'Missing \"redirect_uri\"')\n }\n\n // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-10#section-1.4.1\n // > The authorization server MAY fully or partially ignore the scope\n // > requested by the client, based on the authorization server policy or\n // > the resource owner's instructions. If the issued access token scope is\n // > different from the one requested by the client, the authorization\n // > server MUST include the scope response parameter in the token response\n // > (Section 3.2.3) to inform the client of the actual scope granted.\n\n // Let's make sure the scopes are unique (to reduce the token & storage\n // size).\n const scopes = new Set(parameters.scope?.split(' '))\n\n // @NOTE An app requesting a not yet supported list of scopes will need to\n // re-authenticate the user once the scopes are supported. This is due to\n // the fact that the AS does not know how to properly display those scopes\n // to the user, so it cannot properly ask for consent.\n const scope =\n Array.from(scopes).filter(isAtprotoOauthScope).join(' ') || undefined\n parameters = { ...parameters, scope }\n\n if (parameters.code_challenge) {\n switch (parameters.code_challenge_method) {\n case undefined:\n // https://datatracker.ietf.org/doc/html/rfc7636#section-4.3\n parameters = { ...parameters, code_challenge_method: 'plain' }\n // falls through\n case 'plain':\n case 'S256':\n break\n default: {\n throw new AuthorizationError(\n parameters,\n `Unsupported code_challenge_method \"${parameters.code_challenge_method}\"`,\n )\n }\n }\n } else {\n if (parameters.code_challenge_method) {\n // https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1\n throw new AuthorizationError(\n parameters,\n 'code_challenge is required when code_challenge_method is provided',\n )\n }\n\n // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-4.1.2.1\n //\n // > An AS MUST reject requests without a code_challenge from public\n // > clients, and MUST reject such requests from other clients unless\n // > there is reasonable assurance that the client mitigates\n // > authorization code injection in other ways. See Section 7.5.1 for\n // > details.\n //\n // > [...] In the specific deployment and the specific request, there is\n // > reasonable assurance by the authorization server that the client\n // > implements the OpenID Connect nonce mechanism properly.\n //\n // atproto does not implement the OpenID Connect nonce mechanism, so we\n // require the use of PKCE for all clients.\n\n throw new AuthorizationError(parameters, 'Use of PKCE is required')\n }\n\n // -----------------\n // atproto extension\n // -----------------\n\n if (parameters.response_type !== 'code') {\n throw new AuthorizationError(\n parameters,\n 'atproto only supports the \"code\" response_type',\n )\n }\n\n if (!scopes.has('atproto')) {\n throw new InvalidScopeError(parameters, 'The \"atproto\" scope is required')\n } else if (scopes.has('openid')) {\n throw new InvalidScopeError(\n parameters,\n 'OpenID Connect is not compatible with atproto',\n )\n }\n\n if (parameters.code_challenge_method !== 'S256') {\n throw new AuthorizationError(\n parameters,\n 'atproto requires use of \"S256\" code_challenge_method',\n )\n }\n\n // atproto extension: if the client is not trusted, and not authenticated,\n // force users to consent to authorization requests. We do this to avoid\n // unauthenticated clients from being able to silently re-authenticate\n // users.\n if (\n !client.info.isTrusted &&\n !client.info.isFirstParty &&\n client.metadata.token_endpoint_auth_method === 'none'\n ) {\n if (parameters.prompt === 'none') {\n throw new ConsentRequiredError(\n parameters,\n 'Public clients are not allowed to use silent-sign-on',\n )\n }\n\n // force \"consent\" for unauthenticated third party clients, unless they\n // are trying to create accounts:\n if (parameters.prompt !== 'create') {\n parameters = { ...parameters, prompt: 'consent' }\n }\n }\n\n // atproto extension: ensure that the login_hint is a valid handle or DID\n // @NOTE we to allow invalid case here, which is not spec'd anywhere.\n const hint = parameters.login_hint?.toLowerCase()\n if (hint) {\n if (!isAtprotoDid(hint) && !isValidHandle(hint)) {\n throw new AuthorizationError(parameters, `Invalid login_hint \"${hint}\"`)\n }\n\n // @TODO: ensure that the account actually exists on this server (there is\n // no point in showing the UI to the user if the account does not exist).\n\n // Update the parameters to ensure the right case is used\n parameters = { ...parameters, login_hint: hint }\n }\n\n // Make sure that every nsid in the scope resolves to a valid permission set\n // lexicon\n if (parameters.scope) {\n try {\n await this.lexiconManager.getPermissionSetsFromScope(parameters.scope)\n } catch (err) {\n // Parse expected errors\n if (err instanceof LexResolverError) {\n throw new AuthorizationError(\n parameters,\n err.message,\n 'invalid_scope',\n err,\n )\n }\n\n // Unexpected error\n throw err\n }\n }\n\n return parameters\n }\n\n async get(requestUri: RequestUri, deviceId?: DeviceId, clientId?: ClientId) {\n const requestId = decodeRequestUri(requestUri)\n\n const data = await this.store.readRequest(requestId)\n if (!data) throw new InvalidRequestError('Unknown request_uri')\n\n const updates: UpdateRequestData = {}\n\n try {\n if (data.sub || data.code) {\n // If an account was linked to the request, the next step is to exchange\n // the code for a token.\n throw new AccessDeniedError(\n data.parameters,\n 'This request was already authorized',\n )\n }\n\n if (data.expiresAt < new Date()) {\n throw new AccessDeniedError(data.parameters, 'This request has expired')\n } else {\n updates.expiresAt = new Date(\n Date.now() + AUTHORIZATION_INACTIVITY_TIMEOUT,\n )\n }\n\n if (clientId != null && data.clientId !== clientId) {\n throw new AccessDeniedError(\n data.parameters,\n 'This request was initiated for another client',\n )\n }\n\n if (deviceId != null) {\n if (!data.deviceId) {\n updates.deviceId = deviceId\n } else if (data.deviceId !== deviceId) {\n throw new AccessDeniedError(\n data.parameters,\n 'This request was initiated from another device',\n )\n }\n }\n } catch (err) {\n await this.store.deleteRequest(requestId)\n throw err\n }\n\n if (Object.keys(updates).length > 0) {\n await this.store.updateRequest(requestId, updates)\n }\n\n return {\n requestUri,\n expiresAt: updates.expiresAt || data.expiresAt,\n parameters: data.parameters,\n clientId: data.clientId,\n }\n }\n\n async setAuthorized(\n requestUri: RequestUri,\n client: Client,\n account: Account,\n deviceId: DeviceId,\n deviceMetadata: RequestMetadata,\n scopeOverride?: string,\n ): Promise<Code> {\n const requestId = decodeRequestUri(requestUri)\n\n const data = await this.store.readRequest(requestId)\n if (!data) throw new InvalidRequestError('Unknown request_uri')\n\n let { parameters } = data\n\n try {\n if (data.expiresAt < new Date()) {\n throw new AccessDeniedError(parameters, 'This request has expired')\n }\n if (!data.deviceId) {\n throw new AccessDeniedError(\n parameters,\n 'This request was not initiated',\n )\n }\n if (data.deviceId !== deviceId) {\n throw new AccessDeniedError(\n parameters,\n 'This request was initiated from another device',\n )\n }\n if (data.sub || data.code) {\n throw new AccessDeniedError(\n parameters,\n 'This request was already authorized',\n )\n }\n\n // If a new scope value is provided, update the parameters by ensuring\n // that every existing scope in the parameters is also present in the\n // override value. This allows the user to remove scopes from the request,\n // but not to add new ones.\n if (scopeOverride != null) {\n const allowedScopes = new Set(scopeOverride.split(' '))\n const existingScopes = parameters.scope?.split(' ')\n\n // Compute the intersection of the existing scopes and the overrides.\n const newScopes = existingScopes?.filter((s) => allowedScopes.has(s))\n\n // Validate: make sure the new scopes are valid\n if (!newScopes?.includes('atproto')) {\n throw new AccessDeniedError(\n parameters,\n 'The \"atproto\" scope is required',\n )\n }\n\n parameters = { ...parameters, scope: newScopes.join(' ') }\n }\n\n // Only response_type=code is supported\n const code = await generateCode()\n\n // Bind the request to the account, preventing it from being used again.\n await this.store.updateRequest(requestId, {\n sub: account.sub,\n code,\n // Allow the client to exchange the code for a token within the next 60 seconds.\n expiresAt: new Date(Date.now() + AUTHORIZATION_INACTIVITY_TIMEOUT),\n parameters,\n })\n\n await this.hooks.onAuthorized?.call(null, {\n client,\n account,\n parameters,\n deviceId,\n deviceMetadata,\n requestId,\n })\n\n return code\n } catch (err) {\n await this.store.deleteRequest(requestId)\n throw err\n }\n }\n\n /**\n * @note If this method throws an error, any token previously generated from\n * the same `code` **must** me revoked.\n */\n public async consumeCode(code: Code): Promise<RequestDataAuthorized> {\n const result = await this.store.consumeRequestCode(code)\n if (!result) throw new InvalidGrantError('Invalid code')\n\n const { requestId, data } = result\n\n // Fool-proofing the store implementation against code replay attacks (in\n // case consumeRequestCode() does not delete the request).\n if (NODE_ENV !== 'production') {\n const result = await this.store.readRequest(requestId)\n if (result) {\n throw new Error('Invalid store implementation: request not deleted')\n }\n }\n\n if (!isRequestDataAuthorized(data) || data.code !== code) {\n // Should never happen: maybe the store implementation is faulty ?\n throw new Error('Unexpected request state')\n }\n\n if (data.expiresAt < new Date()) {\n throw new InvalidGrantError('This code has expired')\n }\n\n return data\n }\n\n async delete(requestUri: RequestUri): Promise<void> {\n const requestId = decodeRequestUri(requestUri)\n await this.store.deleteRequest(requestId)\n }\n}\n"]}
1
+ {"version":3,"file":"request-manager.js","sourceRoot":"","sources":["../../src/request/request-manager.ts"],"names":[],"mappings":"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","sourcesContent":["import { isAtprotoDid } from '@atproto/did'\nimport { LexResolverError } from '@atproto/lex-resolver'\nimport type { Account } from '@atproto/oauth-provider-api'\nimport { isAtprotoOauthScope } from '@atproto/oauth-scopes'\nimport {\n OAuthAuthorizationRequestParameters,\n OAuthAuthorizationServerMetadata,\n} from '@atproto/oauth-types'\nimport { isValidHandle } from '@atproto/syntax'\nimport { ClientAuth } from '../client/client-auth.js'\nimport { ClientId } from '../client/client-id.js'\nimport { Client } from '../client/client.js'\nimport {\n AUTHORIZATION_INACTIVITY_TIMEOUT,\n NODE_ENV,\n PAR_EXPIRES_IN,\n TOKEN_MAX_AGE,\n} from '../constants.js'\nimport { DeviceId } from '../device/device-id.js'\nimport { AccessDeniedError } from '../errors/access-denied-error.js'\nimport { AuthorizationError } from '../errors/authorization-error.js'\nimport { ConsentRequiredError } from '../errors/consent-required-error.js'\nimport { InvalidAuthorizationDetailsError } from '../errors/invalid-authorization-details-error.js'\nimport { InvalidGrantError } from '../errors/invalid-grant-error.js'\nimport { InvalidRequestError } from '../errors/invalid-request-error.js'\nimport { InvalidScopeError } from '../errors/invalid-scope-error.js'\nimport { LexiconManager } from '../lexicon/lexicon-manager.js'\nimport { RequestMetadata } from '../lib/http/request.js'\nimport { OAuthHooks } from '../oauth-hooks.js'\nimport { Signer } from '../signer/signer.js'\nimport { Code, generateCode } from './code.js'\nimport {\n RequestDataAuthorized,\n isRequestDataAuthorized,\n} from './request-data.js'\nimport { generateRequestId } from './request-id.js'\nimport { RequestStore, UpdateRequestData } from './request-store.js'\nimport {\n RequestUri,\n decodeRequestUri,\n encodeRequestUri,\n} from './request-uri.js'\n\nexport class RequestManager {\n constructor(\n protected readonly store: RequestStore,\n protected readonly lexiconManager: LexiconManager,\n protected readonly signer: Signer,\n protected readonly metadata: OAuthAuthorizationServerMetadata,\n protected readonly hooks: OAuthHooks,\n protected readonly tokenMaxAge = TOKEN_MAX_AGE,\n ) {}\n\n protected createTokenExpiry() {\n return new Date(Date.now() + this.tokenMaxAge)\n }\n\n async createAuthorizationRequest(\n client: Client,\n clientAuth: null | ClientAuth,\n input: Readonly<OAuthAuthorizationRequestParameters>,\n deviceId: null | DeviceId,\n ) {\n const parameters = await this.validate(client, clientAuth, input)\n\n await this.hooks.onAuthorizationRequest?.call(null, {\n client,\n clientAuth,\n parameters,\n })\n\n const expiresAt = new Date(Date.now() + PAR_EXPIRES_IN)\n const requestId = await generateRequestId()\n\n await this.store.createRequest(requestId, {\n clientId: client.id,\n clientAuth,\n parameters,\n expiresAt,\n deviceId,\n sub: null,\n code: null,\n })\n\n const requestUri = encodeRequestUri(requestId)\n return { requestUri, expiresAt, parameters }\n }\n\n protected async validate(\n client: Client,\n clientAuth: null | ClientAuth,\n parameters: Readonly<OAuthAuthorizationRequestParameters>,\n ): Promise<Readonly<OAuthAuthorizationRequestParameters>> {\n // -------------------------------\n // Validate unsupported parameters\n // -------------------------------\n\n for (const k of [\n // Known unsupported OIDC parameters\n 'claims',\n 'id_token_hint',\n 'nonce', // note that OIDC \"nonce\" is redundant with PKCE\n ] as const) {\n if (parameters[k] !== undefined) {\n throw new AuthorizationError(parameters, `Unsupported \"${k}\" parameter`)\n }\n }\n\n // -----------------------\n // Validate against server\n // -----------------------\n\n if (\n !this.metadata.response_types_supported?.includes(\n parameters.response_type,\n )\n ) {\n throw new AuthorizationError(\n parameters,\n `Unsupported response_type \"${parameters.response_type}\"`,\n 'unsupported_response_type',\n )\n }\n\n if (\n parameters.response_type === 'code' &&\n !this.metadata.grant_types_supported?.includes('authorization_code')\n ) {\n throw new AuthorizationError(\n parameters,\n `Unsupported grant_type \"authorization_code\"`,\n 'invalid_request',\n )\n }\n\n if (parameters.authorization_details) {\n for (const detail of parameters.authorization_details) {\n if (\n !this.metadata.authorization_details_types_supported?.includes(\n detail.type,\n )\n ) {\n throw new InvalidAuthorizationDetailsError(\n parameters,\n `Unsupported \"authorization_details\" type \"${detail.type}\"`,\n )\n }\n }\n }\n\n // -----------------------\n // Validate against client\n // -----------------------\n\n parameters = client.validateRequest(parameters)\n\n // -------------------\n // Validate parameters\n // -------------------\n\n if (!parameters.redirect_uri) {\n // Should already be ensured by client.validateRequest(). Adding here for\n // clarity & extra safety.\n throw new AuthorizationError(parameters, 'Missing \"redirect_uri\"')\n }\n\n // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-10#section-1.4.1\n // > The authorization server MAY fully or partially ignore the scope\n // > requested by the client, based on the authorization server policy or\n // > the resource owner's instructions. If the issued access token scope is\n // > different from the one requested by the client, the authorization\n // > server MUST include the scope response parameter in the token response\n // > (Section 3.2.3) to inform the client of the actual scope granted.\n\n // Let's make sure the scopes are unique (to reduce the token & storage\n // size).\n const scopes = new Set(parameters.scope?.split(' '))\n\n // @NOTE An app requesting a not yet supported list of scopes will need to\n // re-authenticate the user once the scopes are supported. This is due to\n // the fact that the AS does not know how to properly display those scopes\n // to the user, so it cannot properly ask for consent.\n const scope =\n Array.from(scopes).filter(isAtprotoOauthScope).join(' ') || undefined\n parameters = { ...parameters, scope }\n\n if (parameters.code_challenge) {\n switch (parameters.code_challenge_method) {\n case undefined:\n // https://datatracker.ietf.org/doc/html/rfc7636#section-4.3\n parameters = { ...parameters, code_challenge_method: 'plain' }\n // falls through\n case 'plain':\n case 'S256':\n break\n default: {\n throw new AuthorizationError(\n parameters,\n `Unsupported code_challenge_method \"${parameters.code_challenge_method}\"`,\n )\n }\n }\n } else {\n if (parameters.code_challenge_method) {\n // https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1\n throw new AuthorizationError(\n parameters,\n 'code_challenge is required when code_challenge_method is provided',\n )\n }\n\n // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-4.1.2.1\n //\n // > An AS MUST reject requests without a code_challenge from public\n // > clients, and MUST reject such requests from other clients unless\n // > there is reasonable assurance that the client mitigates\n // > authorization code injection in other ways. See Section 7.5.1 for\n // > details.\n //\n // > [...] In the specific deployment and the specific request, there is\n // > reasonable assurance by the authorization server that the client\n // > implements the OpenID Connect nonce mechanism properly.\n //\n // atproto does not implement the OpenID Connect nonce mechanism, so we\n // require the use of PKCE for all clients.\n\n throw new AuthorizationError(parameters, 'Use of PKCE is required')\n }\n\n // -----------------\n // atproto extension\n // -----------------\n\n if (parameters.response_type !== 'code') {\n throw new AuthorizationError(\n parameters,\n 'atproto only supports the \"code\" response_type',\n )\n }\n\n if (!scopes.has('atproto')) {\n throw new InvalidScopeError(parameters, 'The \"atproto\" scope is required')\n } else if (scopes.has('openid')) {\n throw new InvalidScopeError(\n parameters,\n 'OpenID Connect is not compatible with atproto',\n )\n }\n\n if (parameters.code_challenge_method !== 'S256') {\n throw new AuthorizationError(\n parameters,\n 'atproto requires use of \"S256\" code_challenge_method',\n )\n }\n\n // atproto extension: if the client is not trusted, and not authenticated,\n // force users to consent to authorization requests. We do this to avoid\n // unauthenticated clients from being able to silently re-authenticate\n // users.\n if (\n !client.info.isTrusted &&\n !client.info.isFirstParty &&\n client.metadata.token_endpoint_auth_method === 'none'\n ) {\n if (parameters.prompt === 'none') {\n throw new ConsentRequiredError(\n parameters,\n 'Public clients are not allowed to use silent-sign-on',\n )\n }\n\n // force \"consent\" for unauthenticated third party clients, unless they\n // are trying to create accounts:\n if (parameters.prompt !== 'create') {\n parameters = { ...parameters, prompt: 'consent' }\n }\n }\n\n // atproto extension: ensure that the login_hint is a valid handle or DID\n // @NOTE we to allow invalid case here, which is not spec'd anywhere.\n const hint = parameters.login_hint?.toLowerCase()\n if (hint) {\n if (!isAtprotoDid(hint) && !isValidHandle(hint)) {\n throw new AuthorizationError(parameters, `Invalid login_hint \"${hint}\"`)\n }\n\n // @TODO: ensure that the account actually exists on this server (there is\n // no point in showing the UI to the user if the account does not exist).\n\n // Update the parameters to ensure the right case is used\n parameters = { ...parameters, login_hint: hint }\n }\n\n // Make sure that every nsid in the scope resolves to a valid permission set\n // lexicon\n if (parameters.scope) {\n try {\n await this.lexiconManager.getPermissionSetsFromScope(parameters.scope)\n } catch (err) {\n // Parse expected errors\n if (err instanceof LexResolverError) {\n throw new AuthorizationError(\n parameters,\n err.message,\n 'invalid_scope',\n err,\n )\n }\n\n // Unexpected error\n throw err\n }\n }\n\n return parameters\n }\n\n /**\n * Reads the {@link ClientId} associated with a request URI without any of\n * the validation or side-effects performed by {@link RequestManager.get}\n *\n * Returns `undefined` when no such request exists.\n */\n async peekClientId(requestUri: RequestUri): Promise<ClientId | undefined> {\n const requestId = decodeRequestUri(requestUri)\n const data = await this.store.readRequest(requestId)\n return data?.clientId\n }\n\n async get(requestUri: RequestUri, deviceId?: DeviceId, clientId?: ClientId) {\n const requestId = decodeRequestUri(requestUri)\n\n const data = await this.store.readRequest(requestId)\n if (!data) throw new InvalidRequestError('Unknown request_uri')\n\n const updates: UpdateRequestData = {}\n\n try {\n if (data.sub || data.code) {\n // If an account was linked to the request, the next step is to exchange\n // the code for a token.\n throw new AccessDeniedError(\n data.parameters,\n 'This request was already authorized',\n )\n }\n\n if (data.expiresAt < new Date()) {\n throw new AccessDeniedError(data.parameters, 'This request has expired')\n } else {\n updates.expiresAt = new Date(\n Date.now() + AUTHORIZATION_INACTIVITY_TIMEOUT,\n )\n }\n\n if (clientId != null && data.clientId !== clientId) {\n throw new AccessDeniedError(\n data.parameters,\n 'This request was initiated for another client',\n )\n }\n\n if (deviceId != null) {\n if (!data.deviceId) {\n updates.deviceId = deviceId\n } else if (data.deviceId !== deviceId) {\n throw new AccessDeniedError(\n data.parameters,\n 'This request was initiated from another device',\n )\n }\n }\n } catch (err) {\n await this.store.deleteRequest(requestId)\n throw err\n }\n\n if (Object.keys(updates).length > 0) {\n await this.store.updateRequest(requestId, updates)\n }\n\n return {\n requestUri,\n expiresAt: updates.expiresAt || data.expiresAt,\n parameters: data.parameters,\n clientId: data.clientId,\n }\n }\n\n async setAuthorized(\n requestUri: RequestUri,\n client: Client,\n account: Account,\n deviceId: DeviceId,\n deviceMetadata: RequestMetadata,\n scopeOverride?: string,\n ): Promise<Code> {\n const requestId = decodeRequestUri(requestUri)\n\n const data = await this.store.readRequest(requestId)\n if (!data) throw new InvalidRequestError('Unknown request_uri')\n\n let { parameters } = data\n\n try {\n if (data.expiresAt < new Date()) {\n throw new AccessDeniedError(parameters, 'This request has expired')\n }\n if (!data.deviceId) {\n throw new AccessDeniedError(\n parameters,\n 'This request was not initiated',\n )\n }\n if (data.deviceId !== deviceId) {\n throw new AccessDeniedError(\n parameters,\n 'This request was initiated from another device',\n )\n }\n if (data.sub || data.code) {\n throw new AccessDeniedError(\n parameters,\n 'This request was already authorized',\n )\n }\n\n // If a new scope value is provided, update the parameters by ensuring\n // that every existing scope in the parameters is also present in the\n // override value. This allows the user to remove scopes from the request,\n // but not to add new ones.\n if (scopeOverride != null) {\n const allowedScopes = new Set(scopeOverride.split(' '))\n const existingScopes = parameters.scope?.split(' ')\n\n // Compute the intersection of the existing scopes and the overrides.\n const newScopes = existingScopes?.filter((s) => allowedScopes.has(s))\n\n // Validate: make sure the new scopes are valid\n if (!newScopes?.includes('atproto')) {\n throw new AccessDeniedError(\n parameters,\n 'The \"atproto\" scope is required',\n )\n }\n\n parameters = { ...parameters, scope: newScopes.join(' ') }\n }\n\n // Only response_type=code is supported\n const code = await generateCode()\n\n // Bind the request to the account, preventing it from being used again.\n await this.store.updateRequest(requestId, {\n sub: account.sub,\n code,\n // Allow the client to exchange the code for a token within the next 60 seconds.\n expiresAt: new Date(Date.now() + AUTHORIZATION_INACTIVITY_TIMEOUT),\n parameters,\n })\n\n await this.hooks.onAuthorized?.call(null, {\n client,\n account,\n parameters,\n deviceId,\n deviceMetadata,\n requestId,\n })\n\n return code\n } catch (err) {\n await this.store.deleteRequest(requestId)\n throw err\n }\n }\n\n /**\n * @note If this method throws an error, any token previously generated from\n * the same `code` **must** me revoked.\n */\n public async consumeCode(code: Code): Promise<RequestDataAuthorized> {\n const result = await this.store.consumeRequestCode(code)\n if (!result) throw new InvalidGrantError('Invalid code')\n\n const { requestId, data } = result\n\n // Fool-proofing the store implementation against code replay attacks (in\n // case consumeRequestCode() does not delete the request).\n if (NODE_ENV !== 'production') {\n const result = await this.store.readRequest(requestId)\n if (result) {\n throw new Error('Invalid store implementation: request not deleted')\n }\n }\n\n if (!isRequestDataAuthorized(data) || data.code !== code) {\n // Should never happen: maybe the store implementation is faulty ?\n throw new Error('Unexpected request state')\n }\n\n if (data.expiresAt < new Date()) {\n throw new InvalidGrantError('This code has expired')\n }\n\n return data\n }\n\n async delete(requestUri: RequestUri): Promise<void> {\n const requestId = decodeRequestUri(requestUri)\n await this.store.deleteRequest(requestId)\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"create-api-middleware.d.ts","sourceRoot":"","sources":["../../src/router/create-api-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AA8BhE,OAAO,EAEL,UAAU,EAaX,MAAM,sBAAsB,CAAA;AAK7B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAUzD,OAAO,EAEL,oBAAoB,EAMrB,MAAM,2BAA2B,CAAA;AAClC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAIhE,wBAAgB,mBAAmB,CACjC,GAAG,SAAS,MAAM,GAAG,IAAI,GAAG,IAAI,EAChC,GAAG,SAAS,eAAe,GAAG,eAAe,EAC7C,GAAG,SAAS,cAAc,GAAG,cAAc,EAE3C,MAAM,EAAE,aAAa,EACrB,EAAE,OAAO,EAAE,EAAE,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,GACvC,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CA8rB3B;AA2BD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,GAAG,oBAAoB,CA4C/D"}
1
+ {"version":3,"file":"create-api-middleware.d.ts","sourceRoot":"","sources":["../../src/router/create-api-middleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AA8BhE,OAAO,EAEL,UAAU,EAaX,MAAM,sBAAsB,CAAA;AAK7B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAUzD,OAAO,EAEL,oBAAoB,EAMrB,MAAM,2BAA2B,CAAA;AAClC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAIhE,wBAAgB,mBAAmB,CACjC,GAAG,SAAS,MAAM,GAAG,IAAI,GAAG,IAAI,EAChC,GAAG,SAAS,eAAe,GAAG,eAAe,EAC7C,GAAG,SAAS,cAAc,GAAG,cAAc,EAE3C,MAAM,EAAE,aAAa,EACrB,EAAE,OAAO,EAAE,EAAE,iBAAiB,CAAC,GAAG,EAAE,GAAG,CAAC,GACvC,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAqsB3B;AA2BD,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,GAAG,GAAG,oBAAoB,CA4C/D"}
@@ -79,7 +79,12 @@ function createApiMiddleware(server, { onError }) {
79
79
  const { deviceId, deviceMetadata, requestUri } = this;
80
80
  // Remember when not in the context of a request by default
81
81
  const { remember = requestUri == null, ...input } = this.input;
82
- const account = await server.accountManager.authenticateAccount(deviceId, deviceMetadata, input);
82
+ // Look up the client identifier associated with the pending OAuth
83
+ // request, if any, so it can be surfaced to the sign-in hooks.
84
+ const clientId = requestUri
85
+ ? await server.requestManager.peekClientId(requestUri)
86
+ : undefined;
87
+ const account = await server.accountManager.authenticateAccount(deviceId, deviceMetadata, input, clientId);
83
88
  if (remember) {
84
89
  await server.accountManager.upsertDeviceAccount(deviceId, account.sub);
85
90
  }
@@ -1 +1 @@
1
- {"version":3,"file":"create-api-middleware.js","sourceRoot":"","sources":["../../src/router/create-api-middleware.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport createHttpError from 'http-errors'\nimport { z } from 'zod'\nimport { signedJwtSchema } from '@atproto/jwk'\nimport {\n API_ENDPOINT_PREFIX,\n ActiveAccountSession,\n ActiveDeviceSession,\n ActiveOAuthSession,\n ApiEndpoints,\n ISODateString,\n} from '@atproto/oauth-provider-api'\nimport {\n OAuthAuthorizationRequestParameters,\n OAuthRedirectUri,\n OAuthResponseMode,\n oauthRedirectUriSchema,\n oauthResponseModeSchema,\n} from '@atproto/oauth-types'\nimport { signInDataSchema } from '../account/sign-in-data.js'\nimport { signUpInputSchema } from '../account/sign-up-input.js'\nimport { DeviceId, deviceIdSchema } from '../device/device-id.js'\nimport { AuthorizationError } from '../errors/authorization-error.js'\nimport {\n ErrorPayload,\n buildErrorPayload,\n buildErrorStatus,\n} from '../errors/error-parser.js'\nimport { InvalidRequestError } from '../errors/invalid-request-error.js'\nimport { WWWAuthenticateError } from '../errors/www-authenticate-error.js'\nimport {\n JsonResponse,\n Middleware,\n RequestMetadata,\n Router,\n RouterCtx,\n SubCtx,\n flushStream,\n jsonHandler,\n parseHttpRequest,\n subCtx,\n validateFetchMode,\n validateFetchSite,\n validateOrigin,\n validateReferrer,\n} from '../lib/http/index.js'\nimport { RouteCtx, createRoute } from '../lib/http/route.js'\nimport { asArray } from '../lib/util/cast.js'\nimport { localeSchema } from '../lib/util/locale.js'\nimport type { Awaitable } from '../lib/util/type.js'\nimport type { OAuthProvider } from '../oauth-provider.js'\nimport { Sub, subSchema } from '../oidc/sub.js'\nimport { RequestUri, requestUriSchema } from '../request/request-uri.js'\nimport { AuthorizationRedirectParameters } from '../result/authorization-redirect-parameters.js'\nimport { tokenIdSchema } from '../token/token-id.js'\nimport { emailOtpSchema } from '../types/email-otp.js'\nimport { emailSchema } from '../types/email.js'\nimport { handleSchema } from '../types/handle.js'\nimport { newPasswordSchema } from '../types/password.js'\nimport { validateCsrfToken } from './assets/csrf.js'\nimport {\n ERROR_REDIRECT_KEYS,\n OAuthRedirectOptions,\n OAuthRedirectQueryParameter,\n SUCCESS_REDIRECT_KEYS,\n buildRedirectMode,\n buildRedirectParams,\n buildRedirectUri,\n} from './assets/send-redirect.js'\nimport type { MiddlewareOptions } from './middleware-options.js'\n\nconst verifyHandleSchema = z.object({ handle: handleSchema }).strict()\n\nexport function createApiMiddleware<\n Ctx extends object | void = void,\n Req extends IncomingMessage = IncomingMessage,\n Res extends ServerResponse = ServerResponse,\n>(\n server: OAuthProvider,\n { onError }: MiddlewareOptions<Req, Res>,\n): Middleware<Ctx, Req, Res> {\n const issuerUrl = new URL(server.issuer)\n const issuerOrigin = issuerUrl.origin\n const router = new Router<Ctx, Req, Res>(issuerUrl)\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/verify-handle-availability',\n schema: verifyHandleSchema,\n async handler() {\n await server.accountManager.verifyHandleAvailability(this.input.handle)\n return { json: { available: true } }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/sign-up',\n schema: signUpInputSchema,\n rotateDeviceCookies: true,\n async handler() {\n const { deviceId, deviceMetadata, input, requestUri } = this\n\n const account = await server.accountManager.createAccount(\n deviceId,\n deviceMetadata,\n input,\n )\n\n // Remember when not in the context of a request by default\n const remember = requestUri == null\n\n // Only \"remember\" the newly created account if it was not created during an\n // OAuth flow.\n if (remember) {\n await server.accountManager.upsertDeviceAccount(deviceId, account.sub)\n }\n\n const ephemeralToken = remember\n ? undefined\n : await server.signer.createEphemeralToken({\n sub: account.sub,\n deviceId,\n requestUri: this.requestUri,\n })\n\n const json = { account, ephemeralToken }\n return { json }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/sign-in',\n schema: signInDataSchema.extend({ remember: z.boolean().optional() }),\n rotateDeviceCookies: true,\n async handler() {\n const { deviceId, deviceMetadata, requestUri } = this\n\n // Remember when not in the context of a request by default\n const { remember = requestUri == null, ...input } = this.input\n\n const account = await server.accountManager.authenticateAccount(\n deviceId,\n deviceMetadata,\n input,\n )\n\n if (remember) {\n await server.accountManager.upsertDeviceAccount(deviceId, account.sub)\n } else {\n // In case the user was already signed in, and signed in again, this\n // time without \"remember me\", let's sign them off of the device.\n await server.accountManager.removeDeviceAccount(deviceId, account.sub)\n }\n\n const ephemeralToken = remember\n ? undefined\n : await server.signer.createEphemeralToken({\n sub: account.sub,\n deviceId,\n requestUri,\n })\n\n if (requestUri) {\n // Check if a consent is required for the client, but only if this\n // call is made within the context of an oauth request.\n\n const { clientId, parameters } = await server.requestManager.get(\n requestUri,\n deviceId,\n )\n\n const { authorizedClients } = await server.accountManager.getAccount(\n account.sub,\n )\n\n const json = {\n account,\n ephemeralToken,\n consentRequired: server.checkConsentRequired(\n parameters,\n authorizedClients.get(clientId),\n ),\n }\n\n return { json }\n }\n\n const json = { account, ephemeralToken }\n return { json }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/sign-out',\n schema: z\n .object({\n sub: z.union([subSchema, z.array(subSchema)]),\n })\n .strict(),\n rotateDeviceCookies: true,\n async handler() {\n const uniqueSubs = new Set(asArray(this.input.sub))\n\n for (const sub of uniqueSubs) {\n await server.accountManager.removeDeviceAccount(this.deviceId, sub)\n }\n\n return { json: { success: true as const } }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/reset-password-request',\n schema: z\n .object({\n locale: localeSchema,\n email: emailSchema,\n })\n .strict(),\n async handler() {\n await server.accountManager.resetPasswordRequest(\n this.deviceId,\n this.deviceMetadata,\n this.input,\n )\n return { json: { success: true } }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/reset-password-confirm',\n schema: z\n .object({\n token: emailOtpSchema,\n password: newPasswordSchema,\n })\n .strict(),\n async handler() {\n await server.accountManager.resetPasswordConfirm(\n this.deviceId,\n this.deviceMetadata,\n this.input,\n )\n return { json: { success: true } }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'GET',\n endpoint: '/device-sessions',\n schema: undefined,\n async handler() {\n const deviceAccounts = await server.accountManager.listDeviceAccounts(\n this.deviceId,\n )\n\n const json = deviceAccounts.map(\n (deviceAccount): ActiveDeviceSession => ({\n account: deviceAccount.account,\n loginRequired: server.checkLoginRequired(deviceAccount),\n }),\n )\n\n return { json }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'GET',\n endpoint: '/oauth-sessions',\n schema: z.object({ sub: subSchema }).strict(),\n async handler(req, res) {\n const { account } = await authenticate.call(this, req, res)\n\n const tokenInfos = await server.tokenManager.listAccountTokens(\n account.sub,\n )\n\n const clientIds = tokenInfos.map((tokenInfo) => tokenInfo.data.clientId)\n\n const clients = await server.clientManager.loadClients(clientIds, {\n onError: (err, clientId) => {\n onError?.(req, res, err, `Failed to load client ${clientId}`)\n return undefined // metadata won't be available in the UI\n },\n })\n\n // @TODO: We should ideally filter sessions that are expired (or even\n // expose the expiration date). This requires a change to the way\n // TokenInfo are stored (see TokenManager#isTokenExpired and\n // TokenManager#isTokenInactive).\n const json = tokenInfos.map(({ id, data }): ActiveOAuthSession => {\n return {\n tokenId: id,\n\n createdAt: data.createdAt.toISOString() as ISODateString,\n updatedAt: data.updatedAt.toISOString() as ISODateString,\n\n clientId: data.clientId,\n clientMetadata: clients.get(data.clientId)?.metadata,\n\n scope: data.parameters.scope,\n }\n })\n\n return { json }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'GET',\n endpoint: '/account-sessions',\n schema: z.object({ sub: subSchema }).strict(),\n async handler(req, res) {\n const { account } = await authenticate.call(this, req, res)\n\n const deviceAccounts = await server.accountManager.listAccountDevices(\n account.sub,\n )\n\n const json = deviceAccounts.map(\n (accountSession): ActiveAccountSession => ({\n deviceId: accountSession.deviceId,\n deviceMetadata: {\n ipAddress: accountSession.deviceData.ipAddress,\n userAgent: accountSession.deviceData.userAgent,\n lastSeenAt:\n accountSession.deviceData.lastSeenAt.toISOString() as ISODateString,\n },\n\n isCurrentDevice: accountSession.deviceId === this.deviceId,\n }),\n )\n\n return { json }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/revoke-account-session',\n schema: z.object({ sub: subSchema, deviceId: deviceIdSchema }).strict(),\n async handler() {\n // @NOTE This route is not authenticated. If a user is able to steal\n // another user's session cookie, we allow them to revoke the device\n // session.\n\n await server.accountManager.removeDeviceAccount(\n this.input.deviceId,\n this.input.sub,\n )\n\n return { json: { success: true } }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/revoke-oauth-session',\n schema: z.object({ sub: subSchema, tokenId: tokenIdSchema }).strict(),\n async handler(req, res) {\n const { account } = await authenticate.call(this, req, res)\n\n const tokenInfo = await server.tokenManager.getTokenInfo(\n this.input.tokenId,\n )\n\n if (!tokenInfo || tokenInfo.account.sub !== account.sub) {\n // report this as though the token was not found\n throw new InvalidRequestError(`Invalid token`)\n }\n\n await server.tokenManager.deleteToken(tokenInfo.id)\n\n return { json: { success: true } }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/consent',\n schema: z\n .object({\n sub: z.union([subSchema, signedJwtSchema]),\n scope: z.string().optional(),\n })\n .strict(),\n async handler(req, res) {\n if (!this.requestUri) {\n throw new InvalidRequestError(\n 'This endpoint can only be used in the context of an OAuth request',\n )\n }\n\n // Any AuthorizationError caught in this block will result in a redirect\n // to the client's redirect_uri with an error.\n try {\n const { clientId, parameters } = await server.requestManager.get(\n this.requestUri,\n this.deviceId,\n )\n\n // Any error thrown in this block will be transformed into an\n // AuthorizationError.\n try {\n const { account, authorizedClients } = await authenticate.call(\n this,\n req,\n res,\n )\n\n const client = await server.clientManager.getClient(clientId)\n\n const code = await server.requestManager.setAuthorized(\n this.requestUri,\n client,\n account,\n this.deviceId,\n this.deviceMetadata,\n this.input.scope,\n )\n\n const clientData = authorizedClients.get(clientId)\n if (server.checkConsentRequired(parameters, clientData)) {\n const scopes = new Set(clientData?.authorizedScopes)\n\n // Add the newly accepted scopes to the authorized scopes\n\n // @NOTE `oauthScopeSchema` ensures that `scope` contains no\n // leading/trailing/duplicate spaces.\n for (const s of parameters.scope?.split(' ') ?? []) scopes.add(s)\n\n await server.accountManager.setAuthorizedClient(account, client, {\n ...clientData,\n authorizedScopes: [...scopes],\n })\n }\n\n const url = buildRedirectUrl(server.issuer, parameters, { code })\n\n return { json: { url } }\n } catch (err) {\n // Since we have access to the parameters, we can re-throw an\n // AuthorizationError with the redirect_uri parameter.\n throw AuthorizationError.from(parameters, err)\n }\n } catch (err) {\n onError?.(req, res, err, 'Failed to consent authorization request')\n\n // If any error happened (unauthenticated, invalid request, etc.),\n // lets make sure the request can no longer be used.\n try {\n await server.requestManager.delete(this.requestUri)\n } catch (err) {\n onError?.(req, res, err, 'Failed to delete request')\n }\n\n if (err instanceof AuthorizationError) {\n try {\n const url = buildRedirectUrl(\n server.issuer,\n err.parameters,\n err.toJSON(),\n )\n\n return { json: { url } }\n } catch {\n // Unable to build redirect URL, ignore\n }\n }\n\n // @NOTE Not re-throwing the error here, as the error was already\n // handled by the `onError` callback, and apiRoute (`apiMiddleware`)\n // would call `onError` again.\n return buildErrorJsonResponse(err)\n }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/reject',\n schema: z.object({}).strict(),\n rotateDeviceCookies: true,\n async handler(req, res) {\n const { requestUri } = this\n if (!requestUri) {\n throw new InvalidRequestError(\n 'This endpoint can only be used in the context of an OAuth request',\n )\n }\n\n // Once this endpoint is called, the request will definitely be\n // rejected.\n try {\n // No need to authenticate the user here as they are not authorizing a\n // particular account (CSRF protection is enough).\n\n // @NOTE that the client could *technically* trigger this endpoint while\n // the user is on the authorize page by forging the request (because the\n // client knows the RequestURI from PAR and has all the info needed to\n // forge the request, including CSRF). This cannot be used as DoS attack\n // as the request ID is not guessable and would only result in a bad UX\n // for misbehaving clients, only for the users of those clients.\n\n const { parameters } = await server.requestManager.get(\n requestUri,\n this.deviceId,\n )\n\n const url = buildRedirectUrl(server.issuer, parameters, {\n error: 'access_denied',\n error_description: 'The user rejected the request',\n })\n\n return { json: { url } }\n } catch (err) {\n onError?.(req, res, err, 'Failed to reject authorization request')\n\n if (err instanceof AuthorizationError) {\n try {\n const url = buildRedirectUrl(\n server.issuer,\n err.parameters,\n err.toJSON(),\n )\n\n return { json: { url } }\n } catch {\n // Unable to build redirect URL, ignore\n }\n }\n\n return buildErrorJsonResponse(err)\n } finally {\n await server.requestManager.delete(requestUri).catch((err) => {\n onError?.(req, res, err, 'Failed to delete request')\n })\n }\n },\n }),\n )\n\n return router.buildMiddleware()\n\n async function authenticate(\n this: ApiContext<void, { sub: Sub }>,\n req: Req,\n _res: Res,\n ) {\n if (req.headers.authorization?.startsWith('Bearer ')) {\n try {\n // If there is an authorization header, verify that the ephemeral token it\n // contains is a jwt bound to the right [sub, device, request].\n const bearer = req.headers.authorization.slice(7)\n const ephemeralToken = signedJwtSchema.parse(bearer)\n const { payload } =\n await server.signer.verifyEphemeralToken(ephemeralToken)\n\n if (\n payload.sub === this.input.sub &&\n payload.deviceId === this.deviceId &&\n payload.requestUri === this.requestUri\n ) {\n return await server.accountManager.getAccount(payload.sub)\n }\n } catch (err) {\n throw new WWWAuthenticateError(\n 'unauthorized',\n `Invalid or expired bearer token`,\n { Bearer: {} },\n err,\n )\n }\n }\n\n try {\n // Ensures the \"sub\" has an active session on the device\n const deviceAccount = await server.accountManager.getDeviceAccount(\n this.deviceId,\n this.input.sub,\n )\n\n // The session exists but was created too long ago\n if (server.checkLoginRequired(deviceAccount)) {\n throw new InvalidRequestError('Login required')\n }\n\n return deviceAccount\n } catch (err) {\n throw new WWWAuthenticateError(\n 'unauthorized',\n `User ${this.input.sub} not authenticated on this device`,\n { Bearer: {} },\n err,\n )\n }\n }\n\n type ApiContext<T extends object | void, I = void> = SubCtx<\n T,\n {\n deviceId: DeviceId\n deviceMetadata: RequestMetadata\n\n /**\n * The parsed input data (json payload if \"POST\", query params if \"GET\").\n */\n input: I\n\n /**\n * When defined, the request originated from the authorize page.\n */\n requestUri?: RequestUri\n }\n >\n\n type InferValidation<S extends void | z.ZodTypeAny> = S extends z.ZodTypeAny\n ? z.infer<S>\n : void\n\n /**\n * The main purpose of this function is to ensure that the endpoint\n * implementation matches its type definition from {@link ApiEndpoints}.\n * @private\n */\n function apiRoute<\n C extends RouterCtx<Ctx>,\n M extends 'GET' | 'POST',\n E extends `/${string}` &\n // Extract all the endpoint path that match the method (allows for\n // auto-complete & better error reporting)\n {\n [E in keyof ApiEndpoints]: ApiEndpoints[E] extends { method: M }\n ? E\n : never\n }[keyof ApiEndpoints],\n S extends // A schema that validates the POST input or GET params\n ApiEndpoints[E] extends { method: 'POST'; input: infer I }\n ? z.ZodType<I>\n : ApiEndpoints[E] extends { method: 'GET'; params: infer P }\n ? z.ZodType<P>\n : void,\n >(options: {\n method: M\n endpoint: E\n schema: S\n rotateDeviceCookies?: boolean\n handler: (\n this: ApiContext<RouteCtx<C>, InferValidation<S>>,\n req: Req,\n res: Res,\n ) => Awaitable<JsonResponse<ErrorPayload | ApiEndpoints[E]['output']>>\n }): Middleware<C, Req, Res> {\n return createRoute(\n options.method,\n `${API_ENDPOINT_PREFIX}${options.endpoint}`,\n apiMiddleware(options),\n )\n }\n\n function apiMiddleware<C extends RouterCtx, S extends void | z.ZodTypeAny>({\n method,\n schema,\n rotateDeviceCookies,\n handler,\n }: {\n method: 'GET' | 'POST'\n schema: S\n rotateDeviceCookies?: boolean\n handler: (\n this: ApiContext<C, InferValidation<S>>,\n req: Req,\n res: Res,\n ) => Awaitable<JsonResponse>\n }): Middleware<C, Req, Res> {\n const parseInput: (this: C, req: Req) => Promise<InferValidation<S>> =\n schema == null // No schema means endpoint doesn't accept any input\n ? async function (req) {\n await flushStream(req)\n return undefined\n }\n : method === 'POST'\n ? async function (req) {\n const body = await parseHttpRequest(req, ['json'])\n return schema.parseAsync(body, { path: ['body'] })\n }\n : async function (req) {\n await flushStream(req)\n const query = Object.fromEntries(this.url.searchParams)\n return schema.parseAsync(query, { path: ['query'] })\n }\n\n return jsonHandler<C, Req, Res>(async function (req, res) {\n try {\n // Prevent caching of API routes\n res.setHeader('Cache-Control', 'no-store')\n res.setHeader('Pragma', 'no-cache')\n\n // Prevent CORS requests\n validateFetchMode(req, ['same-origin'])\n validateFetchSite(req, ['same-origin'])\n validateOrigin(req, issuerOrigin)\n const referrer = validateReferrer(req, { origin: issuerOrigin })\n\n // Ensure we are one the right page\n if (\n // trailing slashes are not allowed\n referrer.pathname !== '/oauth/authorize' &&\n referrer.pathname !== '/account' &&\n !referrer.pathname.startsWith(`/account/`)\n ) {\n throw createHttpError(400, `Invalid referrer ${referrer}`)\n }\n\n // Check if the request originated from the authorize page\n const requestUri =\n referrer.pathname === '/oauth/authorize'\n ? await requestUriSchema.parseAsync(\n referrer.searchParams.get('request_uri'),\n )\n : undefined\n\n // Validate CSRF token\n await validateCsrfToken(req, res)\n\n // Parse and validate the input data\n const input = await parseInput.call(this, req)\n\n // Load session data, rotating the session cookie if needed\n const { deviceId, deviceMetadata } = await server.deviceManager.load(\n req,\n res,\n rotateDeviceCookies,\n )\n\n const context: ApiContext<C, InferValidation<S>> = subCtx(this, {\n input,\n requestUri,\n deviceId,\n deviceMetadata,\n })\n\n return await handler.call(context, req, res)\n } catch (err) {\n onError?.(req, res, err, `Failed to handle API request`)\n\n // Make sore to always return a JSON response\n return buildErrorJsonResponse(err)\n }\n })\n }\n}\n\nfunction buildErrorJsonResponse(err: unknown) {\n // @TODO Rework the API error responses (relying on codes)\n const json = buildErrorPayload(err)\n const status = buildErrorStatus(err)\n\n return { json, status }\n}\n\nfunction buildRedirectUrl(\n iss: string,\n parameters: OAuthAuthorizationRequestParameters,\n redirect: AuthorizationRedirectParameters,\n): string {\n const url = new URL('/oauth/authorize/redirect', iss)\n\n url.searchParams.set('redirect_mode', buildRedirectMode(parameters))\n url.searchParams.set('redirect_uri', buildRedirectUri(parameters))\n\n for (const [key, value] of buildRedirectParams(iss, parameters, redirect)) {\n url.searchParams.set(key, value)\n }\n\n return url.href\n}\n\nexport function parseRedirectUrl(url: URL): OAuthRedirectOptions {\n if (url.pathname !== '/oauth/authorize/redirect') {\n throw new InvalidRequestError(\n `Invalid redirect URL: ${url.pathname} is not a valid path`,\n )\n }\n\n const params: [OAuthRedirectQueryParameter, string][] = []\n\n const state = url.searchParams.get('state')\n if (state) params.push(['state', state])\n\n const iss = url.searchParams.get('iss')\n if (iss) params.push(['iss', iss])\n\n if (url.searchParams.has('code')) {\n for (const key of SUCCESS_REDIRECT_KEYS) {\n const value = url.searchParams.get(key)\n if (value != null) params.push([key, value])\n }\n } else if (url.searchParams.has('error')) {\n for (const key of ERROR_REDIRECT_KEYS) {\n const value = url.searchParams.get(key)\n if (value != null) params.push([key, value])\n }\n } else {\n throw new InvalidRequestError(\n 'Invalid redirect URL: neither code nor error found',\n )\n }\n\n try {\n const mode: OAuthResponseMode = oauthResponseModeSchema.parse(\n url.searchParams.get('redirect_mode'),\n )\n\n const redirectUri: OAuthRedirectUri = oauthRedirectUriSchema.parse(\n url.searchParams.get('redirect_uri'),\n )\n\n return { mode, redirectUri, params }\n } catch (err) {\n throw InvalidRequestError.from(err, 'Invalid redirect URL')\n }\n}\n"]}
1
+ {"version":3,"file":"create-api-middleware.js","sourceRoot":"","sources":["../../src/router/create-api-middleware.ts"],"names":[],"mappings":"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","sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport createHttpError from 'http-errors'\nimport { z } from 'zod'\nimport { signedJwtSchema } from '@atproto/jwk'\nimport {\n API_ENDPOINT_PREFIX,\n ActiveAccountSession,\n ActiveDeviceSession,\n ActiveOAuthSession,\n ApiEndpoints,\n ISODateString,\n} from '@atproto/oauth-provider-api'\nimport {\n OAuthAuthorizationRequestParameters,\n OAuthRedirectUri,\n OAuthResponseMode,\n oauthRedirectUriSchema,\n oauthResponseModeSchema,\n} from '@atproto/oauth-types'\nimport { signInDataSchema } from '../account/sign-in-data.js'\nimport { signUpInputSchema } from '../account/sign-up-input.js'\nimport { DeviceId, deviceIdSchema } from '../device/device-id.js'\nimport { AuthorizationError } from '../errors/authorization-error.js'\nimport {\n ErrorPayload,\n buildErrorPayload,\n buildErrorStatus,\n} from '../errors/error-parser.js'\nimport { InvalidRequestError } from '../errors/invalid-request-error.js'\nimport { WWWAuthenticateError } from '../errors/www-authenticate-error.js'\nimport {\n JsonResponse,\n Middleware,\n RequestMetadata,\n Router,\n RouterCtx,\n SubCtx,\n flushStream,\n jsonHandler,\n parseHttpRequest,\n subCtx,\n validateFetchMode,\n validateFetchSite,\n validateOrigin,\n validateReferrer,\n} from '../lib/http/index.js'\nimport { RouteCtx, createRoute } from '../lib/http/route.js'\nimport { asArray } from '../lib/util/cast.js'\nimport { localeSchema } from '../lib/util/locale.js'\nimport type { Awaitable } from '../lib/util/type.js'\nimport type { OAuthProvider } from '../oauth-provider.js'\nimport { Sub, subSchema } from '../oidc/sub.js'\nimport { RequestUri, requestUriSchema } from '../request/request-uri.js'\nimport { AuthorizationRedirectParameters } from '../result/authorization-redirect-parameters.js'\nimport { tokenIdSchema } from '../token/token-id.js'\nimport { emailOtpSchema } from '../types/email-otp.js'\nimport { emailSchema } from '../types/email.js'\nimport { handleSchema } from '../types/handle.js'\nimport { newPasswordSchema } from '../types/password.js'\nimport { validateCsrfToken } from './assets/csrf.js'\nimport {\n ERROR_REDIRECT_KEYS,\n OAuthRedirectOptions,\n OAuthRedirectQueryParameter,\n SUCCESS_REDIRECT_KEYS,\n buildRedirectMode,\n buildRedirectParams,\n buildRedirectUri,\n} from './assets/send-redirect.js'\nimport type { MiddlewareOptions } from './middleware-options.js'\n\nconst verifyHandleSchema = z.object({ handle: handleSchema }).strict()\n\nexport function createApiMiddleware<\n Ctx extends object | void = void,\n Req extends IncomingMessage = IncomingMessage,\n Res extends ServerResponse = ServerResponse,\n>(\n server: OAuthProvider,\n { onError }: MiddlewareOptions<Req, Res>,\n): Middleware<Ctx, Req, Res> {\n const issuerUrl = new URL(server.issuer)\n const issuerOrigin = issuerUrl.origin\n const router = new Router<Ctx, Req, Res>(issuerUrl)\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/verify-handle-availability',\n schema: verifyHandleSchema,\n async handler() {\n await server.accountManager.verifyHandleAvailability(this.input.handle)\n return { json: { available: true } }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/sign-up',\n schema: signUpInputSchema,\n rotateDeviceCookies: true,\n async handler() {\n const { deviceId, deviceMetadata, input, requestUri } = this\n\n const account = await server.accountManager.createAccount(\n deviceId,\n deviceMetadata,\n input,\n )\n\n // Remember when not in the context of a request by default\n const remember = requestUri == null\n\n // Only \"remember\" the newly created account if it was not created during an\n // OAuth flow.\n if (remember) {\n await server.accountManager.upsertDeviceAccount(deviceId, account.sub)\n }\n\n const ephemeralToken = remember\n ? undefined\n : await server.signer.createEphemeralToken({\n sub: account.sub,\n deviceId,\n requestUri: this.requestUri,\n })\n\n const json = { account, ephemeralToken }\n return { json }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/sign-in',\n schema: signInDataSchema.extend({ remember: z.boolean().optional() }),\n rotateDeviceCookies: true,\n async handler() {\n const { deviceId, deviceMetadata, requestUri } = this\n\n // Remember when not in the context of a request by default\n const { remember = requestUri == null, ...input } = this.input\n\n // Look up the client identifier associated with the pending OAuth\n // request, if any, so it can be surfaced to the sign-in hooks.\n const clientId = requestUri\n ? await server.requestManager.peekClientId(requestUri)\n : undefined\n\n const account = await server.accountManager.authenticateAccount(\n deviceId,\n deviceMetadata,\n input,\n clientId,\n )\n\n if (remember) {\n await server.accountManager.upsertDeviceAccount(deviceId, account.sub)\n } else {\n // In case the user was already signed in, and signed in again, this\n // time without \"remember me\", let's sign them off of the device.\n await server.accountManager.removeDeviceAccount(deviceId, account.sub)\n }\n\n const ephemeralToken = remember\n ? undefined\n : await server.signer.createEphemeralToken({\n sub: account.sub,\n deviceId,\n requestUri,\n })\n\n if (requestUri) {\n // Check if a consent is required for the client, but only if this\n // call is made within the context of an oauth request.\n\n const { clientId, parameters } = await server.requestManager.get(\n requestUri,\n deviceId,\n )\n\n const { authorizedClients } = await server.accountManager.getAccount(\n account.sub,\n )\n\n const json = {\n account,\n ephemeralToken,\n consentRequired: server.checkConsentRequired(\n parameters,\n authorizedClients.get(clientId),\n ),\n }\n\n return { json }\n }\n\n const json = { account, ephemeralToken }\n return { json }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/sign-out',\n schema: z\n .object({\n sub: z.union([subSchema, z.array(subSchema)]),\n })\n .strict(),\n rotateDeviceCookies: true,\n async handler() {\n const uniqueSubs = new Set(asArray(this.input.sub))\n\n for (const sub of uniqueSubs) {\n await server.accountManager.removeDeviceAccount(this.deviceId, sub)\n }\n\n return { json: { success: true as const } }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/reset-password-request',\n schema: z\n .object({\n locale: localeSchema,\n email: emailSchema,\n })\n .strict(),\n async handler() {\n await server.accountManager.resetPasswordRequest(\n this.deviceId,\n this.deviceMetadata,\n this.input,\n )\n return { json: { success: true } }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/reset-password-confirm',\n schema: z\n .object({\n token: emailOtpSchema,\n password: newPasswordSchema,\n })\n .strict(),\n async handler() {\n await server.accountManager.resetPasswordConfirm(\n this.deviceId,\n this.deviceMetadata,\n this.input,\n )\n return { json: { success: true } }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'GET',\n endpoint: '/device-sessions',\n schema: undefined,\n async handler() {\n const deviceAccounts = await server.accountManager.listDeviceAccounts(\n this.deviceId,\n )\n\n const json = deviceAccounts.map(\n (deviceAccount): ActiveDeviceSession => ({\n account: deviceAccount.account,\n loginRequired: server.checkLoginRequired(deviceAccount),\n }),\n )\n\n return { json }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'GET',\n endpoint: '/oauth-sessions',\n schema: z.object({ sub: subSchema }).strict(),\n async handler(req, res) {\n const { account } = await authenticate.call(this, req, res)\n\n const tokenInfos = await server.tokenManager.listAccountTokens(\n account.sub,\n )\n\n const clientIds = tokenInfos.map((tokenInfo) => tokenInfo.data.clientId)\n\n const clients = await server.clientManager.loadClients(clientIds, {\n onError: (err, clientId) => {\n onError?.(req, res, err, `Failed to load client ${clientId}`)\n return undefined // metadata won't be available in the UI\n },\n })\n\n // @TODO: We should ideally filter sessions that are expired (or even\n // expose the expiration date). This requires a change to the way\n // TokenInfo are stored (see TokenManager#isTokenExpired and\n // TokenManager#isTokenInactive).\n const json = tokenInfos.map(({ id, data }): ActiveOAuthSession => {\n return {\n tokenId: id,\n\n createdAt: data.createdAt.toISOString() as ISODateString,\n updatedAt: data.updatedAt.toISOString() as ISODateString,\n\n clientId: data.clientId,\n clientMetadata: clients.get(data.clientId)?.metadata,\n\n scope: data.parameters.scope,\n }\n })\n\n return { json }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'GET',\n endpoint: '/account-sessions',\n schema: z.object({ sub: subSchema }).strict(),\n async handler(req, res) {\n const { account } = await authenticate.call(this, req, res)\n\n const deviceAccounts = await server.accountManager.listAccountDevices(\n account.sub,\n )\n\n const json = deviceAccounts.map(\n (accountSession): ActiveAccountSession => ({\n deviceId: accountSession.deviceId,\n deviceMetadata: {\n ipAddress: accountSession.deviceData.ipAddress,\n userAgent: accountSession.deviceData.userAgent,\n lastSeenAt:\n accountSession.deviceData.lastSeenAt.toISOString() as ISODateString,\n },\n\n isCurrentDevice: accountSession.deviceId === this.deviceId,\n }),\n )\n\n return { json }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/revoke-account-session',\n schema: z.object({ sub: subSchema, deviceId: deviceIdSchema }).strict(),\n async handler() {\n // @NOTE This route is not authenticated. If a user is able to steal\n // another user's session cookie, we allow them to revoke the device\n // session.\n\n await server.accountManager.removeDeviceAccount(\n this.input.deviceId,\n this.input.sub,\n )\n\n return { json: { success: true } }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/revoke-oauth-session',\n schema: z.object({ sub: subSchema, tokenId: tokenIdSchema }).strict(),\n async handler(req, res) {\n const { account } = await authenticate.call(this, req, res)\n\n const tokenInfo = await server.tokenManager.getTokenInfo(\n this.input.tokenId,\n )\n\n if (!tokenInfo || tokenInfo.account.sub !== account.sub) {\n // report this as though the token was not found\n throw new InvalidRequestError(`Invalid token`)\n }\n\n await server.tokenManager.deleteToken(tokenInfo.id)\n\n return { json: { success: true } }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/consent',\n schema: z\n .object({\n sub: z.union([subSchema, signedJwtSchema]),\n scope: z.string().optional(),\n })\n .strict(),\n async handler(req, res) {\n if (!this.requestUri) {\n throw new InvalidRequestError(\n 'This endpoint can only be used in the context of an OAuth request',\n )\n }\n\n // Any AuthorizationError caught in this block will result in a redirect\n // to the client's redirect_uri with an error.\n try {\n const { clientId, parameters } = await server.requestManager.get(\n this.requestUri,\n this.deviceId,\n )\n\n // Any error thrown in this block will be transformed into an\n // AuthorizationError.\n try {\n const { account, authorizedClients } = await authenticate.call(\n this,\n req,\n res,\n )\n\n const client = await server.clientManager.getClient(clientId)\n\n const code = await server.requestManager.setAuthorized(\n this.requestUri,\n client,\n account,\n this.deviceId,\n this.deviceMetadata,\n this.input.scope,\n )\n\n const clientData = authorizedClients.get(clientId)\n if (server.checkConsentRequired(parameters, clientData)) {\n const scopes = new Set(clientData?.authorizedScopes)\n\n // Add the newly accepted scopes to the authorized scopes\n\n // @NOTE `oauthScopeSchema` ensures that `scope` contains no\n // leading/trailing/duplicate spaces.\n for (const s of parameters.scope?.split(' ') ?? []) scopes.add(s)\n\n await server.accountManager.setAuthorizedClient(account, client, {\n ...clientData,\n authorizedScopes: [...scopes],\n })\n }\n\n const url = buildRedirectUrl(server.issuer, parameters, { code })\n\n return { json: { url } }\n } catch (err) {\n // Since we have access to the parameters, we can re-throw an\n // AuthorizationError with the redirect_uri parameter.\n throw AuthorizationError.from(parameters, err)\n }\n } catch (err) {\n onError?.(req, res, err, 'Failed to consent authorization request')\n\n // If any error happened (unauthenticated, invalid request, etc.),\n // lets make sure the request can no longer be used.\n try {\n await server.requestManager.delete(this.requestUri)\n } catch (err) {\n onError?.(req, res, err, 'Failed to delete request')\n }\n\n if (err instanceof AuthorizationError) {\n try {\n const url = buildRedirectUrl(\n server.issuer,\n err.parameters,\n err.toJSON(),\n )\n\n return { json: { url } }\n } catch {\n // Unable to build redirect URL, ignore\n }\n }\n\n // @NOTE Not re-throwing the error here, as the error was already\n // handled by the `onError` callback, and apiRoute (`apiMiddleware`)\n // would call `onError` again.\n return buildErrorJsonResponse(err)\n }\n },\n }),\n )\n\n router.use(\n apiRoute({\n method: 'POST',\n endpoint: '/reject',\n schema: z.object({}).strict(),\n rotateDeviceCookies: true,\n async handler(req, res) {\n const { requestUri } = this\n if (!requestUri) {\n throw new InvalidRequestError(\n 'This endpoint can only be used in the context of an OAuth request',\n )\n }\n\n // Once this endpoint is called, the request will definitely be\n // rejected.\n try {\n // No need to authenticate the user here as they are not authorizing a\n // particular account (CSRF protection is enough).\n\n // @NOTE that the client could *technically* trigger this endpoint while\n // the user is on the authorize page by forging the request (because the\n // client knows the RequestURI from PAR and has all the info needed to\n // forge the request, including CSRF). This cannot be used as DoS attack\n // as the request ID is not guessable and would only result in a bad UX\n // for misbehaving clients, only for the users of those clients.\n\n const { parameters } = await server.requestManager.get(\n requestUri,\n this.deviceId,\n )\n\n const url = buildRedirectUrl(server.issuer, parameters, {\n error: 'access_denied',\n error_description: 'The user rejected the request',\n })\n\n return { json: { url } }\n } catch (err) {\n onError?.(req, res, err, 'Failed to reject authorization request')\n\n if (err instanceof AuthorizationError) {\n try {\n const url = buildRedirectUrl(\n server.issuer,\n err.parameters,\n err.toJSON(),\n )\n\n return { json: { url } }\n } catch {\n // Unable to build redirect URL, ignore\n }\n }\n\n return buildErrorJsonResponse(err)\n } finally {\n await server.requestManager.delete(requestUri).catch((err) => {\n onError?.(req, res, err, 'Failed to delete request')\n })\n }\n },\n }),\n )\n\n return router.buildMiddleware()\n\n async function authenticate(\n this: ApiContext<void, { sub: Sub }>,\n req: Req,\n _res: Res,\n ) {\n if (req.headers.authorization?.startsWith('Bearer ')) {\n try {\n // If there is an authorization header, verify that the ephemeral token it\n // contains is a jwt bound to the right [sub, device, request].\n const bearer = req.headers.authorization.slice(7)\n const ephemeralToken = signedJwtSchema.parse(bearer)\n const { payload } =\n await server.signer.verifyEphemeralToken(ephemeralToken)\n\n if (\n payload.sub === this.input.sub &&\n payload.deviceId === this.deviceId &&\n payload.requestUri === this.requestUri\n ) {\n return await server.accountManager.getAccount(payload.sub)\n }\n } catch (err) {\n throw new WWWAuthenticateError(\n 'unauthorized',\n `Invalid or expired bearer token`,\n { Bearer: {} },\n err,\n )\n }\n }\n\n try {\n // Ensures the \"sub\" has an active session on the device\n const deviceAccount = await server.accountManager.getDeviceAccount(\n this.deviceId,\n this.input.sub,\n )\n\n // The session exists but was created too long ago\n if (server.checkLoginRequired(deviceAccount)) {\n throw new InvalidRequestError('Login required')\n }\n\n return deviceAccount\n } catch (err) {\n throw new WWWAuthenticateError(\n 'unauthorized',\n `User ${this.input.sub} not authenticated on this device`,\n { Bearer: {} },\n err,\n )\n }\n }\n\n type ApiContext<T extends object | void, I = void> = SubCtx<\n T,\n {\n deviceId: DeviceId\n deviceMetadata: RequestMetadata\n\n /**\n * The parsed input data (json payload if \"POST\", query params if \"GET\").\n */\n input: I\n\n /**\n * When defined, the request originated from the authorize page.\n */\n requestUri?: RequestUri\n }\n >\n\n type InferValidation<S extends void | z.ZodTypeAny> = S extends z.ZodTypeAny\n ? z.infer<S>\n : void\n\n /**\n * The main purpose of this function is to ensure that the endpoint\n * implementation matches its type definition from {@link ApiEndpoints}.\n * @private\n */\n function apiRoute<\n C extends RouterCtx<Ctx>,\n M extends 'GET' | 'POST',\n E extends `/${string}` &\n // Extract all the endpoint path that match the method (allows for\n // auto-complete & better error reporting)\n {\n [E in keyof ApiEndpoints]: ApiEndpoints[E] extends { method: M }\n ? E\n : never\n }[keyof ApiEndpoints],\n S extends // A schema that validates the POST input or GET params\n ApiEndpoints[E] extends { method: 'POST'; input: infer I }\n ? z.ZodType<I>\n : ApiEndpoints[E] extends { method: 'GET'; params: infer P }\n ? z.ZodType<P>\n : void,\n >(options: {\n method: M\n endpoint: E\n schema: S\n rotateDeviceCookies?: boolean\n handler: (\n this: ApiContext<RouteCtx<C>, InferValidation<S>>,\n req: Req,\n res: Res,\n ) => Awaitable<JsonResponse<ErrorPayload | ApiEndpoints[E]['output']>>\n }): Middleware<C, Req, Res> {\n return createRoute(\n options.method,\n `${API_ENDPOINT_PREFIX}${options.endpoint}`,\n apiMiddleware(options),\n )\n }\n\n function apiMiddleware<C extends RouterCtx, S extends void | z.ZodTypeAny>({\n method,\n schema,\n rotateDeviceCookies,\n handler,\n }: {\n method: 'GET' | 'POST'\n schema: S\n rotateDeviceCookies?: boolean\n handler: (\n this: ApiContext<C, InferValidation<S>>,\n req: Req,\n res: Res,\n ) => Awaitable<JsonResponse>\n }): Middleware<C, Req, Res> {\n const parseInput: (this: C, req: Req) => Promise<InferValidation<S>> =\n schema == null // No schema means endpoint doesn't accept any input\n ? async function (req) {\n await flushStream(req)\n return undefined\n }\n : method === 'POST'\n ? async function (req) {\n const body = await parseHttpRequest(req, ['json'])\n return schema.parseAsync(body, { path: ['body'] })\n }\n : async function (req) {\n await flushStream(req)\n const query = Object.fromEntries(this.url.searchParams)\n return schema.parseAsync(query, { path: ['query'] })\n }\n\n return jsonHandler<C, Req, Res>(async function (req, res) {\n try {\n // Prevent caching of API routes\n res.setHeader('Cache-Control', 'no-store')\n res.setHeader('Pragma', 'no-cache')\n\n // Prevent CORS requests\n validateFetchMode(req, ['same-origin'])\n validateFetchSite(req, ['same-origin'])\n validateOrigin(req, issuerOrigin)\n const referrer = validateReferrer(req, { origin: issuerOrigin })\n\n // Ensure we are one the right page\n if (\n // trailing slashes are not allowed\n referrer.pathname !== '/oauth/authorize' &&\n referrer.pathname !== '/account' &&\n !referrer.pathname.startsWith(`/account/`)\n ) {\n throw createHttpError(400, `Invalid referrer ${referrer}`)\n }\n\n // Check if the request originated from the authorize page\n const requestUri =\n referrer.pathname === '/oauth/authorize'\n ? await requestUriSchema.parseAsync(\n referrer.searchParams.get('request_uri'),\n )\n : undefined\n\n // Validate CSRF token\n await validateCsrfToken(req, res)\n\n // Parse and validate the input data\n const input = await parseInput.call(this, req)\n\n // Load session data, rotating the session cookie if needed\n const { deviceId, deviceMetadata } = await server.deviceManager.load(\n req,\n res,\n rotateDeviceCookies,\n )\n\n const context: ApiContext<C, InferValidation<S>> = subCtx(this, {\n input,\n requestUri,\n deviceId,\n deviceMetadata,\n })\n\n return await handler.call(context, req, res)\n } catch (err) {\n onError?.(req, res, err, `Failed to handle API request`)\n\n // Make sore to always return a JSON response\n return buildErrorJsonResponse(err)\n }\n })\n }\n}\n\nfunction buildErrorJsonResponse(err: unknown) {\n // @TODO Rework the API error responses (relying on codes)\n const json = buildErrorPayload(err)\n const status = buildErrorStatus(err)\n\n return { json, status }\n}\n\nfunction buildRedirectUrl(\n iss: string,\n parameters: OAuthAuthorizationRequestParameters,\n redirect: AuthorizationRedirectParameters,\n): string {\n const url = new URL('/oauth/authorize/redirect', iss)\n\n url.searchParams.set('redirect_mode', buildRedirectMode(parameters))\n url.searchParams.set('redirect_uri', buildRedirectUri(parameters))\n\n for (const [key, value] of buildRedirectParams(iss, parameters, redirect)) {\n url.searchParams.set(key, value)\n }\n\n return url.href\n}\n\nexport function parseRedirectUrl(url: URL): OAuthRedirectOptions {\n if (url.pathname !== '/oauth/authorize/redirect') {\n throw new InvalidRequestError(\n `Invalid redirect URL: ${url.pathname} is not a valid path`,\n )\n }\n\n const params: [OAuthRedirectQueryParameter, string][] = []\n\n const state = url.searchParams.get('state')\n if (state) params.push(['state', state])\n\n const iss = url.searchParams.get('iss')\n if (iss) params.push(['iss', iss])\n\n if (url.searchParams.has('code')) {\n for (const key of SUCCESS_REDIRECT_KEYS) {\n const value = url.searchParams.get(key)\n if (value != null) params.push([key, value])\n }\n } else if (url.searchParams.has('error')) {\n for (const key of ERROR_REDIRECT_KEYS) {\n const value = url.searchParams.get(key)\n if (value != null) params.push([key, value])\n }\n } else {\n throw new InvalidRequestError(\n 'Invalid redirect URL: neither code nor error found',\n )\n }\n\n try {\n const mode: OAuthResponseMode = oauthResponseModeSchema.parse(\n url.searchParams.get('redirect_mode'),\n )\n\n const redirectUri: OAuthRedirectUri = oauthRedirectUriSchema.parse(\n url.searchParams.get('redirect_uri'),\n )\n\n return { mode, redirectUri, params }\n } catch (err) {\n throw InvalidRequestError.from(err, 'Invalid redirect URL')\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/oauth-provider",
3
- "version": "0.16.0",
3
+ "version": "0.16.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": [
@@ -43,19 +43,19 @@
43
43
  "jose": "^5.2.0",
44
44
  "zod": "^3.23.8",
45
45
  "@atproto-labs/fetch": "^0.2.3",
46
- "@atproto-labs/fetch-node": "^0.2.0",
47
46
  "@atproto-labs/pipe": "^0.1.1",
47
+ "@atproto-labs/fetch-node": "^0.2.0",
48
48
  "@atproto-labs/simple-store": "^0.3.0",
49
49
  "@atproto-labs/simple-store-memory": "^0.1.4",
50
- "@atproto/common": "^0.5.16",
51
- "@atproto/did": "^0.3.0",
52
50
  "@atproto/jwk": "^0.6.0",
51
+ "@atproto/common": "^0.5.16",
53
52
  "@atproto/jwk-jose": "^0.1.11",
53
+ "@atproto/oauth-types": "^0.6.3",
54
54
  "@atproto/lex-document": "^0.0.20",
55
55
  "@atproto/lex-resolver": "^0.0.22",
56
- "@atproto/oauth-types": "^0.6.3",
57
56
  "@atproto/oauth-provider-api": "0.4.0",
58
57
  "@atproto/oauth-provider-ui": "0.5.0",
58
+ "@atproto/did": "^0.3.0",
59
59
  "@atproto/oauth-scopes": "^0.3.2",
60
60
  "@atproto/syntax": "^0.5.4"
61
61
  },
@@ -2,8 +2,10 @@ import {
2
2
  OAuthIssuerIdentifier,
3
3
  isOAuthClientIdLoopback,
4
4
  } from '@atproto/oauth-types'
5
+ import { ClientId } from '../client/client-id.js'
5
6
  import { Client } from '../client/client.js'
6
7
  import { DeviceId } from '../device/device-id.js'
8
+ import { InvalidCredentialsError } from '../errors/invalid-credentials-error.js'
7
9
  import { InvalidRequestError } from '../errors/invalid-request-error.js'
8
10
  import { HCaptchaClient, HcaptchaVerifyResult } from '../lib/hcaptcha.js'
9
11
  import { constantTime } from '../lib/util/time.js'
@@ -159,26 +161,67 @@ export class AccountManager {
159
161
  deviceId: DeviceId,
160
162
  deviceMetadata: RequestMetadata,
161
163
  data: SignInData,
164
+ clientId?: ClientId,
162
165
  ): Promise<Account> {
163
166
  try {
164
167
  await this.hooks.onSignInAttempt?.call(null, {
165
168
  data,
166
169
  deviceId,
167
170
  deviceMetadata,
171
+ clientId,
168
172
  })
169
173
 
170
- const account = await constantTime(
171
- TIMING_ATTACK_MITIGATION_DELAY,
172
- async () => {
173
- return this.store.authenticateAccount(data)
174
- },
175
- )
174
+ let account: Account
175
+ try {
176
+ account = await constantTime(
177
+ TIMING_ATTACK_MITIGATION_DELAY,
178
+ async () => {
179
+ return this.store.authenticateAccount(data)
180
+ },
181
+ )
182
+ } catch (err) {
183
+ // Only notify for credential failures (e.g. unknown identifier, wrong
184
+ // password). Server errors and flows that require an additional factor
185
+ // (e.g. SecondAuthenticationFactorRequiredError) are not "failed
186
+ // sign-ins" and do not trigger the hook.
187
+ if (err instanceof InvalidRequestError) {
188
+ // Stores that throw the more specific `InvalidCredentialsError`
189
+ // can attach the matched subject identifier to distinguish
190
+ // "identifier known, password wrong" from "identifier unknown".
191
+ // This information is only exposed to the hook and is never
192
+ // surfaced to the client.
193
+ const isCredentialsError = err instanceof InvalidCredentialsError
194
+ const sub = isCredentialsError ? err.sub ?? null : null
195
+
196
+ // Swallow any error from the hook itself so that it does not mask
197
+ // the underlying authentication failure being reported.
198
+ try {
199
+ await this.hooks.onSignInFailed?.call(null, {
200
+ data,
201
+ error: err,
202
+ sub,
203
+ deviceId,
204
+ deviceMetadata,
205
+ clientId,
206
+ })
207
+ } catch {
208
+ // noop
209
+ }
210
+
211
+ if (isCredentialsError) {
212
+ // Defensively downgrade to a plain InvalidRequestError
213
+ throw new InvalidRequestError(err.error_description)
214
+ }
215
+ }
216
+ throw err
217
+ }
176
218
 
177
219
  await this.hooks.onSignedIn?.call(null, {
178
220
  data,
179
221
  account,
180
222
  deviceId,
181
223
  deviceMetadata,
224
+ clientId,
182
225
  })
183
226
 
184
227
  return account
@@ -11,6 +11,7 @@ import { HcaptchaVerifyResult } from '../lib/hcaptcha.js'
11
11
  import { Awaitable, buildInterfaceChecker } from '../lib/util/type.js'
12
12
  import {
13
13
  HandleUnavailableError,
14
+ InvalidCredentialsError,
14
15
  InvalidRequestError,
15
16
  SecondAuthenticationFactorRequiredError,
16
17
  } from '../oauth-errors.js'
@@ -36,6 +37,7 @@ export type {
36
37
 
37
38
  export {
38
39
  HandleUnavailableError,
40
+ InvalidCredentialsError,
39
41
  InvalidRequestError,
40
42
  SecondAuthenticationFactorRequiredError,
41
43
  }
@@ -108,7 +110,13 @@ export interface AccountStore {
108
110
  createAccount(data: CreateAccountData): Awaitable<Account>
109
111
 
110
112
  /**
111
- * @throws {InvalidRequestError} - When the credentials are not valid
113
+ * @throws {InvalidCredentialsError} - When the credentials are not valid.
114
+ * Populate {@link InvalidCredentialsError.sub} with the subject identifier
115
+ * when the identifier matched an existing account (e.g. wrong password for
116
+ * a known user); omit it when the identifier was not found. Throwing the
117
+ * generic {@link InvalidRequestError} is also accepted for backward
118
+ * compatibility but prevents the `onSignInFailed` hook from distinguishing
119
+ * the two cases.
112
120
  * @throws {SecondAuthenticationFactorRequiredError} - To indicate that an {@link SecondAuthenticationFactorRequiredError.type} is required in the credentials
113
121
  */
114
122
  authenticateAccount(data: AuthenticateAccountData): Awaitable<Account>
@@ -0,0 +1,29 @@
1
+ import { Sub } from '../oidc/sub.js'
2
+ import { InvalidRequestError } from './invalid-request-error.js'
3
+
4
+ /**
5
+ * Thrown by {@link AccountStore.authenticateAccount} implementations to signal
6
+ * that a sign-in attempt was rejected because the provided credentials did not
7
+ * match a known account.
8
+ *
9
+ * Stores should populate {@link InvalidCredentialsError.sub} when the
10
+ * identifier resolved to an existing account but e.g. the password or OTP was
11
+ * incorrect. The identifier-unknown case should leave `sub` unset. This
12
+ * information is surfaced to the `onSignInFailed` hook and never sent back to
13
+ * the client, so populating it does not affect the client-visible response.
14
+ *
15
+ * Only the subject identifier (DID) is carried — not a full `Account` — to
16
+ * avoid embedding PII (email, name, etc.) in an error that may be serialized
17
+ * by loggers or monitoring tools walking the `.cause` chain. Hook consumers
18
+ * that need richer profile info can resolve it from their own account store
19
+ * using `sub`.
20
+ */
21
+ export class InvalidCredentialsError extends InvalidRequestError {
22
+ constructor(
23
+ message = 'Invalid identifier or password',
24
+ public readonly sub?: Sub,
25
+ cause?: unknown,
26
+ ) {
27
+ super(message, cause)
28
+ }
29
+ }
@@ -10,6 +10,7 @@ export * from './errors/invalid-authorization-details-error.js'
10
10
  export * from './errors/invalid-client-error.js'
11
11
  export * from './errors/invalid-client-id-error.js'
12
12
  export * from './errors/invalid-client-metadata-error.js'
13
+ export * from './errors/invalid-credentials-error.js'
13
14
  export * from './errors/invalid-dpop-key-binding-error.js'
14
15
  export * from './errors/invalid-dpop-proof-error.js'
15
16
  export * from './errors/invalid-grant-error.js'