@spfn/auth 0.3.0-beta.18 → 0.3.0-beta.19

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/README.md CHANGED
@@ -227,6 +227,7 @@ routes use `.skip(['auth'])`; the rest require `Authorization: Bearer <client-si
227
227
  | `cancelAccountDeletion` | POST `/_auth/deletion/cancel` | public | cancel a pending deletion (credential-based recovery) |
228
228
  | `listRoles` / `createAdminRole` / `updateAdminRole` / `deleteAdminRole` / `updateUserRole` | — | superadmin | admin RBAC management |
229
229
  | OAuth routes | — | — | see OAuth section |
230
+ | `registerOAuth2Client` / `getOAuth2Authorize` / `createOAuth2AuthorizationCode` / `oauth2Token` / `oauth2Revoke` / `listOAuth2Grants` / `revokeOAuth2Grant` | `/_auth/oauth2/*` | mixed | OAuth 2.1 authorization server for MCP clients — see [Authorization server for MCP clients](#authorization-server-for-mcp-clients). 404 unless configured |
230
231
 
231
232
  There is deliberately **no account-existence endpoint**. `POST /_auth/exists` was removed
232
233
  because it answered "does this account exist" directly, which is user enumeration; the
@@ -2022,6 +2023,139 @@ x-acme-service-token: <the app's own credential>
2022
2023
  The field stays informational: downstream permission and tenant code takes one principal shape and
2023
2024
  never branches on how it was produced.
2024
2025
 
2026
+ ## Authorization server for MCP clients
2027
+
2028
+ Let Claude Code and Codex connect to your app's `/mcp` endpoint as the user, over the flow
2029
+ they already speak: OAuth 2.1 with dynamic client registration and PKCE.
2030
+
2031
+ ```console
2032
+ $ claude mcp add --transport http acme https://api.acme.com/mcp
2033
+ $ claude
2034
+ > /mcp
2035
+ ```
2036
+
2037
+ Between those two lines the CLI discovers `/.well-known/oauth-authorization-server`, registers
2038
+ itself, opens a browser at your consent screen, catches the redirect on a loopback port, and
2039
+ exchanges the code for a token. Nobody pastes anything.
2040
+
2041
+ The feature is opt-in and the opt-in is one block:
2042
+
2043
+ ```typescript
2044
+ createAuthLifecycle({
2045
+ authorizationServer: {
2046
+ scopes: {
2047
+ 'mcp:read': 'Read your projects and tasks',
2048
+ 'mcp:write': 'Create and edit your tasks',
2049
+ },
2050
+ defaultScopes: ['mcp:read'], // what a request with no `scope` asks for. default: all of them
2051
+ // issuer: 'https://api.acme.com', // default: SPFN_API_URL
2052
+ // authorizeUrl: 'https://acme.com/oauth/authorize', // default: {app url}/oauth/authorize
2053
+ // allowedRedirectOrigins: [], // https origins a client may register. loopback needs no entry
2054
+ // accessTokenTtlMs: 8 * 60 * 60 * 1000, // default 8 hours
2055
+ // refreshTokenTtlMs: 30 * 24 * 60 * 60 * 1000, // default 30 days
2056
+ // codeTtlMs: 60 * 1000, // default 60 seconds
2057
+ },
2058
+ })
2059
+ ```
2060
+
2061
+ Without that block every endpoint below answers 404 and nothing else changes — including the
2062
+ boot check, which does not run. `scopes` is the one setting with no default: the names are
2063
+ your application's vocabulary, they are published in the metadata document and read aloud on
2064
+ the consent screen, and there is nothing to derive them from.
2065
+
2066
+ | Endpoint | Host | Auth | What it is |
2067
+ | --- | --- | --- | --- |
2068
+ | `GET /.well-known/oauth-authorization-server` | API | public | RFC 8414 discovery — the first request any client makes |
2069
+ | `POST /_auth/oauth2/register` | API | public, IP rate limited | RFC 7591 dynamic registration. Public clients only |
2070
+ | `GET /_auth/oauth2/authorize` | API | `authenticate` | What the consent screen should say. Records nothing |
2071
+ | `POST /_auth/oauth2/authorize` | API | `authenticate` | The decision. Mints the code |
2072
+ | `POST /_auth/oauth2/token` | API | public, IP rate limited | `authorization_code` and `refresh_token` |
2073
+ | `POST /_auth/oauth2/revoke` | API | public (RFC 7009) | `client_id` required; 200 for an unknown token as surely as for a real one |
2074
+ | `GET /_auth/oauth2/grants` · `DELETE /_auth/oauth2/grants/:id` | API | `authenticate` | What the user has connected, and the button that disconnects it |
2075
+ | `GET /oauth/authorize` · `POST /oauth/authorize` | web | session | The consent screen itself — see the note at the end |
2076
+
2077
+ Two lines wire it to `@spfn/mcp`:
2078
+
2079
+ ```typescript
2080
+ import { verifyAccessToken } from '@spfn/auth/server';
2081
+
2082
+ export const mcp = createMcpRoute({ validateToken: verifyAccessToken, tools: [...] });
2083
+ ```
2084
+
2085
+ `verifyAccessToken(token, resource)` answers `{ clientId, scopes, expiresAt, userId }` or
2086
+ `null`, and `null` is a refusal — `@spfn/mcp` ≥ 0.3.0-beta.3 accepts it as one rather than
2087
+ requiring a throw. `expiresAt` is seconds since the epoch, like every other OAuth field here.
2088
+
2089
+ - **Only loopback and origins you allowed.** A client may register `http://localhost:*`,
2090
+ `http://127.0.0.1:*` or `http://[::1]:*` — a CLI cannot know which port the OS will hand it,
2091
+ so the **port** is the one thing allowed to vary. Nothing else does: host, path and query
2092
+ must match the registration exactly, a fragment is refused at registration and at request,
2093
+ and plain `http` anywhere else is refused outright. An `https` redirect URI has to be on an
2094
+ origin listed in `allowedRedirectOrigins`.
2095
+ - **Those three spellings are three registrations.** `localhost`, `127.0.0.1` and `[::1]` do
2096
+ not stand in for one another — they resolve differently on a machine with a split-horizon
2097
+ resolver, and a client answered on a host it did not register is a client something
2098
+ redirected. IPv6 is the one place spelling is folded: `http://[0:0:0:0:0:0:0:1]:5/cb` and
2099
+ `http://[::1]:5/cb` are the same registration, because both sides are read through
2100
+ `new URL(...).hostname`.
2101
+ - **An unknown client or a mismatched redirect URI is shown, never redirected.** There is no
2102
+ vetted URI to send that error to, and sending it to the one the request supplied is the open
2103
+ redirect the whole rule exists to close. Every other authorize-time error —
2104
+ `invalid_request`, `invalid_scope`, `invalid_target`, `access_denied` — goes back to the
2105
+ client on its registered URI, which is the only form the waiting CLI can read.
2106
+ - **PKCE S256, and nothing else.** No `plain`, and no request without a challenge. The code
2107
+ arrives on a loopback port that any process on the machine could have been listening on.
2108
+ - **`resource` is required** (RFC 8707) and the token is only good against it. A token your
2109
+ user approved for your MCP server cannot be replayed against a neighbouring deployment that
2110
+ shares this authorization server.
2111
+ - **A code is spent by the statement that reads it**, so of two exchanges arriving together
2112
+ exactly one gets tokens — and **presenting a code twice revokes the grant**, because by then
2113
+ somebody else may hold what the first exchange produced.
2114
+ - **Refresh tokens rotate, and a rotated one is marked rather than deleted.** Presenting it
2115
+ again revokes the grant, which kills the replacement as well as the replayed token: both
2116
+ hang off the grant and there is no telling which holder is the thief. A refresh may ask for
2117
+ a **subset** of the granted scopes and never for more; narrowing applies to that request and
2118
+ leaves the user's consent record as they gave it.
2119
+ - **Every code and refresh failure is one `invalid_grant`, word for word.** Unknown, expired,
2120
+ spent, wrong verifier, another client's. The endpoint is public, and an error that told
2121
+ those apart would answer the question somebody holding a stolen value is asking.
2122
+ - **Token endpoint errors are RFC 6749 §5.2, not the SPFN envelope** —
2123
+ `{ "error": "invalid_grant", "error_description": "..." }`, status 400,
2124
+ `Cache-Control: no-store`. The client reading it is an OAuth library that knows those two
2125
+ field names and nothing about this framework. Registration refusals are RFC 7591 §3.2.2 the
2126
+ same way (`invalid_redirect_uri`, `invalid_client_metadata`).
2127
+ - **Nothing but a hash is stored.** Codes and tokens are `spfn_at_<64 hex>` /
2128
+ `spfn_rt_<64 hex>` / 43 url-safe characters, and the value exists in the clear exactly once,
2129
+ in the response that issues it. It is never logged and never put in an event.
2130
+ - **A global revocation reaches the grants.** `revoke-all`, a password change, a completed
2131
+ password reset and a deletion request each revoke every grant the account has — so a CLI
2132
+ holding a refresh token through "sign me out everywhere" cannot be back within the hour,
2133
+ which is exactly the client that call was aimed at. The user's own
2134
+ `DELETE /_auth/oauth2/grants/:id` does the same for one client, immediately.
2135
+ - **The issuer is checked at boot.** It must be an absolute URL with no path — the metadata
2136
+ document is served at an origin's root and nowhere else — and it must be `https`, or `http`
2137
+ on `localhost` / `127.0.0.1` / `[::1]` for development. Anything else refuses to start with
2138
+ a message naming `SPFN_API_URL` or `authorizationServer.issuer`, whichever the value came
2139
+ from. An application with no `authorizationServer` block never reaches this check. The one
2140
+ value that is accepted and rewritten is a bare trailing slash: `https://api.acme.com/` is
2141
+ stored as `https://api.acme.com`, the form `@spfn/mcp` derives, so the two documents naming
2142
+ this server agree (RFC 8414 §3.3). That reduction happens where the config is resolved, not
2143
+ in the boot check, so a document read without the lifecycle hook publishes the same issuer.
2144
+ - **Unapproved client rows are swept.** Registration is unauthenticated by necessity, so
2145
+ `auth.oauth2.client-purge` (in `authJobRouter`, daily at 05:00) deletes clients older than a
2146
+ day that no user ever approved. One with a grant against it is never touched. Registration
2147
+ is also capped per IP two ways — a burst rate limit, and a cap on how many unapproved
2148
+ clients one address may have standing, which a rate limit cannot express.
2149
+ - **`/mcp` tokens are not sessions.** An access token issued here authorizes the MCP surface
2150
+ for the resource it names. It is not a user session and is not accepted by ordinary API
2151
+ routes.
2152
+
2153
+ The consent screen itself — `createOAuth2AuthorizeHandlers({ loginPath })`, which renders
2154
+ `GET /oauth/authorize` and handles its POST — ships in the next release. Until then the API
2155
+ side above is complete and a handler can be written against it: `GET /_auth/oauth2/authorize`
2156
+ returns `{ clientName, redirectHost, scopes, resource }` to draw, and
2157
+ `POST /_auth/oauth2/authorize` takes the decision and returns the code to redirect with.
2158
+
2025
2159
  ## Machine principals (`registerMachineVerifier`)
2026
2160
 
2027
2161
  A machine credential is issued by a service to a non-interactive process, and its subject is
package/dist/errors.d.ts CHANGED
@@ -598,6 +598,70 @@ declare class PasskeyConfigError extends HttpError {
598
598
  details?: Record<string, any>;
599
599
  });
600
600
  }
601
+ /**
602
+ * OAuth2 Unknown Client Error (400)
603
+ *
604
+ * Thrown by the API authorize endpoints when `client_id` names no registered
605
+ * client. One of the two refusals that must NOT be turned into a redirect: with
606
+ * no client there is no registered redirect URI, so the only place left to send
607
+ * the error is the one the request supplied — which is exactly the open redirect
608
+ * this rule exists to close. The consent screen shows it instead.
609
+ */
610
+ declare class OAuth2UnknownClientError extends ValidationError {
611
+ constructor(data?: {
612
+ message?: string;
613
+ details?: Record<string, any>;
614
+ });
615
+ }
616
+ /**
617
+ * OAuth2 Redirect URI Mismatch Error (400)
618
+ *
619
+ * Thrown when `redirect_uri` is not one the client registered. The second
620
+ * non-redirectable refusal, for the same reason as the first: redirecting the
621
+ * error to an unregistered URI is the attack.
622
+ */
623
+ declare class OAuth2RedirectUriMismatchError extends ValidationError {
624
+ constructor(data?: {
625
+ message?: string;
626
+ details?: Record<string, any>;
627
+ });
628
+ }
629
+ /**
630
+ * OAuth2 Authorize Redirect Error (400)
631
+ *
632
+ * Every other authorize-time refusal — `invalid_request`, `invalid_scope`,
633
+ * `invalid_target`, `access_denied`. The client and its redirect URI are both
634
+ * known by the time these are decided, so RFC 6749 §4.1.2.1 says the error goes
635
+ * back to the client as query parameters on that URI rather than to the person.
636
+ *
637
+ * The API cannot perform that redirect — it is answering the web app's consent
638
+ * handler, not the browser — so it carries the pieces in `details` and the
639
+ * handler builds the 302. `redirectUri` is the registered-and-matched value, not
640
+ * the raw parameter, which is what makes it safe to send somebody to.
641
+ */
642
+ declare class OAuth2AuthorizeRedirectError extends ValidationError {
643
+ constructor(data: {
644
+ error: string;
645
+ redirectUri: string;
646
+ state?: string;
647
+ message?: string;
648
+ details?: Record<string, any>;
649
+ });
650
+ }
651
+ /**
652
+ * OAuth2 Grant Not Found Error (404)
653
+ *
654
+ * Thrown by `DELETE /_auth/oauth2/grants/:id` when the caller owns no live grant
655
+ * of that id. A grant belonging to somebody else answers the same way as one
656
+ * that never existed — the id is a number in a URL, and telling the two apart
657
+ * would let anyone count other people's connected clients.
658
+ */
659
+ declare class OAuth2GrantNotFoundError extends NotFoundError {
660
+ constructor(data?: {
661
+ message?: string;
662
+ details?: Record<string, any>;
663
+ });
664
+ }
601
665
 
602
666
  type authErrors_AccountAlreadyExistsError = AccountAlreadyExistsError;
603
667
  declare const authErrors_AccountAlreadyExistsError: typeof AccountAlreadyExistsError;
@@ -653,6 +717,14 @@ type authErrors_NativeSignInUnsupportedError = NativeSignInUnsupportedError;
653
717
  declare const authErrors_NativeSignInUnsupportedError: typeof NativeSignInUnsupportedError;
654
718
  type authErrors_NonceKeyBindingError = NonceKeyBindingError;
655
719
  declare const authErrors_NonceKeyBindingError: typeof NonceKeyBindingError;
720
+ type authErrors_OAuth2AuthorizeRedirectError = OAuth2AuthorizeRedirectError;
721
+ declare const authErrors_OAuth2AuthorizeRedirectError: typeof OAuth2AuthorizeRedirectError;
722
+ type authErrors_OAuth2GrantNotFoundError = OAuth2GrantNotFoundError;
723
+ declare const authErrors_OAuth2GrantNotFoundError: typeof OAuth2GrantNotFoundError;
724
+ type authErrors_OAuth2RedirectUriMismatchError = OAuth2RedirectUriMismatchError;
725
+ declare const authErrors_OAuth2RedirectUriMismatchError: typeof OAuth2RedirectUriMismatchError;
726
+ type authErrors_OAuth2UnknownClientError = OAuth2UnknownClientError;
727
+ declare const authErrors_OAuth2UnknownClientError: typeof OAuth2UnknownClientError;
656
728
  type authErrors_PasskeyAlreadyRegisteredError = PasskeyAlreadyRegisteredError;
657
729
  declare const authErrors_PasskeyAlreadyRegisteredError: typeof PasskeyAlreadyRegisteredError;
658
730
  type authErrors_PasskeyChallengeError = PasskeyChallengeError;
@@ -684,7 +756,7 @@ declare const authErrors_VerificationTokenPurposeMismatchError: typeof Verificat
684
756
  type authErrors_VerificationTokenTargetMismatchError = VerificationTokenTargetMismatchError;
685
757
  declare const authErrors_VerificationTokenTargetMismatchError: typeof VerificationTokenTargetMismatchError;
686
758
  declare namespace authErrors {
687
- export { authErrors_AccountAlreadyExistsError as AccountAlreadyExistsError, authErrors_AccountDisabledError as AccountDisabledError, authErrors_AccountPendingDeletionError as AccountPendingDeletionError, authErrors_DeletionAlreadyRequestedError as DeletionAlreadyRequestedError, authErrors_DeletionNotRequestedError as DeletionNotRequestedError, authErrors_DeviceAuthAlreadyHandledError as DeviceAuthAlreadyHandledError, authErrors_DeviceAuthDeniedError as DeviceAuthDeniedError, authErrors_DeviceAuthExpiredError as DeviceAuthExpiredError, authErrors_DeviceAuthNotFoundError as DeviceAuthNotFoundError, authErrors_ImmediateDeletionNotAllowedError as ImmediateDeletionNotAllowedError, authErrors_InsufficientPermissionsError as InsufficientPermissionsError, authErrors_InsufficientRoleError as InsufficientRoleError, authErrors_InvalidCredentialsError as InvalidCredentialsError, authErrors_InvalidKeyFingerprintError as InvalidKeyFingerprintError, authErrors_InvalidSignupLinkError as InvalidSignupLinkError, authErrors_InvalidSignupSetupSessionError as InvalidSignupSetupSessionError, authErrors_InvalidSocialTokenError as InvalidSocialTokenError, authErrors_InvalidTokenError as InvalidTokenError, authErrors_InvalidVerificationCodeError as InvalidVerificationCodeError, authErrors_InvalidVerificationTokenError as InvalidVerificationTokenError, authErrors_KeyAlgorithmMismatchError as KeyAlgorithmMismatchError, authErrors_KeyExpiredError as KeyExpiredError, authErrors_KeyIdAlreadyRegisteredError as KeyIdAlreadyRegisteredError, authErrors_KeyNotFoundError as KeyNotFoundError, authErrors_LastRecoveryCredentialError as LastRecoveryCredentialError, authErrors_NativeSignInUnsupportedError as NativeSignInUnsupportedError, authErrors_NonceKeyBindingError as NonceKeyBindingError, authErrors_PasskeyAlreadyRegisteredError as PasskeyAlreadyRegisteredError, authErrors_PasskeyChallengeError as PasskeyChallengeError, authErrors_PasskeyConfigError as PasskeyConfigError, authErrors_PasskeyNotFoundError as PasskeyNotFoundError, authErrors_PasskeyVerificationError as PasskeyVerificationError, authErrors_PasswordResetLinkError as PasswordResetLinkError, authErrors_PasswordResetSessionError as PasswordResetSessionError, authErrors_RecentAuthenticationRequiredError as RecentAuthenticationRequiredError, authErrors_RegistrationRejectedError as RegistrationRejectedError, authErrors_ReservedUsernameError as ReservedUsernameError, authErrors_TokenExpiredError as TokenExpiredError, authErrors_UnverifiedEmailLinkError as UnverifiedEmailLinkError, authErrors_UsernameAlreadyTakenError as UsernameAlreadyTakenError, authErrors_VerificationTokenPurposeMismatchError as VerificationTokenPurposeMismatchError, authErrors_VerificationTokenTargetMismatchError as VerificationTokenTargetMismatchError };
759
+ export { authErrors_AccountAlreadyExistsError as AccountAlreadyExistsError, authErrors_AccountDisabledError as AccountDisabledError, authErrors_AccountPendingDeletionError as AccountPendingDeletionError, authErrors_DeletionAlreadyRequestedError as DeletionAlreadyRequestedError, authErrors_DeletionNotRequestedError as DeletionNotRequestedError, authErrors_DeviceAuthAlreadyHandledError as DeviceAuthAlreadyHandledError, authErrors_DeviceAuthDeniedError as DeviceAuthDeniedError, authErrors_DeviceAuthExpiredError as DeviceAuthExpiredError, authErrors_DeviceAuthNotFoundError as DeviceAuthNotFoundError, authErrors_ImmediateDeletionNotAllowedError as ImmediateDeletionNotAllowedError, authErrors_InsufficientPermissionsError as InsufficientPermissionsError, authErrors_InsufficientRoleError as InsufficientRoleError, authErrors_InvalidCredentialsError as InvalidCredentialsError, authErrors_InvalidKeyFingerprintError as InvalidKeyFingerprintError, authErrors_InvalidSignupLinkError as InvalidSignupLinkError, authErrors_InvalidSignupSetupSessionError as InvalidSignupSetupSessionError, authErrors_InvalidSocialTokenError as InvalidSocialTokenError, authErrors_InvalidTokenError as InvalidTokenError, authErrors_InvalidVerificationCodeError as InvalidVerificationCodeError, authErrors_InvalidVerificationTokenError as InvalidVerificationTokenError, authErrors_KeyAlgorithmMismatchError as KeyAlgorithmMismatchError, authErrors_KeyExpiredError as KeyExpiredError, authErrors_KeyIdAlreadyRegisteredError as KeyIdAlreadyRegisteredError, authErrors_KeyNotFoundError as KeyNotFoundError, authErrors_LastRecoveryCredentialError as LastRecoveryCredentialError, authErrors_NativeSignInUnsupportedError as NativeSignInUnsupportedError, authErrors_NonceKeyBindingError as NonceKeyBindingError, authErrors_OAuth2AuthorizeRedirectError as OAuth2AuthorizeRedirectError, authErrors_OAuth2GrantNotFoundError as OAuth2GrantNotFoundError, authErrors_OAuth2RedirectUriMismatchError as OAuth2RedirectUriMismatchError, authErrors_OAuth2UnknownClientError as OAuth2UnknownClientError, authErrors_PasskeyAlreadyRegisteredError as PasskeyAlreadyRegisteredError, authErrors_PasskeyChallengeError as PasskeyChallengeError, authErrors_PasskeyConfigError as PasskeyConfigError, authErrors_PasskeyNotFoundError as PasskeyNotFoundError, authErrors_PasskeyVerificationError as PasskeyVerificationError, authErrors_PasswordResetLinkError as PasswordResetLinkError, authErrors_PasswordResetSessionError as PasswordResetSessionError, authErrors_RecentAuthenticationRequiredError as RecentAuthenticationRequiredError, authErrors_RegistrationRejectedError as RegistrationRejectedError, authErrors_ReservedUsernameError as ReservedUsernameError, authErrors_TokenExpiredError as TokenExpiredError, authErrors_UnverifiedEmailLinkError as UnverifiedEmailLinkError, authErrors_UsernameAlreadyTakenError as UsernameAlreadyTakenError, authErrors_VerificationTokenPurposeMismatchError as VerificationTokenPurposeMismatchError, authErrors_VerificationTokenTargetMismatchError as VerificationTokenTargetMismatchError };
688
760
  }
689
761
 
690
762
  /**
@@ -693,4 +765,4 @@ declare namespace authErrors {
693
765
 
694
766
  declare const authErrorRegistry: ErrorRegistry;
695
767
 
696
- export { AccountAlreadyExistsError, AccountDisabledError, AccountPendingDeletionError, authErrors as AuthError, DeletionAlreadyRequestedError, DeletionNotRequestedError, DeviceAuthAlreadyHandledError, DeviceAuthDeniedError, DeviceAuthExpiredError, DeviceAuthNotFoundError, ImmediateDeletionNotAllowedError, InsufficientPermissionsError, InsufficientRoleError, InvalidCredentialsError, InvalidKeyFingerprintError, InvalidSignupLinkError, InvalidSignupSetupSessionError, InvalidSocialTokenError, InvalidTokenError, InvalidVerificationCodeError, InvalidVerificationTokenError, KeyAlgorithmMismatchError, KeyExpiredError, KeyIdAlreadyRegisteredError, KeyNotFoundError, LastRecoveryCredentialError, NativeSignInUnsupportedError, NonceKeyBindingError, PasskeyAlreadyRegisteredError, PasskeyChallengeError, PasskeyConfigError, PasskeyNotFoundError, PasskeyVerificationError, PasswordResetLinkError, PasswordResetSessionError, RecentAuthenticationRequiredError, RegistrationRejectedError, ReservedUsernameError, TokenExpiredError, UnverifiedEmailLinkError, UsernameAlreadyTakenError, VerificationTokenPurposeMismatchError, VerificationTokenTargetMismatchError, authErrorRegistry };
768
+ export { AccountAlreadyExistsError, AccountDisabledError, AccountPendingDeletionError, authErrors as AuthError, DeletionAlreadyRequestedError, DeletionNotRequestedError, DeviceAuthAlreadyHandledError, DeviceAuthDeniedError, DeviceAuthExpiredError, DeviceAuthNotFoundError, ImmediateDeletionNotAllowedError, InsufficientPermissionsError, InsufficientRoleError, InvalidCredentialsError, InvalidKeyFingerprintError, InvalidSignupLinkError, InvalidSignupSetupSessionError, InvalidSocialTokenError, InvalidTokenError, InvalidVerificationCodeError, InvalidVerificationTokenError, KeyAlgorithmMismatchError, KeyExpiredError, KeyIdAlreadyRegisteredError, KeyNotFoundError, LastRecoveryCredentialError, NativeSignInUnsupportedError, NonceKeyBindingError, OAuth2AuthorizeRedirectError, OAuth2GrantNotFoundError, OAuth2RedirectUriMismatchError, OAuth2UnknownClientError, PasskeyAlreadyRegisteredError, PasskeyChallengeError, PasskeyConfigError, PasskeyNotFoundError, PasskeyVerificationError, PasswordResetLinkError, PasswordResetSessionError, RecentAuthenticationRequiredError, RegistrationRejectedError, ReservedUsernameError, TokenExpiredError, UnverifiedEmailLinkError, UsernameAlreadyTakenError, VerificationTokenPurposeMismatchError, VerificationTokenTargetMismatchError, authErrorRegistry };
package/dist/errors.js CHANGED
@@ -37,6 +37,10 @@ __export(auth_errors_exports, {
37
37
  LastRecoveryCredentialError: () => LastRecoveryCredentialError,
38
38
  NativeSignInUnsupportedError: () => NativeSignInUnsupportedError,
39
39
  NonceKeyBindingError: () => NonceKeyBindingError,
40
+ OAuth2AuthorizeRedirectError: () => OAuth2AuthorizeRedirectError,
41
+ OAuth2GrantNotFoundError: () => OAuth2GrantNotFoundError,
42
+ OAuth2RedirectUriMismatchError: () => OAuth2RedirectUriMismatchError,
43
+ OAuth2UnknownClientError: () => OAuth2UnknownClientError,
40
44
  PasskeyAlreadyRegisteredError: () => PasskeyAlreadyRegisteredError,
41
45
  PasskeyChallengeError: () => PasskeyChallengeError,
42
46
  PasskeyConfigError: () => PasskeyConfigError,
@@ -409,6 +413,44 @@ var PasskeyConfigError = class extends HttpError {
409
413
  this.name = "PasskeyConfigError";
410
414
  }
411
415
  };
416
+ var OAuth2UnknownClientError = class extends ValidationError {
417
+ constructor(data = {}) {
418
+ super({
419
+ message: data.message || "Unknown OAuth client",
420
+ details: { error: "unknown_client", ...data.details }
421
+ });
422
+ this.name = "OAuth2UnknownClientError";
423
+ }
424
+ };
425
+ var OAuth2RedirectUriMismatchError = class extends ValidationError {
426
+ constructor(data = {}) {
427
+ super({
428
+ message: data.message || "redirect_uri does not match a registered URI for this client",
429
+ details: { error: "redirect_uri_mismatch", ...data.details }
430
+ });
431
+ this.name = "OAuth2RedirectUriMismatchError";
432
+ }
433
+ };
434
+ var OAuth2AuthorizeRedirectError = class extends ValidationError {
435
+ constructor(data) {
436
+ super({
437
+ message: data.message || `OAuth authorize request refused: ${data.error}`,
438
+ details: {
439
+ error: data.error,
440
+ redirectUri: data.redirectUri,
441
+ state: data.state,
442
+ ...data.details
443
+ }
444
+ });
445
+ this.name = "OAuth2AuthorizeRedirectError";
446
+ }
447
+ };
448
+ var OAuth2GrantNotFoundError = class extends NotFoundError {
449
+ constructor(data = {}) {
450
+ super({ message: data.message || "No such connected application", details: data.details });
451
+ this.name = "OAuth2GrantNotFoundError";
452
+ }
453
+ };
412
454
 
413
455
  // src/errors/index.ts
414
456
  var authErrorRegistry = new ErrorRegistry();
@@ -454,7 +496,11 @@ authErrorRegistry.append([
454
496
  PasskeyAlreadyRegisteredError,
455
497
  RecentAuthenticationRequiredError,
456
498
  LastRecoveryCredentialError,
457
- PasskeyConfigError
499
+ PasskeyConfigError,
500
+ OAuth2UnknownClientError,
501
+ OAuth2RedirectUriMismatchError,
502
+ OAuth2AuthorizeRedirectError,
503
+ OAuth2GrantNotFoundError
458
504
  ]);
459
505
  export {
460
506
  AccountAlreadyExistsError,
@@ -485,6 +531,10 @@ export {
485
531
  LastRecoveryCredentialError,
486
532
  NativeSignInUnsupportedError,
487
533
  NonceKeyBindingError,
534
+ OAuth2AuthorizeRedirectError,
535
+ OAuth2GrantNotFoundError,
536
+ OAuth2RedirectUriMismatchError,
537
+ OAuth2UnknownClientError,
488
538
  PasskeyAlreadyRegisteredError,
489
539
  PasskeyChallengeError,
490
540
  PasskeyConfigError,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors/index.ts","../src/errors/auth-errors.ts"],"sourcesContent":["/**\n * Auth Error Exports\n */\n\nimport { ErrorRegistry } from '@spfn/core/errors';\n\nimport {\n InvalidCredentialsError,\n InvalidTokenError,\n InvalidSocialTokenError,\n TokenExpiredError,\n KeyExpiredError,\n AccountDisabledError,\n AccountPendingDeletionError,\n DeletionAlreadyRequestedError,\n DeletionNotRequestedError,\n ImmediateDeletionNotAllowedError,\n AccountAlreadyExistsError,\n RegistrationRejectedError,\n ReservedUsernameError,\n UsernameAlreadyTakenError,\n InvalidVerificationCodeError,\n InvalidVerificationTokenError,\n InvalidKeyFingerprintError,\n KeyAlgorithmMismatchError,\n NonceKeyBindingError,\n NativeSignInUnsupportedError,\n UnverifiedEmailLinkError,\n InvalidSignupLinkError,\n InvalidSignupSetupSessionError,\n PasswordResetLinkError,\n PasswordResetSessionError,\n KeyNotFoundError,\n KeyIdAlreadyRegisteredError,\n DeviceAuthNotFoundError,\n DeviceAuthExpiredError,\n DeviceAuthAlreadyHandledError,\n DeviceAuthDeniedError,\n VerificationTokenPurposeMismatchError,\n VerificationTokenTargetMismatchError,\n InsufficientPermissionsError,\n InsufficientRoleError,\n PasskeyChallengeError,\n PasskeyVerificationError,\n PasskeyNotFoundError,\n PasskeyAlreadyRegisteredError,\n RecentAuthenticationRequiredError,\n LastRecoveryCredentialError,\n PasskeyConfigError,\n} from './auth-errors';\n\nexport {\n InvalidCredentialsError,\n InvalidTokenError,\n InvalidSocialTokenError,\n TokenExpiredError,\n KeyExpiredError,\n AccountDisabledError,\n AccountPendingDeletionError,\n DeletionAlreadyRequestedError,\n DeletionNotRequestedError,\n ImmediateDeletionNotAllowedError,\n AccountAlreadyExistsError,\n RegistrationRejectedError,\n ReservedUsernameError,\n UsernameAlreadyTakenError,\n InvalidVerificationCodeError,\n InvalidVerificationTokenError,\n InvalidKeyFingerprintError,\n KeyAlgorithmMismatchError,\n NonceKeyBindingError,\n NativeSignInUnsupportedError,\n UnverifiedEmailLinkError,\n InvalidSignupLinkError,\n InvalidSignupSetupSessionError,\n PasswordResetLinkError,\n PasswordResetSessionError,\n KeyNotFoundError,\n KeyIdAlreadyRegisteredError,\n DeviceAuthNotFoundError,\n DeviceAuthExpiredError,\n DeviceAuthAlreadyHandledError,\n DeviceAuthDeniedError,\n VerificationTokenPurposeMismatchError,\n VerificationTokenTargetMismatchError,\n InsufficientPermissionsError,\n InsufficientRoleError,\n PasskeyChallengeError,\n PasskeyVerificationError,\n PasskeyNotFoundError,\n PasskeyAlreadyRegisteredError,\n RecentAuthenticationRequiredError,\n LastRecoveryCredentialError,\n PasskeyConfigError,\n} from './auth-errors';\n\nexport const authErrorRegistry = new ErrorRegistry();\nauthErrorRegistry.append([\n InvalidCredentialsError,\n InvalidTokenError,\n InvalidSocialTokenError,\n TokenExpiredError,\n KeyExpiredError,\n AccountDisabledError,\n AccountPendingDeletionError,\n DeletionAlreadyRequestedError,\n DeletionNotRequestedError,\n ImmediateDeletionNotAllowedError,\n AccountAlreadyExistsError,\n RegistrationRejectedError,\n ReservedUsernameError,\n UsernameAlreadyTakenError,\n InvalidVerificationCodeError,\n InvalidVerificationTokenError,\n InvalidKeyFingerprintError,\n KeyAlgorithmMismatchError,\n NonceKeyBindingError,\n NativeSignInUnsupportedError,\n UnverifiedEmailLinkError,\n InvalidSignupLinkError,\n InvalidSignupSetupSessionError,\n PasswordResetLinkError,\n PasswordResetSessionError,\n KeyNotFoundError,\n KeyIdAlreadyRegisteredError,\n DeviceAuthNotFoundError,\n DeviceAuthExpiredError,\n DeviceAuthAlreadyHandledError,\n DeviceAuthDeniedError,\n VerificationTokenPurposeMismatchError,\n VerificationTokenTargetMismatchError,\n InsufficientPermissionsError,\n InsufficientRoleError,\n PasskeyChallengeError,\n PasskeyVerificationError,\n PasskeyNotFoundError,\n PasskeyAlreadyRegisteredError,\n RecentAuthenticationRequiredError,\n LastRecoveryCredentialError,\n PasskeyConfigError,\n]);\n\nexport * as AuthError from './auth-errors';\n","/**\n * Authentication & Authorization Error Classes\n *\n * Custom error classes for auth-specific scenarios\n */\n\nimport {\n ValidationError,\n UnauthorizedError,\n ForbiddenError,\n ConflictError,\n NotFoundError,\n HttpError,\n} from '@spfn/core/errors';\n\n/**\n * Invalid Credentials Error (401)\n *\n * Thrown when login credentials are incorrect\n */\nexport class InvalidCredentialsError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Invalid credentials', details: data.details });\n this.name = 'InvalidCredentialsError';\n }\n}\n\n/**\n * Invalid Token Error (401)\n *\n * Thrown when authentication token is invalid or malformed\n */\nexport class InvalidTokenError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Invalid authentication token', details: data.details });\n this.name = 'InvalidTokenError';\n }\n}\n\n/**\n * Invalid Social Token Error (401)\n *\n * Thrown when a social provider id_token fails verification\n * (bad signature, wrong issuer/audience, expired, or nonce mismatch).\n */\nexport class InvalidSocialTokenError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Invalid social id_token', details: data.details });\n this.name = 'InvalidSocialTokenError';\n }\n}\n\n/**\n * Token Expired Error (401)\n *\n * Thrown when authentication token has expired\n */\nexport class TokenExpiredError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Authentication token has expired', details: data.details });\n this.name = 'TokenExpiredError';\n }\n}\n\n/**\n * Key Expired Error (401)\n *\n * Thrown when public key has expired\n */\nexport class KeyExpiredError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Public key has expired', details: data.details });\n this.name = 'KeyExpiredError';\n }\n}\n\n/**\n * Account Disabled Error (403)\n *\n * Thrown when user account is disabled or inactive\n */\nexport class AccountDisabledError extends ForbiddenError\n{\n constructor(data: { status?: string; message?: string; details?: Record<string, any> } = {})\n {\n const status = data.status || 'disabled';\n super({\n message: data.message || `Account is ${status}`,\n details: { status, ...data.details },\n });\n this.name = 'AccountDisabledError';\n }\n}\n\n/**\n * Account Pending Deletion Error (403)\n *\n * Thrown on login (password/OAuth/authenticate) when the account is within its\n * deletion grace period. Carries `purgeScheduledAt` so the client can offer a\n * recovery flow instead of a generic \"disabled\" message.\n */\nexport class AccountPendingDeletionError extends ForbiddenError\n{\n constructor(data: { purgeScheduledAt?: string; message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Account is scheduled for deletion',\n details: { status: 'pending_deletion', purgeScheduledAt: data.purgeScheduledAt, ...data.details },\n });\n this.name = 'AccountPendingDeletionError';\n }\n}\n\n/**\n * Deletion Already Requested Error (409)\n *\n * Thrown when requesting deletion for an account that already has a pending\n * deletion request (or has already been purged).\n */\nexport class DeletionAlreadyRequestedError extends ConflictError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Account deletion has already been requested', details: data.details });\n this.name = 'DeletionAlreadyRequestedError';\n }\n}\n\n/**\n * Deletion Not Requested Error (404)\n *\n * Thrown when trying to cancel/purge a deletion for an account that has no\n * pending deletion request.\n */\nexport class DeletionNotRequestedError extends NotFoundError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'No pending account deletion request found', details: data.details });\n this.name = 'DeletionNotRequestedError';\n }\n}\n\n/**\n * Immediate Deletion Not Allowed Error (403)\n *\n * Thrown when a self-service caller requests `immediate: true` but the server\n * has not enabled `deletion.allowSelfImmediate`.\n */\nexport class ImmediateDeletionNotAllowedError extends ForbiddenError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Immediate self-service deletion is not enabled', details: data.details });\n this.name = 'ImmediateDeletionNotAllowedError';\n }\n}\n\n/**\n * Account Already Exists Error (409)\n *\n * Thrown when trying to register with existing email/phone\n */\nexport class AccountAlreadyExistsError extends ConflictError\n{\n constructor(data: { identifier?: string; identifierType?: 'email' | 'phone'; message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Account already exists',\n details: {\n identifier: data.identifier,\n identifierType: data.identifierType,\n ...data.details,\n },\n });\n this.name = 'AccountAlreadyExistsError';\n }\n}\n\n/**\n * Registration Rejected Error (403)\n *\n * Thrown by the app-injected beforeRegister hook to reject a registration\n * (age gate, domain restriction, block list, ...)\n */\nexport class RegistrationRejectedError extends ForbiddenError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Registration rejected', details: data.details });\n this.name = 'RegistrationRejectedError';\n }\n}\n\n/**\n * Invalid Verification Code Error (400)\n *\n * Thrown when verification code is invalid, expired, or already used\n */\nexport class InvalidVerificationCodeError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Invalid verification code', details: data.details });\n this.name = 'InvalidVerificationCodeError';\n }\n}\n\n/**\n * Invalid Verification Token Error (400)\n *\n * Thrown when verification token is invalid or expired\n */\nexport class InvalidVerificationTokenError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Invalid or expired verification token', details: data.details });\n this.name = 'InvalidVerificationTokenError';\n }\n}\n\n/**\n * Key ID Already Registered Error (409)\n *\n * Thrown when a sign-in submits a keyId that is already taken — the client's own\n * revoked keyId, or a keyId belonging to another user. `keyId` is unique across\n * all users, so either case would collide on insert.\n *\n * The same error covers both cases on purpose: a distinguishable response would\n * let a caller probe whether an arbitrary keyId exists. Revoked stays revoked —\n * the client must generate a fresh keyId and retry.\n */\nexport class KeyIdAlreadyRegisteredError extends ConflictError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This keyId is already registered. Generate a new keyId and retry.',\n details: data.details,\n });\n this.name = 'KeyIdAlreadyRegisteredError';\n }\n}\n\n/**\n * Invalid Key Fingerprint Error (400)\n *\n * Thrown when public key fingerprint doesn't match the public key\n */\nexport class InvalidKeyFingerprintError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Invalid key fingerprint', details: data.details });\n this.name = 'InvalidKeyFingerprintError';\n }\n}\n\n/**\n * Key Algorithm Mismatch Error (400)\n *\n * Thrown when the submitted public key's SPKI type is not the one its declared\n * algorithm needs — a P-256 EC key declared RS256, an RSA key declared ES256, a\n * curve other than P-256 declared ES256, or bytes that are no SPKI key at all.\n *\n * The algorithm is stored beside the key and read back at proof verification;\n * nothing re-derives it from the key material. So a mismatch accepted here\n * surfaces only after the device believes it is enrolled, on every request it\n * then makes. It is refused at registration instead, wherever key material is\n * stored: register, login, rotate, invitation acceptance and device start.\n */\nexport class KeyAlgorithmMismatchError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Public key type does not match the declared algorithm',\n details: data.details,\n });\n this.name = 'KeyAlgorithmMismatchError';\n }\n}\n\n/**\n * Key Not Found Error (404)\n *\n * Thrown when a key operation names a keyId the caller does not own. It says\n * nothing about whether that keyId exists on another account — the repository\n * scopes every lookup by userId, so the answer is only ever \"not yours\".\n */\nexport class KeyNotFoundError extends NotFoundError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Key not found', details: data.details });\n this.name = 'KeyNotFoundError';\n }\n}\n\n/**\n * Device Auth Not Found Error (404)\n *\n * Thrown when a device-code operation names a code the server cannot act on: one\n * that was never issued, and one whose record has already been consumed.\n *\n * Those two are answered identically on purpose. A consumed record is a login\n * that finished, and saying so would tell whoever holds the code that it was\n * real — which is the difference between guessing at random and knowing a guess\n * landed. Every route that accepts a code is rate limited for the same reason.\n */\nexport class DeviceAuthNotFoundError extends NotFoundError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Device authorization not found', details: data.details });\n this.name = 'DeviceAuthNotFoundError';\n }\n}\n\n/**\n * Device Auth Expired Error (400)\n *\n * Thrown when a device-code operation names a record whose TTL has run out,\n * whatever state it is in. The waiting device starts again; the approver is told\n * the code on the other screen is stale.\n *\n * 400 rather than 401: on the approve and deny routes the caller's own session is\n * fine, and answering 401 would send a signed-in user to a login screen over a\n * code that simply sat too long.\n */\nexport class DeviceAuthExpiredError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This device code has expired. Start again on the other device.',\n details: data.details,\n });\n this.name = 'DeviceAuthExpiredError';\n }\n}\n\n/**\n * Device Auth Already Handled Error (409)\n *\n * Thrown when an approve, deny or info call names a record that has already been\n * approved or denied. A decision on a device is made once — a second approval\n * would let one code be answered twice, and re-approving a record the owner\n * denied would undo the refusal.\n *\n * This is also what the loser of two concurrent approvals sees, since the\n * transition names the state it moves from and only one call can match it.\n */\nexport class DeviceAuthAlreadyHandledError extends ConflictError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This device request has already been answered',\n details: data.details,\n });\n this.name = 'DeviceAuthAlreadyHandledError';\n }\n}\n\n/**\n * Device Auth Denied Error (403)\n *\n * Thrown when the waiting device polls a record its owner refused. Distinct from\n * a pending answer, and distinct from a code that does not exist: the device\n * asked a person and the person said no, so it should stop polling and say so\n * rather than time out looking like a network fault.\n */\nexport class DeviceAuthDeniedError extends ForbiddenError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This device request was denied',\n details: data.details,\n });\n this.name = 'DeviceAuthDeniedError';\n }\n}\n\n/**\n * Nonce Key Binding Error (400)\n *\n * Thrown when a native id_token sign-in submits a nonce that is not the public\n * key's fingerprint. The nonce is what the provider echoed back inside the\n * id_token, so tying it to the key is what proves the id_token and the key came\n * from the same device — see the native section of the README.\n */\nexport class NonceKeyBindingError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'nonce must be the fingerprint of the submitted public key',\n details: data.details,\n });\n this.name = 'NonceKeyBindingError';\n }\n}\n\n/**\n * Native Sign-In Unsupported Error (400)\n *\n * Thrown when a provider is asked for native id_token sign-in and has no\n * implementation for it — a server configuration fact, not something the user\n * did. A client reading this hides that provider's native button instead of\n * asking the user to try again.\n *\n * Split out of ValidationError because the other native-enrollment refusal\n * (linking to an account whose email the provider never verified) needs a\n * different response from the app, and one code cannot ask for two.\n */\nexport class NativeSignInUnsupportedError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This provider does not support native id_token sign-in.',\n details: data.details,\n });\n this.name = 'NativeSignInUnsupportedError';\n }\n}\n\n/**\n * Invalid Signup Link Error (400)\n *\n * Thrown when an emailed signup confirmation link is unknown, expired, already\n * consumed, or superseded by a newer request for the same address.\n *\n * One error for all four states, on purpose. Distinguishing \"expired\" from\n * \"unknown\" tells a caller holding a random token whether it named a real\n * pending signup, which is exactly the enumeration the request step avoids. The\n * specific reason is logged.\n */\nexport class InvalidSignupLinkError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This signup link is no longer valid. Request a new one.',\n details: data.details,\n });\n this.name = 'InvalidSignupLinkError';\n }\n}\n\n/**\n * Invalid Signup Setup Session Error (401)\n *\n * Thrown when the password-setup session backing a verified-email signup is\n * missing, unknown, expired, superseded or already used.\n *\n * One error for every one of those, on purpose: telling a caller which of them\n * applies tells them whether an address is mid-signup, which is the same\n * enumeration the request step is careful not to leak.\n */\nexport class InvalidSignupSetupSessionError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Password setup session is invalid or has expired. Start the signup again.',\n details: data.details,\n });\n this.name = 'InvalidSignupSetupSessionError';\n }\n}\n\n/**\n * Password Reset Link Error (401)\n *\n * Thrown when an emailed password reset link is unknown, expired, already\n * consumed, superseded by a newer request, or belongs to an account that is no\n * longer active.\n *\n * One error for every one of those, on purpose. Distinguishing \"expired\" from\n * \"unknown\" tells a caller holding a random token whether it named a real\n * pending reset, which is exactly the enumeration the request step avoids. The\n * specific reason is logged.\n */\nexport class PasswordResetLinkError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This password reset link is no longer valid. Request a new one.',\n details: data.details,\n });\n this.name = 'PasswordResetLinkError';\n }\n}\n\n/**\n * Password Reset Session Error (401)\n *\n * Thrown when the password-setup session backing a reset is missing, unknown,\n * expired, superseded or already used.\n *\n * One error for every one of those, for the same reason the link error is one\n * error: which of them applies tells a caller whether an account is mid-reset,\n * and that is the enumeration the request step is careful not to leak.\n */\nexport class PasswordResetSessionError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Password reset session is invalid or has expired. Request a new link.',\n details: data.details,\n });\n this.name = 'PasswordResetSessionError';\n }\n}\n\n/**\n * Unverified Email Link Error (400)\n *\n * Thrown when a social identity carries an email that already belongs to an\n * account, but the provider never verified it. Linking on an unverified email\n * is account takeover, so the refusal stands and the user is sent to a path\n * that proves the address.\n *\n * This says an account exists for that address. It is not a leak introduced\n * here: the message this replaces already stated the same fact in prose, and\n * the paths that must not disclose account existence (password login, deletion\n * re-auth, verification issuance) answer uniformly and are untouched.\n */\nexport class UnverifiedEmailLinkError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message\n || 'Cannot link to existing account with unverified email. Please verify your email with the provider first.',\n details: data.details,\n });\n this.name = 'UnverifiedEmailLinkError';\n }\n}\n\n/**\n * Verification Token Purpose Mismatch Error (400)\n *\n * Thrown when verification token purpose doesn't match expected purpose\n */\nexport class VerificationTokenPurposeMismatchError extends ValidationError\n{\n constructor(data: { expected?: string; actual?: string; message?: string; details?: Record<string, any> } = {})\n {\n const expected = data.expected || 'unknown';\n const actual = data.actual || 'unknown';\n super({\n message: data.message || `Verification token is for ${actual}, but ${expected} was expected`,\n details: { expected, actual, ...data.details },\n });\n this.name = 'VerificationTokenPurposeMismatchError';\n }\n}\n\n/**\n * Verification Token Target Mismatch Error (400)\n *\n * Thrown when verification token target doesn't match provided email/phone\n */\nexport class VerificationTokenTargetMismatchError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Verification token does not match provided email/phone',\n details: data.details,\n });\n this.name = 'VerificationTokenTargetMismatchError';\n }\n}\n\n/**\n * Reserved Username Error (400)\n *\n * Thrown when trying to use a reserved/prohibited username\n */\nexport class ReservedUsernameError extends ValidationError\n{\n constructor(data: { username?: string; message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This username is reserved',\n details: { username: data.username, ...data.details },\n });\n this.name = 'ReservedUsernameError';\n }\n}\n\n/**\n * Username Already Taken Error (409)\n *\n * Thrown when trying to set a username that is already in use\n */\nexport class UsernameAlreadyTakenError extends ConflictError\n{\n constructor(data: { username?: string; message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Username is already taken',\n details: { username: data.username, ...data.details },\n });\n this.name = 'UsernameAlreadyTakenError';\n }\n}\n\n/**\n * Insufficient Permissions Error (403)\n *\n * Thrown when user lacks required permissions for the operation\n */\nexport class InsufficientPermissionsError extends ForbiddenError\n{\n constructor(data: { requiredPermissions?: string[]; message?: string; details?: Record<string, any> } = {})\n {\n const requiredPermissions = data.requiredPermissions || [];\n super({\n message: data.message || `Missing required permissions: ${requiredPermissions.join(', ')}`,\n details: { requiredPermissions, ...data.details },\n });\n this.name = 'InsufficientPermissionsError';\n }\n}\n\n/**\n * Insufficient Role Error (403)\n *\n * Thrown when user lacks required role for the operation\n */\nexport class InsufficientRoleError extends ForbiddenError\n{\n constructor(data: { requiredRoles?: string[]; message?: string; details?: Record<string, any> } = {})\n {\n const requiredRoles = data.requiredRoles || [];\n super({\n message: data.message || `Required roles: ${requiredRoles.join(', ')}`,\n details: { requiredRoles, ...data.details },\n });\n this.name = 'InsufficientRoleError';\n }\n}\n\n/**\n * Passkey Challenge Error (401)\n *\n * Thrown when the challenge a WebAuthn ceremony presents is unknown, expired,\n * already spent, minted for the other ceremony, or minted for another account.\n *\n * One error for all five, on purpose. Telling a caller which applies tells them\n * whether a challenge they did not mint exists and what it was for, and the\n * remedy is the same in every case: start the ceremony again.\n */\nexport class PasskeyChallengeError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This passkey challenge is no longer valid. Try again.',\n details: data.details,\n });\n this.name = 'PasskeyChallengeError';\n }\n}\n\n/**\n * Passkey Verification Error (401)\n *\n * Thrown when an assertion or attestation does not verify: wrong origin, wrong\n * rpId, a bad signature, a regressed signature counter — or, on login, a\n * credential that is unknown or has been revoked.\n *\n * Those last two share this error with the cryptographic failures deliberately.\n * A distinct \"no such passkey\" would answer, to anyone holding a credential id,\n * whether it is enrolled here — and a revoked credential answering differently\n * from an unknown one would say the account once had it.\n */\nexport class PasskeyVerificationError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Passkey verification failed.',\n details: data.details,\n });\n this.name = 'PasskeyVerificationError';\n }\n}\n\n/**\n * Passkey Not Found Error (404)\n *\n * Thrown when a management operation names a passkey the caller does not own,\n * or one already revoked. Every lookup is owner-scoped, so the answer is only\n * ever \"not yours\" and says nothing about other accounts.\n */\nexport class PasskeyNotFoundError extends NotFoundError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Passkey not found',\n details: data.details,\n });\n this.name = 'PasskeyNotFoundError';\n }\n}\n\n/**\n * Passkey Already Registered Error (409)\n *\n * Thrown when the credential presented for enrollment is already on file for\n * some account — including one that revoked it. A credential id is reserved for\n * good once used, so this is also the answer to re-enrolling one's own revoked\n * passkey.\n */\nexport class PasskeyAlreadyRegisteredError extends ConflictError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This passkey is already registered',\n details: data.details,\n });\n this.name = 'PasskeyAlreadyRegisteredError';\n }\n}\n\n/**\n * Recent Authentication Required Error (403)\n *\n * Thrown when enrolling or revoking a passkey from a session that proved itself\n * too long ago and carried no password. Clients branch on `code` to know to\n * prompt for the password rather than to show a generic refusal, so the code is\n * a stable field rather than a message they would have to match on.\n */\nexport class RecentAuthenticationRequiredError extends ForbiddenError\n{\n readonly code = 'RECENT_AUTH_REQUIRED';\n\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Confirm it is you before changing passkeys. Sign in again or send your current password.',\n details: data.details,\n });\n this.name = 'RecentAuthenticationRequiredError';\n }\n}\n\n/**\n * Last Recovery Credential Error (409)\n *\n * Thrown when revoking a passkey would leave the account with no way back in:\n * no other live passkey, no password, and no linked social account. There is no\n * password reset in this package, so that state is not recoverable by support\n * either — the refusal is the only thing standing between the owner and a\n * locked account.\n *\n * Clients branch on `code` to offer \"set a password first\" instead of a generic\n * refusal.\n */\nexport class LastRecoveryCredentialError extends ConflictError\n{\n readonly code = 'LAST_RECOVERY_CREDENTIAL';\n\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message\n || 'This is the only way you can sign in. Set a password or link a social account before removing it.',\n details: data.details,\n });\n this.name = 'LastRecoveryCredentialError';\n }\n}\n\n/**\n * Passkey Config Error (500)\n *\n * Thrown at boot when the passkey relying-party configuration cannot be honoured\n * — an origin that is not https outside localhost, an origin off the rpId, an\n * unsupported user-verification value.\n *\n * A refusal at boot rather than at the first ceremony: every one of these makes\n * every passkey operation fail, and the drift is between environments, so the\n * deploy that introduces it is where it has to surface.\n */\nexport class PasskeyConfigError extends HttpError\n{\n constructor(data: { message: string; details?: Record<string, any> })\n {\n super({\n message: data.message,\n statusCode: 500,\n details: data.details,\n });\n this.name = 'PasskeyConfigError';\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/errors/index.ts","../src/errors/auth-errors.ts"],"sourcesContent":["/**\n * Auth Error Exports\n */\n\nimport { ErrorRegistry } from '@spfn/core/errors';\n\nimport {\n InvalidCredentialsError,\n InvalidTokenError,\n InvalidSocialTokenError,\n TokenExpiredError,\n KeyExpiredError,\n AccountDisabledError,\n AccountPendingDeletionError,\n DeletionAlreadyRequestedError,\n DeletionNotRequestedError,\n ImmediateDeletionNotAllowedError,\n AccountAlreadyExistsError,\n RegistrationRejectedError,\n ReservedUsernameError,\n UsernameAlreadyTakenError,\n InvalidVerificationCodeError,\n InvalidVerificationTokenError,\n InvalidKeyFingerprintError,\n KeyAlgorithmMismatchError,\n NonceKeyBindingError,\n NativeSignInUnsupportedError,\n UnverifiedEmailLinkError,\n InvalidSignupLinkError,\n InvalidSignupSetupSessionError,\n PasswordResetLinkError,\n PasswordResetSessionError,\n KeyNotFoundError,\n KeyIdAlreadyRegisteredError,\n DeviceAuthNotFoundError,\n DeviceAuthExpiredError,\n DeviceAuthAlreadyHandledError,\n DeviceAuthDeniedError,\n VerificationTokenPurposeMismatchError,\n VerificationTokenTargetMismatchError,\n InsufficientPermissionsError,\n InsufficientRoleError,\n PasskeyChallengeError,\n PasskeyVerificationError,\n PasskeyNotFoundError,\n PasskeyAlreadyRegisteredError,\n RecentAuthenticationRequiredError,\n LastRecoveryCredentialError,\n PasskeyConfigError,\n OAuth2UnknownClientError,\n OAuth2RedirectUriMismatchError,\n OAuth2AuthorizeRedirectError,\n OAuth2GrantNotFoundError,\n} from './auth-errors';\n\nexport {\n InvalidCredentialsError,\n InvalidTokenError,\n InvalidSocialTokenError,\n TokenExpiredError,\n KeyExpiredError,\n AccountDisabledError,\n AccountPendingDeletionError,\n DeletionAlreadyRequestedError,\n DeletionNotRequestedError,\n ImmediateDeletionNotAllowedError,\n AccountAlreadyExistsError,\n RegistrationRejectedError,\n ReservedUsernameError,\n UsernameAlreadyTakenError,\n InvalidVerificationCodeError,\n InvalidVerificationTokenError,\n InvalidKeyFingerprintError,\n KeyAlgorithmMismatchError,\n NonceKeyBindingError,\n NativeSignInUnsupportedError,\n UnverifiedEmailLinkError,\n InvalidSignupLinkError,\n InvalidSignupSetupSessionError,\n PasswordResetLinkError,\n PasswordResetSessionError,\n KeyNotFoundError,\n KeyIdAlreadyRegisteredError,\n DeviceAuthNotFoundError,\n DeviceAuthExpiredError,\n DeviceAuthAlreadyHandledError,\n DeviceAuthDeniedError,\n VerificationTokenPurposeMismatchError,\n VerificationTokenTargetMismatchError,\n InsufficientPermissionsError,\n InsufficientRoleError,\n PasskeyChallengeError,\n PasskeyVerificationError,\n PasskeyNotFoundError,\n PasskeyAlreadyRegisteredError,\n RecentAuthenticationRequiredError,\n LastRecoveryCredentialError,\n PasskeyConfigError,\n OAuth2UnknownClientError,\n OAuth2RedirectUriMismatchError,\n OAuth2AuthorizeRedirectError,\n OAuth2GrantNotFoundError,\n} from './auth-errors';\n\nexport const authErrorRegistry = new ErrorRegistry();\nauthErrorRegistry.append([\n InvalidCredentialsError,\n InvalidTokenError,\n InvalidSocialTokenError,\n TokenExpiredError,\n KeyExpiredError,\n AccountDisabledError,\n AccountPendingDeletionError,\n DeletionAlreadyRequestedError,\n DeletionNotRequestedError,\n ImmediateDeletionNotAllowedError,\n AccountAlreadyExistsError,\n RegistrationRejectedError,\n ReservedUsernameError,\n UsernameAlreadyTakenError,\n InvalidVerificationCodeError,\n InvalidVerificationTokenError,\n InvalidKeyFingerprintError,\n KeyAlgorithmMismatchError,\n NonceKeyBindingError,\n NativeSignInUnsupportedError,\n UnverifiedEmailLinkError,\n InvalidSignupLinkError,\n InvalidSignupSetupSessionError,\n PasswordResetLinkError,\n PasswordResetSessionError,\n KeyNotFoundError,\n KeyIdAlreadyRegisteredError,\n DeviceAuthNotFoundError,\n DeviceAuthExpiredError,\n DeviceAuthAlreadyHandledError,\n DeviceAuthDeniedError,\n VerificationTokenPurposeMismatchError,\n VerificationTokenTargetMismatchError,\n InsufficientPermissionsError,\n InsufficientRoleError,\n PasskeyChallengeError,\n PasskeyVerificationError,\n PasskeyNotFoundError,\n PasskeyAlreadyRegisteredError,\n RecentAuthenticationRequiredError,\n LastRecoveryCredentialError,\n PasskeyConfigError,\n OAuth2UnknownClientError,\n OAuth2RedirectUriMismatchError,\n OAuth2AuthorizeRedirectError,\n OAuth2GrantNotFoundError,\n]);\n\nexport * as AuthError from './auth-errors';\n","/**\n * Authentication & Authorization Error Classes\n *\n * Custom error classes for auth-specific scenarios\n */\n\nimport {\n ValidationError,\n UnauthorizedError,\n ForbiddenError,\n ConflictError,\n NotFoundError,\n HttpError,\n} from '@spfn/core/errors';\n\n/**\n * Invalid Credentials Error (401)\n *\n * Thrown when login credentials are incorrect\n */\nexport class InvalidCredentialsError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Invalid credentials', details: data.details });\n this.name = 'InvalidCredentialsError';\n }\n}\n\n/**\n * Invalid Token Error (401)\n *\n * Thrown when authentication token is invalid or malformed\n */\nexport class InvalidTokenError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Invalid authentication token', details: data.details });\n this.name = 'InvalidTokenError';\n }\n}\n\n/**\n * Invalid Social Token Error (401)\n *\n * Thrown when a social provider id_token fails verification\n * (bad signature, wrong issuer/audience, expired, or nonce mismatch).\n */\nexport class InvalidSocialTokenError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Invalid social id_token', details: data.details });\n this.name = 'InvalidSocialTokenError';\n }\n}\n\n/**\n * Token Expired Error (401)\n *\n * Thrown when authentication token has expired\n */\nexport class TokenExpiredError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Authentication token has expired', details: data.details });\n this.name = 'TokenExpiredError';\n }\n}\n\n/**\n * Key Expired Error (401)\n *\n * Thrown when public key has expired\n */\nexport class KeyExpiredError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Public key has expired', details: data.details });\n this.name = 'KeyExpiredError';\n }\n}\n\n/**\n * Account Disabled Error (403)\n *\n * Thrown when user account is disabled or inactive\n */\nexport class AccountDisabledError extends ForbiddenError\n{\n constructor(data: { status?: string; message?: string; details?: Record<string, any> } = {})\n {\n const status = data.status || 'disabled';\n super({\n message: data.message || `Account is ${status}`,\n details: { status, ...data.details },\n });\n this.name = 'AccountDisabledError';\n }\n}\n\n/**\n * Account Pending Deletion Error (403)\n *\n * Thrown on login (password/OAuth/authenticate) when the account is within its\n * deletion grace period. Carries `purgeScheduledAt` so the client can offer a\n * recovery flow instead of a generic \"disabled\" message.\n */\nexport class AccountPendingDeletionError extends ForbiddenError\n{\n constructor(data: { purgeScheduledAt?: string; message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Account is scheduled for deletion',\n details: { status: 'pending_deletion', purgeScheduledAt: data.purgeScheduledAt, ...data.details },\n });\n this.name = 'AccountPendingDeletionError';\n }\n}\n\n/**\n * Deletion Already Requested Error (409)\n *\n * Thrown when requesting deletion for an account that already has a pending\n * deletion request (or has already been purged).\n */\nexport class DeletionAlreadyRequestedError extends ConflictError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Account deletion has already been requested', details: data.details });\n this.name = 'DeletionAlreadyRequestedError';\n }\n}\n\n/**\n * Deletion Not Requested Error (404)\n *\n * Thrown when trying to cancel/purge a deletion for an account that has no\n * pending deletion request.\n */\nexport class DeletionNotRequestedError extends NotFoundError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'No pending account deletion request found', details: data.details });\n this.name = 'DeletionNotRequestedError';\n }\n}\n\n/**\n * Immediate Deletion Not Allowed Error (403)\n *\n * Thrown when a self-service caller requests `immediate: true` but the server\n * has not enabled `deletion.allowSelfImmediate`.\n */\nexport class ImmediateDeletionNotAllowedError extends ForbiddenError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Immediate self-service deletion is not enabled', details: data.details });\n this.name = 'ImmediateDeletionNotAllowedError';\n }\n}\n\n/**\n * Account Already Exists Error (409)\n *\n * Thrown when trying to register with existing email/phone\n */\nexport class AccountAlreadyExistsError extends ConflictError\n{\n constructor(data: { identifier?: string; identifierType?: 'email' | 'phone'; message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Account already exists',\n details: {\n identifier: data.identifier,\n identifierType: data.identifierType,\n ...data.details,\n },\n });\n this.name = 'AccountAlreadyExistsError';\n }\n}\n\n/**\n * Registration Rejected Error (403)\n *\n * Thrown by the app-injected beforeRegister hook to reject a registration\n * (age gate, domain restriction, block list, ...)\n */\nexport class RegistrationRejectedError extends ForbiddenError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Registration rejected', details: data.details });\n this.name = 'RegistrationRejectedError';\n }\n}\n\n/**\n * Invalid Verification Code Error (400)\n *\n * Thrown when verification code is invalid, expired, or already used\n */\nexport class InvalidVerificationCodeError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Invalid verification code', details: data.details });\n this.name = 'InvalidVerificationCodeError';\n }\n}\n\n/**\n * Invalid Verification Token Error (400)\n *\n * Thrown when verification token is invalid or expired\n */\nexport class InvalidVerificationTokenError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Invalid or expired verification token', details: data.details });\n this.name = 'InvalidVerificationTokenError';\n }\n}\n\n/**\n * Key ID Already Registered Error (409)\n *\n * Thrown when a sign-in submits a keyId that is already taken — the client's own\n * revoked keyId, or a keyId belonging to another user. `keyId` is unique across\n * all users, so either case would collide on insert.\n *\n * The same error covers both cases on purpose: a distinguishable response would\n * let a caller probe whether an arbitrary keyId exists. Revoked stays revoked —\n * the client must generate a fresh keyId and retry.\n */\nexport class KeyIdAlreadyRegisteredError extends ConflictError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This keyId is already registered. Generate a new keyId and retry.',\n details: data.details,\n });\n this.name = 'KeyIdAlreadyRegisteredError';\n }\n}\n\n/**\n * Invalid Key Fingerprint Error (400)\n *\n * Thrown when public key fingerprint doesn't match the public key\n */\nexport class InvalidKeyFingerprintError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Invalid key fingerprint', details: data.details });\n this.name = 'InvalidKeyFingerprintError';\n }\n}\n\n/**\n * Key Algorithm Mismatch Error (400)\n *\n * Thrown when the submitted public key's SPKI type is not the one its declared\n * algorithm needs — a P-256 EC key declared RS256, an RSA key declared ES256, a\n * curve other than P-256 declared ES256, or bytes that are no SPKI key at all.\n *\n * The algorithm is stored beside the key and read back at proof verification;\n * nothing re-derives it from the key material. So a mismatch accepted here\n * surfaces only after the device believes it is enrolled, on every request it\n * then makes. It is refused at registration instead, wherever key material is\n * stored: register, login, rotate, invitation acceptance and device start.\n */\nexport class KeyAlgorithmMismatchError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Public key type does not match the declared algorithm',\n details: data.details,\n });\n this.name = 'KeyAlgorithmMismatchError';\n }\n}\n\n/**\n * Key Not Found Error (404)\n *\n * Thrown when a key operation names a keyId the caller does not own. It says\n * nothing about whether that keyId exists on another account — the repository\n * scopes every lookup by userId, so the answer is only ever \"not yours\".\n */\nexport class KeyNotFoundError extends NotFoundError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Key not found', details: data.details });\n this.name = 'KeyNotFoundError';\n }\n}\n\n/**\n * Device Auth Not Found Error (404)\n *\n * Thrown when a device-code operation names a code the server cannot act on: one\n * that was never issued, and one whose record has already been consumed.\n *\n * Those two are answered identically on purpose. A consumed record is a login\n * that finished, and saying so would tell whoever holds the code that it was\n * real — which is the difference between guessing at random and knowing a guess\n * landed. Every route that accepts a code is rate limited for the same reason.\n */\nexport class DeviceAuthNotFoundError extends NotFoundError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'Device authorization not found', details: data.details });\n this.name = 'DeviceAuthNotFoundError';\n }\n}\n\n/**\n * Device Auth Expired Error (400)\n *\n * Thrown when a device-code operation names a record whose TTL has run out,\n * whatever state it is in. The waiting device starts again; the approver is told\n * the code on the other screen is stale.\n *\n * 400 rather than 401: on the approve and deny routes the caller's own session is\n * fine, and answering 401 would send a signed-in user to a login screen over a\n * code that simply sat too long.\n */\nexport class DeviceAuthExpiredError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This device code has expired. Start again on the other device.',\n details: data.details,\n });\n this.name = 'DeviceAuthExpiredError';\n }\n}\n\n/**\n * Device Auth Already Handled Error (409)\n *\n * Thrown when an approve, deny or info call names a record that has already been\n * approved or denied. A decision on a device is made once — a second approval\n * would let one code be answered twice, and re-approving a record the owner\n * denied would undo the refusal.\n *\n * This is also what the loser of two concurrent approvals sees, since the\n * transition names the state it moves from and only one call can match it.\n */\nexport class DeviceAuthAlreadyHandledError extends ConflictError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This device request has already been answered',\n details: data.details,\n });\n this.name = 'DeviceAuthAlreadyHandledError';\n }\n}\n\n/**\n * Device Auth Denied Error (403)\n *\n * Thrown when the waiting device polls a record its owner refused. Distinct from\n * a pending answer, and distinct from a code that does not exist: the device\n * asked a person and the person said no, so it should stop polling and say so\n * rather than time out looking like a network fault.\n */\nexport class DeviceAuthDeniedError extends ForbiddenError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This device request was denied',\n details: data.details,\n });\n this.name = 'DeviceAuthDeniedError';\n }\n}\n\n/**\n * Nonce Key Binding Error (400)\n *\n * Thrown when a native id_token sign-in submits a nonce that is not the public\n * key's fingerprint. The nonce is what the provider echoed back inside the\n * id_token, so tying it to the key is what proves the id_token and the key came\n * from the same device — see the native section of the README.\n */\nexport class NonceKeyBindingError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'nonce must be the fingerprint of the submitted public key',\n details: data.details,\n });\n this.name = 'NonceKeyBindingError';\n }\n}\n\n/**\n * Native Sign-In Unsupported Error (400)\n *\n * Thrown when a provider is asked for native id_token sign-in and has no\n * implementation for it — a server configuration fact, not something the user\n * did. A client reading this hides that provider's native button instead of\n * asking the user to try again.\n *\n * Split out of ValidationError because the other native-enrollment refusal\n * (linking to an account whose email the provider never verified) needs a\n * different response from the app, and one code cannot ask for two.\n */\nexport class NativeSignInUnsupportedError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This provider does not support native id_token sign-in.',\n details: data.details,\n });\n this.name = 'NativeSignInUnsupportedError';\n }\n}\n\n/**\n * Invalid Signup Link Error (400)\n *\n * Thrown when an emailed signup confirmation link is unknown, expired, already\n * consumed, or superseded by a newer request for the same address.\n *\n * One error for all four states, on purpose. Distinguishing \"expired\" from\n * \"unknown\" tells a caller holding a random token whether it named a real\n * pending signup, which is exactly the enumeration the request step avoids. The\n * specific reason is logged.\n */\nexport class InvalidSignupLinkError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This signup link is no longer valid. Request a new one.',\n details: data.details,\n });\n this.name = 'InvalidSignupLinkError';\n }\n}\n\n/**\n * Invalid Signup Setup Session Error (401)\n *\n * Thrown when the password-setup session backing a verified-email signup is\n * missing, unknown, expired, superseded or already used.\n *\n * One error for every one of those, on purpose: telling a caller which of them\n * applies tells them whether an address is mid-signup, which is the same\n * enumeration the request step is careful not to leak.\n */\nexport class InvalidSignupSetupSessionError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Password setup session is invalid or has expired. Start the signup again.',\n details: data.details,\n });\n this.name = 'InvalidSignupSetupSessionError';\n }\n}\n\n/**\n * Password Reset Link Error (401)\n *\n * Thrown when an emailed password reset link is unknown, expired, already\n * consumed, superseded by a newer request, or belongs to an account that is no\n * longer active.\n *\n * One error for every one of those, on purpose. Distinguishing \"expired\" from\n * \"unknown\" tells a caller holding a random token whether it named a real\n * pending reset, which is exactly the enumeration the request step avoids. The\n * specific reason is logged.\n */\nexport class PasswordResetLinkError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This password reset link is no longer valid. Request a new one.',\n details: data.details,\n });\n this.name = 'PasswordResetLinkError';\n }\n}\n\n/**\n * Password Reset Session Error (401)\n *\n * Thrown when the password-setup session backing a reset is missing, unknown,\n * expired, superseded or already used.\n *\n * One error for every one of those, for the same reason the link error is one\n * error: which of them applies tells a caller whether an account is mid-reset,\n * and that is the enumeration the request step is careful not to leak.\n */\nexport class PasswordResetSessionError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Password reset session is invalid or has expired. Request a new link.',\n details: data.details,\n });\n this.name = 'PasswordResetSessionError';\n }\n}\n\n/**\n * Unverified Email Link Error (400)\n *\n * Thrown when a social identity carries an email that already belongs to an\n * account, but the provider never verified it. Linking on an unverified email\n * is account takeover, so the refusal stands and the user is sent to a path\n * that proves the address.\n *\n * This says an account exists for that address. It is not a leak introduced\n * here: the message this replaces already stated the same fact in prose, and\n * the paths that must not disclose account existence (password login, deletion\n * re-auth, verification issuance) answer uniformly and are untouched.\n */\nexport class UnverifiedEmailLinkError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message\n || 'Cannot link to existing account with unverified email. Please verify your email with the provider first.',\n details: data.details,\n });\n this.name = 'UnverifiedEmailLinkError';\n }\n}\n\n/**\n * Verification Token Purpose Mismatch Error (400)\n *\n * Thrown when verification token purpose doesn't match expected purpose\n */\nexport class VerificationTokenPurposeMismatchError extends ValidationError\n{\n constructor(data: { expected?: string; actual?: string; message?: string; details?: Record<string, any> } = {})\n {\n const expected = data.expected || 'unknown';\n const actual = data.actual || 'unknown';\n super({\n message: data.message || `Verification token is for ${actual}, but ${expected} was expected`,\n details: { expected, actual, ...data.details },\n });\n this.name = 'VerificationTokenPurposeMismatchError';\n }\n}\n\n/**\n * Verification Token Target Mismatch Error (400)\n *\n * Thrown when verification token target doesn't match provided email/phone\n */\nexport class VerificationTokenTargetMismatchError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Verification token does not match provided email/phone',\n details: data.details,\n });\n this.name = 'VerificationTokenTargetMismatchError';\n }\n}\n\n/**\n * Reserved Username Error (400)\n *\n * Thrown when trying to use a reserved/prohibited username\n */\nexport class ReservedUsernameError extends ValidationError\n{\n constructor(data: { username?: string; message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This username is reserved',\n details: { username: data.username, ...data.details },\n });\n this.name = 'ReservedUsernameError';\n }\n}\n\n/**\n * Username Already Taken Error (409)\n *\n * Thrown when trying to set a username that is already in use\n */\nexport class UsernameAlreadyTakenError extends ConflictError\n{\n constructor(data: { username?: string; message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Username is already taken',\n details: { username: data.username, ...data.details },\n });\n this.name = 'UsernameAlreadyTakenError';\n }\n}\n\n/**\n * Insufficient Permissions Error (403)\n *\n * Thrown when user lacks required permissions for the operation\n */\nexport class InsufficientPermissionsError extends ForbiddenError\n{\n constructor(data: { requiredPermissions?: string[]; message?: string; details?: Record<string, any> } = {})\n {\n const requiredPermissions = data.requiredPermissions || [];\n super({\n message: data.message || `Missing required permissions: ${requiredPermissions.join(', ')}`,\n details: { requiredPermissions, ...data.details },\n });\n this.name = 'InsufficientPermissionsError';\n }\n}\n\n/**\n * Insufficient Role Error (403)\n *\n * Thrown when user lacks required role for the operation\n */\nexport class InsufficientRoleError extends ForbiddenError\n{\n constructor(data: { requiredRoles?: string[]; message?: string; details?: Record<string, any> } = {})\n {\n const requiredRoles = data.requiredRoles || [];\n super({\n message: data.message || `Required roles: ${requiredRoles.join(', ')}`,\n details: { requiredRoles, ...data.details },\n });\n this.name = 'InsufficientRoleError';\n }\n}\n\n/**\n * Passkey Challenge Error (401)\n *\n * Thrown when the challenge a WebAuthn ceremony presents is unknown, expired,\n * already spent, minted for the other ceremony, or minted for another account.\n *\n * One error for all five, on purpose. Telling a caller which applies tells them\n * whether a challenge they did not mint exists and what it was for, and the\n * remedy is the same in every case: start the ceremony again.\n */\nexport class PasskeyChallengeError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This passkey challenge is no longer valid. Try again.',\n details: data.details,\n });\n this.name = 'PasskeyChallengeError';\n }\n}\n\n/**\n * Passkey Verification Error (401)\n *\n * Thrown when an assertion or attestation does not verify: wrong origin, wrong\n * rpId, a bad signature, a regressed signature counter — or, on login, a\n * credential that is unknown or has been revoked.\n *\n * Those last two share this error with the cryptographic failures deliberately.\n * A distinct \"no such passkey\" would answer, to anyone holding a credential id,\n * whether it is enrolled here — and a revoked credential answering differently\n * from an unknown one would say the account once had it.\n */\nexport class PasskeyVerificationError extends UnauthorizedError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Passkey verification failed.',\n details: data.details,\n });\n this.name = 'PasskeyVerificationError';\n }\n}\n\n/**\n * Passkey Not Found Error (404)\n *\n * Thrown when a management operation names a passkey the caller does not own,\n * or one already revoked. Every lookup is owner-scoped, so the answer is only\n * ever \"not yours\" and says nothing about other accounts.\n */\nexport class PasskeyNotFoundError extends NotFoundError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Passkey not found',\n details: data.details,\n });\n this.name = 'PasskeyNotFoundError';\n }\n}\n\n/**\n * Passkey Already Registered Error (409)\n *\n * Thrown when the credential presented for enrollment is already on file for\n * some account — including one that revoked it. A credential id is reserved for\n * good once used, so this is also the answer to re-enrolling one's own revoked\n * passkey.\n */\nexport class PasskeyAlreadyRegisteredError extends ConflictError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'This passkey is already registered',\n details: data.details,\n });\n this.name = 'PasskeyAlreadyRegisteredError';\n }\n}\n\n/**\n * Recent Authentication Required Error (403)\n *\n * Thrown when enrolling or revoking a passkey from a session that proved itself\n * too long ago and carried no password. Clients branch on `code` to know to\n * prompt for the password rather than to show a generic refusal, so the code is\n * a stable field rather than a message they would have to match on.\n */\nexport class RecentAuthenticationRequiredError extends ForbiddenError\n{\n readonly code = 'RECENT_AUTH_REQUIRED';\n\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Confirm it is you before changing passkeys. Sign in again or send your current password.',\n details: data.details,\n });\n this.name = 'RecentAuthenticationRequiredError';\n }\n}\n\n/**\n * Last Recovery Credential Error (409)\n *\n * Thrown when revoking a passkey would leave the account with no way back in:\n * no other live passkey, no password, and no linked social account. There is no\n * password reset in this package, so that state is not recoverable by support\n * either — the refusal is the only thing standing between the owner and a\n * locked account.\n *\n * Clients branch on `code` to offer \"set a password first\" instead of a generic\n * refusal.\n */\nexport class LastRecoveryCredentialError extends ConflictError\n{\n readonly code = 'LAST_RECOVERY_CREDENTIAL';\n\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message\n || 'This is the only way you can sign in. Set a password or link a social account before removing it.',\n details: data.details,\n });\n this.name = 'LastRecoveryCredentialError';\n }\n}\n\n/**\n * Passkey Config Error (500)\n *\n * Thrown at boot when the passkey relying-party configuration cannot be honoured\n * — an origin that is not https outside localhost, an origin off the rpId, an\n * unsupported user-verification value.\n *\n * A refusal at boot rather than at the first ceremony: every one of these makes\n * every passkey operation fail, and the drift is between environments, so the\n * deploy that introduces it is where it has to surface.\n */\nexport class PasskeyConfigError extends HttpError\n{\n constructor(data: { message: string; details?: Record<string, any> })\n {\n super({\n message: data.message,\n statusCode: 500,\n details: data.details,\n });\n this.name = 'PasskeyConfigError';\n }\n}\n\n/**\n * OAuth2 Unknown Client Error (400)\n *\n * Thrown by the API authorize endpoints when `client_id` names no registered\n * client. One of the two refusals that must NOT be turned into a redirect: with\n * no client there is no registered redirect URI, so the only place left to send\n * the error is the one the request supplied — which is exactly the open redirect\n * this rule exists to close. The consent screen shows it instead.\n */\nexport class OAuth2UnknownClientError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'Unknown OAuth client',\n details: { error: 'unknown_client', ...data.details },\n });\n this.name = 'OAuth2UnknownClientError';\n }\n}\n\n/**\n * OAuth2 Redirect URI Mismatch Error (400)\n *\n * Thrown when `redirect_uri` is not one the client registered. The second\n * non-redirectable refusal, for the same reason as the first: redirecting the\n * error to an unregistered URI is the attack.\n */\nexport class OAuth2RedirectUriMismatchError extends ValidationError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({\n message: data.message || 'redirect_uri does not match a registered URI for this client',\n details: { error: 'redirect_uri_mismatch', ...data.details },\n });\n this.name = 'OAuth2RedirectUriMismatchError';\n }\n}\n\n/**\n * OAuth2 Authorize Redirect Error (400)\n *\n * Every other authorize-time refusal — `invalid_request`, `invalid_scope`,\n * `invalid_target`, `access_denied`. The client and its redirect URI are both\n * known by the time these are decided, so RFC 6749 §4.1.2.1 says the error goes\n * back to the client as query parameters on that URI rather than to the person.\n *\n * The API cannot perform that redirect — it is answering the web app's consent\n * handler, not the browser — so it carries the pieces in `details` and the\n * handler builds the 302. `redirectUri` is the registered-and-matched value, not\n * the raw parameter, which is what makes it safe to send somebody to.\n */\nexport class OAuth2AuthorizeRedirectError extends ValidationError\n{\n constructor(data: {\n error: string;\n redirectUri: string;\n state?: string;\n message?: string;\n details?: Record<string, any>;\n })\n {\n super({\n message: data.message || `OAuth authorize request refused: ${data.error}`,\n details: {\n error: data.error,\n redirectUri: data.redirectUri,\n state: data.state,\n ...data.details,\n },\n });\n this.name = 'OAuth2AuthorizeRedirectError';\n }\n}\n\n/**\n * OAuth2 Grant Not Found Error (404)\n *\n * Thrown by `DELETE /_auth/oauth2/grants/:id` when the caller owns no live grant\n * of that id. A grant belonging to somebody else answers the same way as one\n * that never existed — the id is a number in a URL, and telling the two apart\n * would let anyone count other people's connected clients.\n */\nexport class OAuth2GrantNotFoundError extends NotFoundError\n{\n constructor(data: { message?: string; details?: Record<string, any> } = {})\n {\n super({ message: data.message || 'No such connected application', details: data.details });\n this.name = 'OAuth2GrantNotFoundError';\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _spfn_core_nextjs from '@spfn/core/nextjs';
2
- import { P as PermissionConfig, R as RoleConfig, m as mainAuthRouter, S as SendVerificationCodeResult, a as RegisterResult, b as RequestSignupLinkResult, C as ConfirmSignupLinkResult, c as RequestPasswordResetResult, d as ConfirmPasswordResetResult, L as LoginResult, e as StartDeviceAuthResult, D as DeviceAuthInfoResult, F as FinishPasskeyEnrollmentResult, f as PasskeySummary, g as RotateKeyResult, K as KeySummary, h as RevokeAllKeysResult, I as IssueOneTimeTokenResult, O as OAuthStartResult, i as OAuthNativeResult, U as UserProfile, j as ProfileInfo } from './machine-principals-nrpFSvvB.js';
3
- export { A as AuthInitOptions, k as AuthSession, l as PERMISSION_CATEGORIES, n as PermissionCategory, V as VERIFICATION_PURPOSES, o as VERIFICATION_TARGET_TYPES, p as VerificationPurpose, q as VerificationTargetType } from './machine-principals-nrpFSvvB.js';
2
+ import { P as PermissionConfig, R as RoleConfig, m as mainAuthRouter, S as SendVerificationCodeResult, a as RegisterResult, b as RequestSignupLinkResult, C as ConfirmSignupLinkResult, c as RequestPasswordResetResult, d as ConfirmPasswordResetResult, L as LoginResult, e as StartDeviceAuthResult, D as DeviceAuthInfoResult, F as FinishPasskeyEnrollmentResult, f as PasskeySummary, g as RotateKeyResult, K as KeySummary, h as RevokeAllKeysResult, I as IssueOneTimeTokenResult, O as OAuthStartResult, i as OAuthNativeResult, U as UserProfile, j as ProfileInfo, k as OAuth2ConsentView, l as OAuth2AuthorizationCodeIssued, n as OAuth2GrantSummary } from './machine-principals-BD4tnASp.js';
3
+ export { A as AuthInitOptions, o as AuthSession, p as PERMISSION_CATEGORIES, q as PermissionCategory, V as VERIFICATION_PURPOSES, r as VERIFICATION_TARGET_TYPES, s as VerificationPurpose, t as VerificationTargetType } from './machine-principals-BD4tnASp.js';
4
4
  import * as _simplewebauthn_server from '@simplewebauthn/server';
5
5
  import * as _spfn_core_route from '@spfn/core/route';
6
6
  import { HttpMethod } from '@spfn/core/route';
@@ -753,6 +753,43 @@ declare const authApi: _spfn_core_nextjs.Client<_spfn_core_route.Router<{
753
753
  createdAt: string | null;
754
754
  };
755
755
  }>;
756
+ registerOAuth2Client: _spfn_core_route.RouteDef<{}, {}, Response>;
757
+ getOAuth2Authorize: _spfn_core_route.RouteDef<{
758
+ query: _sinclair_typebox.TObject<{
759
+ client_id: _sinclair_typebox.TString;
760
+ redirect_uri: _sinclair_typebox.TString;
761
+ code_challenge: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
762
+ code_challenge_method: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
763
+ resource: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
764
+ scope: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
765
+ state: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
766
+ }>;
767
+ }, {}, OAuth2ConsentView>;
768
+ createOAuth2AuthorizationCode: _spfn_core_route.RouteDef<{
769
+ body: _sinclair_typebox.TObject<{
770
+ approve: _sinclair_typebox.TBoolean;
771
+ client_id: _sinclair_typebox.TString;
772
+ redirect_uri: _sinclair_typebox.TString;
773
+ code_challenge: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
774
+ code_challenge_method: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
775
+ resource: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
776
+ scope: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
777
+ state: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
778
+ }>;
779
+ }, {}, OAuth2AuthorizationCodeIssued>;
780
+ oauth2Token: _spfn_core_route.RouteDef<{}, {}, Response>;
781
+ oauth2Revoke: _spfn_core_route.RouteDef<{}, {}, Response>;
782
+ listOAuth2Grants: _spfn_core_route.RouteDef<{}, {}, {
783
+ grants: OAuth2GrantSummary[];
784
+ }>;
785
+ revokeOAuth2Grant: _spfn_core_route.RouteDef<{
786
+ params: _sinclair_typebox.TObject<{
787
+ id: _sinclair_typebox.TNumber;
788
+ }>;
789
+ }, {}, {
790
+ revoked: boolean;
791
+ }>;
792
+ oauth2AuthorizationServerMetadata: _spfn_core_route.RouteDef<{}, {}, Response>;
756
793
  }>>;
757
794
  type AuthRouter = typeof mainAuthRouter;
758
795