@spfn/auth 0.3.0-beta.23 → 0.3.0-beta.25
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 +328 -7
- package/dist/client-proof.d.ts +10 -1
- package/dist/client-proof.js +136 -11
- package/dist/client-proof.js.map +1 -1
- package/dist/client.d.ts +101 -1
- package/dist/client.js +65 -0
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +122 -0
- package/dist/config.js +53 -0
- package/dist/config.js.map +1 -1
- package/dist/crypto.d.ts +1 -1
- package/dist/errors.d.ts +159 -3
- package/dist/errors.js +95 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +52 -12
- package/dist/index.js +104 -3
- package/dist/index.js.map +1 -1
- package/dist/{machine-principals-CaEFq61K.d.ts → machine-principals-CdEgxOB1.d.ts} +2049 -771
- package/dist/nextjs/api.js +329 -37
- package/dist/nextjs/api.js.map +1 -1
- package/dist/nextjs/server.d.ts +59 -24
- package/dist/nextjs/server.js +105 -11
- package/dist/nextjs/server.js.map +1 -1
- package/dist/server.d.ts +415 -332
- package/dist/server.js +2933 -1545
- package/dist/server.js.map +1 -1
- package/dist/{session-Dfwu5g2W.d.ts → session-BbhAGZtA.d.ts} +57 -1
- package/dist/{types-DYyhze28.d.ts → types-CTdoTOxM.d.ts} +24 -1
- package/migrations/20260918184037_happy_mordo/migration.sql +4 -0
- package/migrations/20260918184037_happy_mordo/snapshot.json +6000 -0
- package/migrations/20260918184152_dear_rictor/migration.sql +3 -0
- package/migrations/20260918184152_dear_rictor/snapshot.json +6039 -0
- package/migrations/20260919023107_even_mikhail_rasputin/migration.sql +20 -0
- package/migrations/20260919023107_even_mikhail_rasputin/snapshot.json +6300 -0
- package/package.json +1 -1
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/index.ts","../src/config/schema.ts"],"sourcesContent":["/**\n * Core Package Configuration\n *\n * @example\n * ```typescript\n * import { registry } from '@spfn/core/config';\n *\n * const env = registry.validate();\n * console.log(env.DB_POOL_MAX);\n * ```\n *\n * @module config\n */\n\nimport { createEnvRegistry } from '@spfn/core/env';\nimport { authEnvSchema } from './schema';\n\nexport { authEnvSchema as envSchema } from './schema';\n\n/**\n * Environment registry\n */\nconst registry = createEnvRegistry(authEnvSchema);\nexport const env = registry.validate();\n","/**\n * Auth Environment Variable Schema\n *\n * Centralized schema definition for all environment variables used in @spfn/auth.\n * This provides type safety, validation, and documentation for Auth configuration.\n *\n * @module config/schema\n */\n\nimport {\n defineEnvSchema,\n envString,\n envNumber,\n envBoolean,\n envEnum,\n createSecureSecretParser,\n createPasswordParser,\n} from '@spfn/core/env';\n\n/**\n * Auth environment variable schema\n *\n * Defines all Auth environment variables with:\n * - Type information\n * - Default values\n * - Validation rules\n * - Documentation\n *\n * @example\n * ```typescript\n * import { envSchema } from '@spfn/auth/config';\n *\n * // Access schema information\n * console.log(envSchema.SPFN_AUTH_SESSION_SECRET.description);\n * console.log(envSchema.SPFN_AUTH_JWT_EXPIRES_IN.default);\n * ```\n */\nexport const authEnvSchema = defineEnvSchema({\n // ============================================================================\n // Session Configuration\n // ============================================================================\n SPFN_AUTH_SESSION_SECRET: {\n ...envString({\n description: 'Session encryption secret (minimum 32 characters for AES-256)',\n required: true,\n fallbackKeys: ['SESSION_SECRET'],\n validator: createSecureSecretParser({\n minLength: 32,\n minUniqueChars: 16,\n minEntropy: 3.5,\n }),\n sensitive: true,\n nextjs: true, // Required for Next.js RSC session validation\n examples: [\n 'my-super-secret-session-key-at-least-32-chars-long',\n 'use-a-cryptographically-secure-random-string-here',\n ],\n }),\n },\n\n SPFN_AUTH_SESSION_TTL: {\n ...envString({\n description: 'Session TTL (time to live) - supports duration strings like \\'7d\\', \\'12h\\', \\'45m\\'',\n default: '7d',\n required: false,\n nextjs: true, // May be needed for session validation in Next.js RSC\n examples: ['7d', '30d', '12h', '45m', '3600'],\n }),\n },\n\n // ============================================================================\n // JWT Configuration\n // ============================================================================\n SPFN_AUTH_JWT_SECRET: {\n ...envString({\n description: 'JWT signing secret for server-signed tokens (legacy mode)',\n default: 'dev-secret-key-change-in-production',\n required: false,\n examples: [\n 'your-jwt-secret-key-here',\n 'use-different-from-session-secret',\n ],\n }),\n },\n\n SPFN_AUTH_JWT_EXPIRES_IN: {\n ...envString({\n description: 'JWT token expiration time (e.g., \\'7d\\', \\'24h\\', \\'1h\\')',\n default: '7d',\n required: false,\n examples: ['7d', '24h', '1h', '30m'],\n }),\n },\n\n // ============================================================================\n // Security Configuration\n // ============================================================================\n SPFN_AUTH_COOKIE_SECURE: {\n ...envBoolean({\n description: 'Override cookie Secure flag. Defaults to NODE_ENV === \"production\". Set to false for HTTP-only environments (e.g. bastion over plain HTTP).',\n required: false,\n nextjs: true,\n examples: [true, false],\n }),\n },\n\n SPFN_AUTH_CSRF: {\n ...envString({\n description: 'CSRF protection for cookie-session mutations in the Next.js proxy: off | warn | enforce. Unset behaves as \"warn\" (log what would be refused, allow it through). configureAuth({ csrf: { mode } }) takes precedence.',\n required: false,\n nextjs: true, // The check runs in the Next.js proxy\n examples: ['enforce', 'warn', 'off'],\n }),\n },\n\n SPFN_AUTH_BCRYPT_SALT_ROUNDS: {\n ...envNumber({\n description: 'Bcrypt salt rounds (cost factor, higher = more secure but slower)',\n default: 12,\n required: false,\n examples: [10, 12, 14],\n }),\n key: 'SPFN_AUTH_BCRYPT_SALT_ROUNDS',\n },\n\n SPFN_AUTH_VERIFICATION_TOKEN_SECRET: {\n ...envString({\n description: 'Verification token secret for email verification, password reset, etc.',\n required: true,\n examples: [\n 'your-verification-token-secret',\n 'can-be-different-from-jwt-secret',\n ],\n }),\n },\n\n SPFN_AUTH_TOKEN_ENCRYPTION_KEYS: {\n ...envString({\n description: 'Backend-only OAuth token encryption keyring. Comma-separated <keyId>:<base64-encoded 32-byte key> entries; the first key encrypts new values and remaining keys decrypt during rotation.',\n required: false,\n sensitive: true,\n examples: [\n 'v2:<base64-encoded-32-byte-key>,v1:<previous-base64-encoded-32-byte-key>',\n ],\n }),\n },\n\n // ============================================================================\n // Admin Account Configuration\n // ============================================================================\n SPFN_AUTH_ADMIN_ACCOUNTS: {\n ...envString({\n description: 'JSON array of admin accounts (recommended for multiple admins)',\n required: false,\n examples: [\n '[{\"email\":\"admin@example.com\",\"password\":\"secure-pass\",\"role\":\"admin\"}]',\n '[{\"email\":\"super@example.com\",\"password\":\"pass1\",\"role\":\"superadmin\"},{\"email\":\"admin@example.com\",\"password\":\"pass2\",\"role\":\"admin\"}]',\n ],\n }),\n },\n\n SPFN_AUTH_ADMIN_EMAILS: {\n ...envString({\n description: 'Comma-separated list of admin emails (legacy CSV format)',\n required: false,\n examples: [\n 'admin@example.com,user@example.com',\n 'super@example.com,admin@example.com,user@example.com',\n ],\n }),\n },\n\n SPFN_AUTH_ADMIN_PASSWORDS: {\n ...envString({\n description: 'Comma-separated list of admin passwords (legacy CSV format)',\n required: false,\n examples: [\n 'admin-pass,user-pass',\n 'super-pass,admin-pass,user-pass',\n ],\n }),\n },\n\n SPFN_AUTH_ADMIN_ROLES: {\n ...envString({\n description: 'Comma-separated list of admin roles (legacy CSV format)',\n required: false,\n examples: [\n 'admin,user',\n 'superadmin,admin,user',\n ],\n }),\n },\n\n SPFN_AUTH_ADMIN_EMAIL: {\n ...envString({\n description: 'Single admin email (simplest format)',\n required: false,\n examples: ['admin@example.com'],\n }),\n },\n\n SPFN_AUTH_ADMIN_PASSWORD: {\n ...envString({\n description: 'Single admin password (simplest format)',\n required: false,\n validator: createPasswordParser({\n minLength: 8,\n requireUppercase: true,\n requireLowercase: true,\n requireNumber: true,\n requireSpecial: true,\n }),\n sensitive: true,\n examples: ['SecureAdmin123!'],\n }),\n },\n\n // ============================================================================\n // Username Configuration\n // ============================================================================\n SPFN_AUTH_RESERVED_USERNAMES: {\n ...envString({\n description: 'Comma-separated list of reserved usernames that cannot be registered',\n required: false,\n default: 'admin,root,system,support,help,moderator,superadmin',\n examples: [\n 'admin,root,system,support,help',\n 'admin,root,system,support,help,moderator,superadmin,operator',\n ],\n }),\n },\n\n SPFN_AUTH_USERNAME_MIN_LENGTH: {\n ...envNumber({\n description: 'Minimum username length',\n default: 3,\n required: false,\n examples: [2, 3, 4],\n }),\n },\n\n SPFN_AUTH_USERNAME_MAX_LENGTH: {\n ...envNumber({\n description: 'Maximum username length',\n default: 30,\n required: false,\n examples: [20, 30, 50],\n }),\n },\n\n // ============================================================================\n // Verified-email signup\n // ============================================================================\n SPFN_AUTH_SIGNUP_LINK_TTL_MINUTES: {\n ...envNumber({\n description: 'How long an emailed signup confirmation link stays valid. Long enough to survive a mail delay, short enough that a link left in an inbox stops working.',\n default: 30,\n required: false,\n examples: [15, 30, 60],\n }),\n },\n\n SPFN_AUTH_SIGNUP_SETUP_TTL_MINUTES: {\n ...envNumber({\n description: 'How long the password-setup session opened by a confirmation link stays valid. Covers one sitting at the password form, not an abandoned tab.',\n default: 15,\n required: false,\n examples: [10, 15, 30],\n }),\n },\n\n SPFN_AUTH_SIGNUP_CONFIRM_PATH: {\n ...envString({\n description: 'App page the emailed confirmation link opens, as a path on {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}. The page reads the token from the query string and posts it to /_auth/signup/email/confirm; it is a page in your app, not an API route.',\n default: '/signup/confirm',\n required: false,\n examples: ['/signup/confirm', '/auth/confirm', '/join/verify'],\n }),\n },\n\n // ============================================================================\n // Password reset (verified email)\n // ============================================================================\n SPFN_AUTH_PASSWORD_RESET_LINK_TTL_MINUTES: {\n ...envNumber({\n description: 'How long an emailed password reset link stays valid. Long enough to survive a mail delay, short enough that a link left in an inbox stops working.',\n default: 30,\n required: false,\n examples: [15, 30, 60],\n }),\n },\n\n SPFN_AUTH_PASSWORD_RESET_SETUP_TTL_MINUTES: {\n ...envNumber({\n description: 'How long the password-setup session opened by a reset link stays valid. Covers one sitting at the new-password form, not an abandoned tab.',\n default: 15,\n required: false,\n examples: [10, 15, 30],\n }),\n },\n\n SPFN_AUTH_PASSWORD_RESET_CONFIRM_PATH: {\n ...envString({\n description: 'App page the emailed reset link opens, as a path on {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}. The page reads the token from the query string and posts it to /_auth/password/reset/confirm; it is a page in your app, not an API route.',\n default: '/password/reset',\n required: false,\n examples: ['/password/reset', '/auth/reset', '/forgot/new-password'],\n }),\n },\n\n // ============================================================================\n // Signed sign-out-everywhere link\n // ============================================================================\n SPFN_AUTH_REVOKE_ALL_LINK_TTL_MINUTES: {\n ...envNumber({\n description: 'How long a signed sign-out-everywhere link stays valid. The same default as the other two link flows: long enough to survive a mail delay, short enough that a link left in an inbox stops working — and this one signs every device out.',\n default: 30,\n required: false,\n examples: [15, 30, 60],\n }),\n },\n\n SPFN_AUTH_REVOKE_ALL_CONFIRM_PATH: {\n ...envString({\n description: 'App page the sign-out-everywhere link opens, as a path on {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}. The page reads the token from the query string and posts it to /_auth/keys/revoke-all/confirm to describe the link, then to /_auth/keys/revoke-all/consume when the owner confirms; it is a page in your app, not an API route.',\n default: '/account/revoke-all',\n required: false,\n examples: ['/account/revoke-all', '/security/sign-out-everywhere'],\n }),\n },\n\n // ============================================================================\n // Link mail delivery\n // ============================================================================\n SPFN_AUTH_LINK_MAIL_DELIVERY: {\n ...envEnum(['auto', 'inline', 'queued'] as const, {\n description: 'Who sends signup-link, password-reset and account-exists mail. \\'auto\\' (default) queues it on auth.link-mail when pg-boss is initialised and sends it on the request path when it is not; \\'queued\\' always queues and surfaces an enqueue failure; \\'inline\\' always sends on the request path, which makes how long the request took reveal whether the address has an account.',\n default: 'auto',\n required: false,\n examples: ['auto', 'inline', 'queued'],\n }),\n },\n\n // ============================================================================\n // Passkeys (WebAuthn)\n // ============================================================================\n SPFN_AUTH_PASSKEY_RP_ID: {\n ...envString({\n description: 'Domain passkeys are bound to — a registrable domain with no protocol and no port. Defaults to the host of {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}. Set it explicitly when the app is served from several hosts that share a domain; every origin below must be that host or a subdomain of it. Changing it orphans every passkey already enrolled.',\n required: false,\n examples: ['example.com', 'app.example.com', 'localhost'],\n }),\n },\n\n SPFN_AUTH_PASSKEY_RP_NAME: {\n ...envString({\n description: \"Name the authenticator's own prompt shows the user. Defaults to the relying party ID.\",\n required: false,\n examples: ['Acme', 'Acme Staging'],\n }),\n },\n\n SPFN_AUTH_PASSKEY_ORIGINS: {\n ...envString({\n description: 'Comma-separated full origins allowed to run a passkey ceremony. Defaults to the origin of {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}. Each must be https (http only for localhost) and must be the relying party ID or a subdomain of it. Checked at boot: a value that breaks either rule refuses to start, because it would otherwise surface as the browser refusing every ceremony.',\n required: false,\n examples: ['https://app.example.com', 'https://app.example.com,https://admin.example.com', 'http://localhost:3000'],\n }),\n },\n\n SPFN_AUTH_PASSKEY_USER_VERIFICATION: {\n ...envString({\n description: \"How hard the authenticator must prove the person is present: 'preferred' or 'required'. 'discouraged' is refused at boot — a passkey is the whole credential here, so an assertion that skipped user verification would sign someone in on an unlocked device alone.\",\n default: 'preferred',\n required: false,\n examples: ['preferred', 'required'],\n }),\n },\n\n SPFN_AUTH_PASSKEY_CHALLENGE_TTL_SECONDS: {\n ...envNumber({\n description: 'How long the challenge minted by a passkey options call stays presentable. One ceremony at the authenticator, not an abandoned tab.',\n default: 300,\n required: false,\n examples: [120, 300, 600],\n }),\n },\n\n SPFN_AUTH_PASSKEY_RECENT_AUTH_MINUTES: {\n ...envNumber({\n description: 'How recently the calling device key must have been registered for enrolling or revoking a passkey to go through without the current password. Older than this and the request needs `currentPassword`; an account with no password has to sign in again.',\n default: 10,\n required: false,\n examples: [5, 10, 30],\n }),\n },\n\n // ============================================================================\n // Second factor (MFA)\n // ============================================================================\n SPFN_AUTH_MFA_ISSUER: {\n ...envString({\n description: 'Name the authenticator app files this account under, carried in the otpauth:// URI. Defaults to the passkey relying-party name, then to the host of {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}. Changing it after people have enrolled only relabels the entry in their app; the codes keep working.',\n required: false,\n examples: ['Acme', 'Acme Staging'],\n }),\n },\n\n SPFN_AUTH_MFA_STEP_UP_MINUTES: {\n ...envNumber({\n description: 'How recently an enrolled account must have proved its second factor on the calling device for a sensitive change (password change, sign out everywhere, disabling MFA, passkey management) to go through. Older than this and the request is 403 STEP_UP_REQUIRED until POST /_auth/mfa/step-up succeeds. Unenrolled accounts are unaffected.',\n default: 10,\n required: false,\n examples: [5, 10, 30],\n }),\n },\n\n // ============================================================================\n // API Configuration\n // ============================================================================\n SPFN_API_URL: {\n ...envString({\n description: 'Internal API URL for server-to-server communication',\n default: 'http://localhost:8790',\n required: false,\n examples: [\n 'https://api.example.com',\n 'http://localhost:8790',\n ],\n }),\n },\n\n NEXT_PUBLIC_SPFN_API_URL: {\n ...envString({\n description: 'Public-facing API URL used for browser-facing redirects. Falls back to SPFN_API_URL if not set.',\n required: false,\n examples: [\n 'https://api.example.com',\n 'http://localhost:8790',\n ],\n }),\n },\n\n SPFN_APP_URL: {\n ...envString({\n description: 'Next.js application URL (internal). Used for server-to-server communication.',\n default: 'http://localhost:3000',\n required: false,\n examples: [\n 'https://app.example.com',\n 'http://localhost:3000',\n ],\n }),\n },\n\n NEXT_PUBLIC_SPFN_APP_URL: {\n ...envString({\n description: 'Public-facing Next.js app URL for browser redirects (e.g. OAuth redirect). Falls back to SPFN_APP_URL if not set.',\n required: false,\n examples: [\n 'https://app.example.com',\n 'http://localhost:3000',\n ],\n }),\n },\n\n // ============================================================================\n // OAuth Configuration - Google\n // ============================================================================\n SPFN_AUTH_GOOGLE_CLIENT_ID: {\n ...envString({\n description: 'Google OAuth 2.0 Client ID. When set, Google OAuth routes are automatically enabled.',\n required: false,\n examples: ['123456789-abc123.apps.googleusercontent.com'],\n }),\n },\n\n SPFN_AUTH_GOOGLE_CLIENT_SECRET: {\n ...envString({\n description: 'Google OAuth 2.0 Client Secret',\n required: false,\n sensitive: true,\n examples: ['GOCSPX-abcdefghijklmnop'],\n }),\n },\n\n SPFN_AUTH_GOOGLE_SCOPES: {\n ...envString({\n description: 'Comma-separated Google OAuth scopes. Defaults to \"email,profile\" if not set.',\n required: false,\n examples: [\n 'email,profile',\n 'email,profile,https://www.googleapis.com/auth/gmail.readonly',\n 'email,profile,https://www.googleapis.com/auth/calendar.readonly',\n ],\n }),\n },\n\n SPFN_AUTH_GOOGLE_REDIRECT_URI: {\n ...envString({\n description: 'Google OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/google/callback. The override must stay on the web app origin at this exact path — the CSRF cookie for the callback is host-only and the app rewrites /_auth/:path* to the API, so a callback that lands anywhere else is refused for CSRF. Checked at boot: a value off the web app origin or off the callback path refuses to start. The one case for an override elsewhere is the direct POST /_auth/oauth/start flow on a split deployment (no Next.js interceptor, so its CSRF cookie is on the API host), which also needs SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK=off.',\n required: false,\n examples: [\n 'https://app.example.com/_auth/oauth/google/callback',\n 'http://localhost:3000/_auth/oauth/google/callback',\n ],\n }),\n },\n\n // ============================================================================\n // OAuth Configuration - Kakao\n // ============================================================================\n SPFN_AUTH_KAKAO_CLIENT_ID: {\n ...envString({\n description: 'Kakao Login REST API key. Used as the OAuth client_id.',\n required: false,\n examples: ['your-kakao-rest-api-key'],\n }),\n },\n\n SPFN_AUTH_KAKAO_CLIENT_SECRET: {\n ...envString({\n description: 'Kakao Login client secret. Required when the Kakao client-secret feature is enabled.',\n required: false,\n sensitive: true,\n examples: ['your-kakao-client-secret'],\n }),\n },\n\n SPFN_AUTH_KAKAO_ADMIN_KEY: {\n ...envString({\n description: 'Kakao app admin key. Required to verify the User Unlinked webhook (Authorization: KakaoAK header).',\n required: false,\n sensitive: true,\n examples: ['your-kakao-admin-key'],\n }),\n },\n\n SPFN_AUTH_KAKAO_SCOPES: {\n ...envString({\n description: 'Comma-separated Kakao consent scopes. Defaults to account_email.',\n required: false,\n examples: ['account_email'],\n }),\n },\n\n SPFN_AUTH_KAKAO_REDIRECT_URI: {\n ...envString({\n description: 'Kakao OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/kakao/callback. The override must stay on the web app origin at this exact path — the CSRF cookie for the callback is host-only and the app rewrites /_auth/:path* to the API, so a callback that lands anywhere else is refused for CSRF. Checked at boot: a value off the web app origin or off the callback path refuses to start. The one case for an override elsewhere is the direct POST /_auth/oauth/start flow on a split deployment (no Next.js interceptor, so its CSRF cookie is on the API host), which also needs SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK=off.',\n required: false,\n examples: ['https://app.example.com/_auth/oauth/kakao/callback'],\n }),\n },\n\n // ============================================================================\n // OAuth Configuration - Naver\n // ============================================================================\n SPFN_AUTH_NAVER_CLIENT_ID: {\n ...envString({\n description: 'Naver Login OAuth client ID.',\n required: false,\n examples: ['your-naver-client-id'],\n }),\n },\n\n SPFN_AUTH_NAVER_CLIENT_SECRET: {\n ...envString({\n description: 'Naver Login OAuth client secret.',\n required: false,\n sensitive: true,\n examples: ['your-naver-client-secret'],\n }),\n },\n\n SPFN_AUTH_NAVER_REDIRECT_URI: {\n ...envString({\n description: 'Naver OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/naver/callback. The override must stay on the web app origin at this exact path — the CSRF cookie for the callback is host-only and the app rewrites /_auth/:path* to the API, so a callback that lands anywhere else is refused for CSRF. Checked at boot: a value off the web app origin or off the callback path refuses to start. The one case for an override elsewhere is the direct POST /_auth/oauth/start flow on a split deployment (no Next.js interceptor, so its CSRF cookie is on the API host), which also needs SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK=off.',\n required: false,\n examples: ['https://app.example.com/_auth/oauth/naver/callback'],\n }),\n },\n\n // ============================================================================\n // OAuth Configuration - GitHub\n // ============================================================================\n SPFN_AUTH_GITHUB_CLIENT_ID: {\n ...envString({\n description: 'GitHub OAuth app client ID. When set, GitHub OAuth routes are automatically enabled.',\n required: false,\n examples: ['Iv1.abc123def456'],\n }),\n },\n\n SPFN_AUTH_GITHUB_CLIENT_SECRET: {\n ...envString({\n description: 'GitHub OAuth app client secret.',\n required: false,\n sensitive: true,\n examples: ['your-github-client-secret'],\n }),\n },\n\n SPFN_AUTH_GITHUB_SCOPES: {\n ...envString({\n description: 'Comma-separated GitHub OAuth scopes. Defaults to \"read:user,user:email\".',\n required: false,\n examples: ['read:user,user:email'],\n }),\n },\n\n SPFN_AUTH_GITHUB_REDIRECT_URI: {\n ...envString({\n description: 'GitHub OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/github/callback. The override must stay on the web app origin at this exact path — the CSRF cookie for the callback is host-only and the app rewrites /_auth/:path* to the API, so a callback that lands anywhere else is refused for CSRF. Checked at boot: a value off the web app origin or off the callback path refuses to start. The one case for an override elsewhere is the direct POST /_auth/oauth/start flow on a split deployment (no Next.js interceptor, so its CSRF cookie is on the API host), which also needs SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK=off.',\n required: false,\n examples: ['https://app.example.com/_auth/oauth/github/callback'],\n }),\n },\n\n SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK: {\n ...envString({\n description: 'Boot-time check of the four SPFN_AUTH_<PROVIDER>_REDIRECT_URI overrides: each one that is set must sit on the web app origin ({NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}) at /_auth/oauth/<provider>/callback, because the callback CSRF cookie is host-only. \"off\" is the only value that disables the check — unset, \"on\" and anything else all run it.',\n required: false,\n examples: ['off'],\n }),\n },\n\n // ============================================================================\n // Native Social Login (mobile/web id_token verification)\n //\n // 네이티브 SDK가 받은 id_token을 서버가 JWKS로 검증하는 경로 전용 설정.\n // authorization code 교환을 하지 않으므로 client secret이 필요 없다.\n // audience(aud)로 허용할 client id 목록만 지정한다.\n // ============================================================================\n SPFN_AUTH_GOOGLE_NATIVE_CLIENT_IDS: {\n ...envString({\n description: 'Comma-separated Google client IDs accepted as id_token audience for native sign-in (iOS, Android, web). When set, Google native sign-in is enabled. SPFN_AUTH_GOOGLE_CLIENT_ID is also accepted automatically.',\n required: false,\n examples: [\n '123-ios.apps.googleusercontent.com,123-android.apps.googleusercontent.com',\n ],\n }),\n },\n\n SPFN_AUTH_APPLE_CLIENT_IDS: {\n ...envString({\n description: 'Comma-separated Apple client IDs accepted as id_token audience for native sign-in (iOS bundle ID, web/Android Services ID). When set, Apple native sign-in is enabled.',\n required: false,\n examples: [\n 'com.example.app,com.example.app.service',\n ],\n }),\n },\n\n SPFN_AUTH_KAKAO_NATIVE_CLIENT_IDS: {\n ...envString({\n description: 'Comma-separated Kakao app keys accepted as id_token audience for native sign-in (native app key). SPFN_AUTH_KAKAO_CLIENT_ID (REST API key) is also accepted automatically, so native sign-in is available when either variable is set. Requires OpenID Connect to be enabled in the Kakao developer console.',\n required: false,\n examples: [\n 'your-kakao-native-app-key',\n ],\n }),\n },\n\n SPFN_AUTH_NAVER_NATIVE_CLIENT_IDS: {\n ...envString({\n description: 'Comma-separated Naver client IDs accepted as id_token audience for native sign-in. SPFN_AUTH_NAVER_CLIENT_ID is also accepted automatically, and one Naver application has a single client ID covering web and app environments — set this only when the app uses a separate application.',\n required: false,\n examples: [\n 'your-naver-app-client-id',\n ],\n }),\n },\n\n SPFN_AUTH_OAUTH_SUCCESS_URL: {\n ...envString({\n description: 'OAuth callback page URL. This page should use OAuthCallback component to finalize session.',\n required: false,\n default: '/auth/callback',\n examples: [\n '/auth/callback',\n 'https://app.example.com/auth/callback',\n ],\n }),\n },\n\n SPFN_AUTH_OAUTH_ERROR_URL: {\n ...envString({\n description: 'URL to redirect after OAuth error. Use {error} placeholder for error message.',\n required: false,\n default: '/auth/error?error={error}',\n examples: [\n 'https://app.example.com/auth/error?error={error}',\n 'http://localhost:3000/auth/error?error={error}',\n ],\n }),\n },\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/config/index.ts","../src/config/schema.ts"],"sourcesContent":["/**\n * Core Package Configuration\n *\n * @example\n * ```typescript\n * import { registry } from '@spfn/core/config';\n *\n * const env = registry.validate();\n * console.log(env.DB_POOL_MAX);\n * ```\n *\n * @module config\n */\n\nimport { createEnvRegistry } from '@spfn/core/env';\nimport { authEnvSchema } from './schema';\n\nexport { authEnvSchema as envSchema } from './schema';\n\n/**\n * Environment registry\n */\nconst registry = createEnvRegistry(authEnvSchema);\nexport const env = registry.validate();\n","/**\n * Auth Environment Variable Schema\n *\n * Centralized schema definition for all environment variables used in @spfn/auth.\n * This provides type safety, validation, and documentation for Auth configuration.\n *\n * @module config/schema\n */\n\nimport {\n defineEnvSchema,\n envString,\n envNumber,\n envBoolean,\n envEnum,\n createSecureSecretParser,\n createPasswordParser,\n} from '@spfn/core/env';\n\n/**\n * Auth environment variable schema\n *\n * Defines all Auth environment variables with:\n * - Type information\n * - Default values\n * - Validation rules\n * - Documentation\n *\n * @example\n * ```typescript\n * import { envSchema } from '@spfn/auth/config';\n *\n * // Access schema information\n * console.log(envSchema.SPFN_AUTH_SESSION_SECRET.description);\n * console.log(envSchema.SPFN_AUTH_JWT_EXPIRES_IN.default);\n * ```\n */\nexport const authEnvSchema = defineEnvSchema({\n // ============================================================================\n // Session Configuration\n // ============================================================================\n SPFN_AUTH_SESSION_SECRET: {\n ...envString({\n description: 'Session encryption secret (minimum 32 characters for AES-256)',\n required: true,\n fallbackKeys: ['SESSION_SECRET'],\n validator: createSecureSecretParser({\n minLength: 32,\n minUniqueChars: 16,\n minEntropy: 3.5,\n }),\n sensitive: true,\n nextjs: true, // Required for Next.js RSC session validation\n examples: [\n 'my-super-secret-session-key-at-least-32-chars-long',\n 'use-a-cryptographically-secure-random-string-here',\n ],\n }),\n },\n\n SPFN_AUTH_SESSION_TTL: {\n ...envString({\n description: 'Session TTL (time to live) - supports duration strings like \\'7d\\', \\'12h\\', \\'45m\\'',\n default: '7d',\n required: false,\n nextjs: true, // May be needed for session validation in Next.js RSC\n examples: ['7d', '30d', '12h', '45m', '3600'],\n }),\n },\n\n // ============================================================================\n // JWT Configuration\n // ============================================================================\n SPFN_AUTH_JWT_SECRET: {\n ...envString({\n description: 'JWT signing secret for server-signed tokens (legacy mode)',\n default: 'dev-secret-key-change-in-production',\n required: false,\n examples: [\n 'your-jwt-secret-key-here',\n 'use-different-from-session-secret',\n ],\n }),\n },\n\n SPFN_AUTH_JWT_EXPIRES_IN: {\n ...envString({\n description: 'JWT token expiration time (e.g., \\'7d\\', \\'24h\\', \\'1h\\')',\n default: '7d',\n required: false,\n examples: ['7d', '24h', '1h', '30m'],\n }),\n },\n\n // ============================================================================\n // Security Configuration\n // ============================================================================\n SPFN_AUTH_COOKIE_SECURE: {\n ...envBoolean({\n description: 'Override cookie Secure flag. Defaults to NODE_ENV === \"production\". Set to false for HTTP-only environments (e.g. bastion over plain HTTP).',\n required: false,\n nextjs: true,\n examples: [true, false],\n }),\n },\n\n SPFN_AUTH_CSRF: {\n ...envString({\n description: 'CSRF protection for cookie-session mutations in the Next.js proxy: off | warn | enforce. Unset behaves as \"warn\" (log what would be refused, allow it through). configureAuth({ csrf: { mode } }) takes precedence.',\n required: false,\n nextjs: true, // The check runs in the Next.js proxy\n examples: ['enforce', 'warn', 'off'],\n }),\n },\n\n SPFN_AUTH_BCRYPT_SALT_ROUNDS: {\n ...envNumber({\n description: 'Bcrypt salt rounds (cost factor, higher = more secure but slower)',\n default: 12,\n required: false,\n examples: [10, 12, 14],\n }),\n key: 'SPFN_AUTH_BCRYPT_SALT_ROUNDS',\n },\n\n SPFN_AUTH_VERIFICATION_TOKEN_SECRET: {\n ...envString({\n description: 'Verification token secret for email verification, password reset, etc.',\n required: true,\n examples: [\n 'your-verification-token-secret',\n 'can-be-different-from-jwt-secret',\n ],\n }),\n },\n\n SPFN_AUTH_TOKEN_ENCRYPTION_KEYS: {\n ...envString({\n description: 'Backend-only OAuth token encryption keyring. Comma-separated <keyId>:<base64-encoded 32-byte key> entries; the first key encrypts new values and remaining keys decrypt during rotation.',\n required: false,\n sensitive: true,\n examples: [\n 'v2:<base64-encoded-32-byte-key>,v1:<previous-base64-encoded-32-byte-key>',\n ],\n }),\n },\n\n // ============================================================================\n // Admin Account Configuration\n // ============================================================================\n SPFN_AUTH_ADMIN_ACCOUNTS: {\n ...envString({\n description: 'JSON array of admin accounts (recommended for multiple admins)',\n required: false,\n examples: [\n '[{\"email\":\"admin@example.com\",\"password\":\"secure-pass\",\"role\":\"admin\"}]',\n '[{\"email\":\"super@example.com\",\"password\":\"pass1\",\"role\":\"superadmin\"},{\"email\":\"admin@example.com\",\"password\":\"pass2\",\"role\":\"admin\"}]',\n ],\n }),\n },\n\n SPFN_AUTH_ADMIN_EMAILS: {\n ...envString({\n description: 'Comma-separated list of admin emails (legacy CSV format)',\n required: false,\n examples: [\n 'admin@example.com,user@example.com',\n 'super@example.com,admin@example.com,user@example.com',\n ],\n }),\n },\n\n SPFN_AUTH_ADMIN_PASSWORDS: {\n ...envString({\n description: 'Comma-separated list of admin passwords (legacy CSV format)',\n required: false,\n examples: [\n 'admin-pass,user-pass',\n 'super-pass,admin-pass,user-pass',\n ],\n }),\n },\n\n SPFN_AUTH_ADMIN_ROLES: {\n ...envString({\n description: 'Comma-separated list of admin roles (legacy CSV format)',\n required: false,\n examples: [\n 'admin,user',\n 'superadmin,admin,user',\n ],\n }),\n },\n\n SPFN_AUTH_ADMIN_EMAIL: {\n ...envString({\n description: 'Single admin email (simplest format)',\n required: false,\n examples: ['admin@example.com'],\n }),\n },\n\n SPFN_AUTH_ADMIN_PASSWORD: {\n ...envString({\n description: 'Single admin password (simplest format)',\n required: false,\n validator: createPasswordParser({\n minLength: 8,\n requireUppercase: true,\n requireLowercase: true,\n requireNumber: true,\n requireSpecial: true,\n }),\n sensitive: true,\n examples: ['SecureAdmin123!'],\n }),\n },\n\n // ============================================================================\n // Username Configuration\n // ============================================================================\n SPFN_AUTH_RESERVED_USERNAMES: {\n ...envString({\n description: 'Comma-separated list of reserved usernames that cannot be registered',\n required: false,\n default: 'admin,root,system,support,help,moderator,superadmin',\n examples: [\n 'admin,root,system,support,help',\n 'admin,root,system,support,help,moderator,superadmin,operator',\n ],\n }),\n },\n\n SPFN_AUTH_USERNAME_MIN_LENGTH: {\n ...envNumber({\n description: 'Minimum username length',\n default: 3,\n required: false,\n examples: [2, 3, 4],\n }),\n },\n\n SPFN_AUTH_USERNAME_MAX_LENGTH: {\n ...envNumber({\n description: 'Maximum username length',\n default: 30,\n required: false,\n examples: [20, 30, 50],\n }),\n },\n\n // ============================================================================\n // Verified-email signup\n // ============================================================================\n SPFN_AUTH_SIGNUP_LINK_TTL_MINUTES: {\n ...envNumber({\n description: 'How long an emailed signup confirmation link stays valid. Long enough to survive a mail delay, short enough that a link left in an inbox stops working.',\n default: 30,\n required: false,\n examples: [15, 30, 60],\n }),\n },\n\n SPFN_AUTH_SIGNUP_SETUP_TTL_MINUTES: {\n ...envNumber({\n description: 'How long the password-setup session opened by a confirmation link stays valid. Covers one sitting at the password form, not an abandoned tab.',\n default: 15,\n required: false,\n examples: [10, 15, 30],\n }),\n },\n\n SPFN_AUTH_SIGNUP_CONFIRM_PATH: {\n ...envString({\n description: 'App page the emailed confirmation link opens, as a path on {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}. The page reads the token from the query string and posts it to /_auth/signup/email/confirm; it is a page in your app, not an API route.',\n default: '/signup/confirm',\n required: false,\n examples: ['/signup/confirm', '/auth/confirm', '/join/verify'],\n }),\n },\n\n // ============================================================================\n // Password reset (verified email)\n // ============================================================================\n SPFN_AUTH_PASSWORD_RESET_LINK_TTL_MINUTES: {\n ...envNumber({\n description: 'How long an emailed password reset link stays valid. Long enough to survive a mail delay, short enough that a link left in an inbox stops working.',\n default: 30,\n required: false,\n examples: [15, 30, 60],\n }),\n },\n\n SPFN_AUTH_PASSWORD_RESET_SETUP_TTL_MINUTES: {\n ...envNumber({\n description: 'How long the password-setup session opened by a reset link stays valid. Covers one sitting at the new-password form, not an abandoned tab.',\n default: 15,\n required: false,\n examples: [10, 15, 30],\n }),\n },\n\n SPFN_AUTH_PASSWORD_RESET_CONFIRM_PATH: {\n ...envString({\n description: 'App page the emailed reset link opens, as a path on {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}. The page reads the token from the query string and posts it to /_auth/password/reset/confirm; it is a page in your app, not an API route.',\n default: '/password/reset',\n required: false,\n examples: ['/password/reset', '/auth/reset', '/forgot/new-password'],\n }),\n },\n\n // ============================================================================\n // Signed sign-out-everywhere link\n // ============================================================================\n SPFN_AUTH_REVOKE_ALL_LINK_TTL_MINUTES: {\n ...envNumber({\n description: 'How long a signed sign-out-everywhere link stays valid. The same default as the other two link flows: long enough to survive a mail delay, short enough that a link left in an inbox stops working — and this one signs every device out.',\n default: 30,\n required: false,\n examples: [15, 30, 60],\n }),\n },\n\n SPFN_AUTH_REVOKE_ALL_CONFIRM_PATH: {\n ...envString({\n description: 'App page the sign-out-everywhere link opens, as a path on {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}. The page reads the token from the query string and posts it to /_auth/keys/revoke-all/confirm to describe the link, then to /_auth/keys/revoke-all/consume when the owner confirms; it is a page in your app, not an API route.',\n default: '/account/revoke-all',\n required: false,\n examples: ['/account/revoke-all', '/security/sign-out-everywhere'],\n }),\n },\n\n // ============================================================================\n // Link mail delivery\n // ============================================================================\n SPFN_AUTH_LINK_MAIL_DELIVERY: {\n ...envEnum(['auto', 'inline', 'queued'] as const, {\n description: 'Who sends signup-link, password-reset and account-exists mail. \\'auto\\' (default) queues it on auth.link-mail when pg-boss is initialised and sends it on the request path when it is not; \\'queued\\' always queues and surfaces an enqueue failure; \\'inline\\' always sends on the request path, which makes how long the request took reveal whether the address has an account.',\n default: 'auto',\n required: false,\n examples: ['auto', 'inline', 'queued'],\n }),\n },\n\n // ============================================================================\n // Passkeys (WebAuthn)\n // ============================================================================\n SPFN_AUTH_PASSKEY_RP_ID: {\n ...envString({\n description: 'Domain passkeys are bound to — a registrable domain with no protocol and no port. Defaults to the host of {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}. Set it explicitly when the app is served from several hosts that share a domain; every origin below must be that host or a subdomain of it. Changing it orphans every passkey already enrolled.',\n required: false,\n examples: ['example.com', 'app.example.com', 'localhost'],\n }),\n },\n\n SPFN_AUTH_PASSKEY_RP_NAME: {\n ...envString({\n description: \"Name the authenticator's own prompt shows the user. Defaults to the relying party ID.\",\n required: false,\n examples: ['Acme', 'Acme Staging'],\n }),\n },\n\n SPFN_AUTH_PASSKEY_ORIGINS: {\n ...envString({\n description: 'Comma-separated full origins allowed to run a passkey ceremony. Defaults to the origin of {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}. Each must be https (http only for localhost) and must be the relying party ID or a subdomain of it. Checked at boot: a value that breaks either rule refuses to start, because it would otherwise surface as the browser refusing every ceremony.',\n required: false,\n examples: ['https://app.example.com', 'https://app.example.com,https://admin.example.com', 'http://localhost:3000'],\n }),\n },\n\n SPFN_AUTH_PASSKEY_USER_VERIFICATION: {\n ...envString({\n description: \"How hard the authenticator must prove the person is present: 'preferred' or 'required'. 'discouraged' is refused at boot — a passkey is the whole credential here, so an assertion that skipped user verification would sign someone in on an unlocked device alone.\",\n default: 'preferred',\n required: false,\n examples: ['preferred', 'required'],\n }),\n },\n\n SPFN_AUTH_PASSKEY_CHALLENGE_TTL_SECONDS: {\n ...envNumber({\n description: 'How long the challenge minted by a passkey options call stays presentable. One ceremony at the authenticator, not an abandoned tab.',\n default: 300,\n required: false,\n examples: [120, 300, 600],\n }),\n },\n\n SPFN_AUTH_PASSKEY_RECENT_AUTH_MINUTES: {\n ...envNumber({\n description: 'How recently the calling device key must have been registered for enrolling or revoking a passkey to go through without the current password. Older than this and the request needs `currentPassword`; an account with no password has to sign in again.',\n default: 10,\n required: false,\n examples: [5, 10, 30],\n }),\n },\n\n // ============================================================================\n // Second factor (MFA)\n // ============================================================================\n SPFN_AUTH_MFA_ISSUER: {\n ...envString({\n description: 'Name the authenticator app files this account under, carried in the otpauth:// URI. Defaults to the passkey relying-party name, then to the host of {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}. Changing it after people have enrolled only relabels the entry in their app; the codes keep working.',\n required: false,\n examples: ['Acme', 'Acme Staging'],\n }),\n },\n\n SPFN_AUTH_MFA_STEP_UP_MINUTES: {\n ...envNumber({\n description: 'How recently an enrolled account must have proved its second factor on the calling device for a sensitive change (password change, sign out everywhere, disabling MFA, passkey management) to go through. Older than this and the request is 403 STEP_UP_REQUIRED until POST /_auth/mfa/step-up succeeds. Unenrolled accounts are unaffected.',\n default: 10,\n required: false,\n examples: [5, 10, 30],\n }),\n },\n\n SPFN_AUTH_MFA_CONFIRM_PATH: {\n ...envString({\n description: 'App page that asks for the second factor after a 202 sign-in, as a path on {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}. createOAuthCallbackHandler redirects the browser there with ?challenge= when a social sign-in needs a step-up; the page posts that challenge and a code to /_auth/mfa/verify. It is a page in your app, not an API route.',\n default: '/auth/mfa',\n required: false,\n examples: ['/auth/mfa', '/sign-in/two-factor'],\n }),\n },\n\n SPFN_AUTH_MFA_CHALLENGE_TTL_MINUTES: {\n ...envNumber({\n description: 'How long a new-device second-factor challenge stays spendable. A sign-in on an enrolled account from a device it has never seen answers 202 with a challenge instead of a session, and the key it registered is inactive until POST /_auth/mfa/verify spends it. This is how long the person has to reach for their authenticator — and how long an attacker holding only the password has. The Next.js proxy seals its pending cookie for the same span.',\n default: 10,\n required: false,\n examples: [5, 10, 15],\n }),\n },\n\n // ============================================================================\n // Session binding (#97)\n // ============================================================================\n SPFN_AUTH_BOUND_KEY_TTL_HOURS: {\n ...envNumber({\n description: 'How long a session key bound to a passkey lives. This is the window in which a copied session cookie is still indistinguishable from the original, so it is hours rather than days; past it the browser runs one WebAuthn ceremony and gets a new key. Only applies to accounts that turned session binding on.',\n default: 24,\n required: false,\n examples: [8, 24, 72],\n }),\n },\n\n SPFN_AUTH_BOUND_KEY_RENEW_GRACE_HOURS: {\n ...envNumber({\n description: 'How long after a bound key expires a passkey renewal is still offered. Past it the account signs in again. Open-ended grace would make an expired key a long-lived key with extra steps.',\n default: 168,\n required: false,\n examples: [24, 168, 720],\n }),\n },\n\n SPFN_AUTH_CONCURRENT_USE_WINDOW_MS: {\n ...envNumber({\n description: 'How far apart two sightings of one device key from two client addresses still count as concurrent use, surfaced as `concurrentUseAtMillis` on the key list. A signal for the owner to read, never a refusal — addresses change legitimately. Meaningful only where proxy-guard is configured, since without it every web request carries the Next.js server\\'s address.',\n default: 300000,\n required: false,\n examples: [60000, 300000, 900000],\n }),\n },\n\n SPFN_AUTH_SESSION_RENEW_PATH: {\n ...envString({\n description: 'Page in your app that runs the renewal ceremony. `RequireAuth` redirects a bound session whose key expired here instead of to the sign-in page; the page calls `renewSession(api)` and returns the user to where they were. Override per guard with the `renewalPath` prop.',\n default: '/auth/renew',\n required: false,\n nextjs: true, // Read by RequireAuth, which renders in the Next.js runtime\n examples: ['/auth/renew', '/session/renew'],\n }),\n },\n\n // ============================================================================\n // API Configuration\n // ============================================================================\n SPFN_API_URL: {\n ...envString({\n description: 'Internal API URL for server-to-server communication',\n default: 'http://localhost:8790',\n required: false,\n examples: [\n 'https://api.example.com',\n 'http://localhost:8790',\n ],\n }),\n },\n\n NEXT_PUBLIC_SPFN_API_URL: {\n ...envString({\n description: 'Public-facing API URL used for browser-facing redirects. Falls back to SPFN_API_URL if not set.',\n required: false,\n examples: [\n 'https://api.example.com',\n 'http://localhost:8790',\n ],\n }),\n },\n\n SPFN_APP_URL: {\n ...envString({\n description: 'Next.js application URL (internal). Used for server-to-server communication.',\n default: 'http://localhost:3000',\n required: false,\n examples: [\n 'https://app.example.com',\n 'http://localhost:3000',\n ],\n }),\n },\n\n NEXT_PUBLIC_SPFN_APP_URL: {\n ...envString({\n description: 'Public-facing Next.js app URL for browser redirects (e.g. OAuth redirect). Falls back to SPFN_APP_URL if not set.',\n required: false,\n examples: [\n 'https://app.example.com',\n 'http://localhost:3000',\n ],\n }),\n },\n\n // ============================================================================\n // OAuth Configuration - Google\n // ============================================================================\n SPFN_AUTH_GOOGLE_CLIENT_ID: {\n ...envString({\n description: 'Google OAuth 2.0 Client ID. When set, Google OAuth routes are automatically enabled.',\n required: false,\n examples: ['123456789-abc123.apps.googleusercontent.com'],\n }),\n },\n\n SPFN_AUTH_GOOGLE_CLIENT_SECRET: {\n ...envString({\n description: 'Google OAuth 2.0 Client Secret',\n required: false,\n sensitive: true,\n examples: ['GOCSPX-abcdefghijklmnop'],\n }),\n },\n\n SPFN_AUTH_GOOGLE_SCOPES: {\n ...envString({\n description: 'Comma-separated Google OAuth scopes. Defaults to \"email,profile\" if not set.',\n required: false,\n examples: [\n 'email,profile',\n 'email,profile,https://www.googleapis.com/auth/gmail.readonly',\n 'email,profile,https://www.googleapis.com/auth/calendar.readonly',\n ],\n }),\n },\n\n SPFN_AUTH_GOOGLE_REDIRECT_URI: {\n ...envString({\n description: 'Google OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/google/callback. The override must stay on the web app origin at this exact path — the CSRF cookie for the callback is host-only and the app rewrites /_auth/:path* to the API, so a callback that lands anywhere else is refused for CSRF. Checked at boot: a value off the web app origin or off the callback path refuses to start. The one case for an override elsewhere is the direct POST /_auth/oauth/start flow on a split deployment (no Next.js interceptor, so its CSRF cookie is on the API host), which also needs SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK=off.',\n required: false,\n examples: [\n 'https://app.example.com/_auth/oauth/google/callback',\n 'http://localhost:3000/_auth/oauth/google/callback',\n ],\n }),\n },\n\n // ============================================================================\n // OAuth Configuration - Kakao\n // ============================================================================\n SPFN_AUTH_KAKAO_CLIENT_ID: {\n ...envString({\n description: 'Kakao Login REST API key. Used as the OAuth client_id.',\n required: false,\n examples: ['your-kakao-rest-api-key'],\n }),\n },\n\n SPFN_AUTH_KAKAO_CLIENT_SECRET: {\n ...envString({\n description: 'Kakao Login client secret. Required when the Kakao client-secret feature is enabled.',\n required: false,\n sensitive: true,\n examples: ['your-kakao-client-secret'],\n }),\n },\n\n SPFN_AUTH_KAKAO_ADMIN_KEY: {\n ...envString({\n description: 'Kakao app admin key. Required to verify the User Unlinked webhook (Authorization: KakaoAK header).',\n required: false,\n sensitive: true,\n examples: ['your-kakao-admin-key'],\n }),\n },\n\n SPFN_AUTH_KAKAO_SCOPES: {\n ...envString({\n description: 'Comma-separated Kakao consent scopes. Defaults to account_email.',\n required: false,\n examples: ['account_email'],\n }),\n },\n\n SPFN_AUTH_KAKAO_REDIRECT_URI: {\n ...envString({\n description: 'Kakao OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/kakao/callback. The override must stay on the web app origin at this exact path — the CSRF cookie for the callback is host-only and the app rewrites /_auth/:path* to the API, so a callback that lands anywhere else is refused for CSRF. Checked at boot: a value off the web app origin or off the callback path refuses to start. The one case for an override elsewhere is the direct POST /_auth/oauth/start flow on a split deployment (no Next.js interceptor, so its CSRF cookie is on the API host), which also needs SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK=off.',\n required: false,\n examples: ['https://app.example.com/_auth/oauth/kakao/callback'],\n }),\n },\n\n // ============================================================================\n // OAuth Configuration - Naver\n // ============================================================================\n SPFN_AUTH_NAVER_CLIENT_ID: {\n ...envString({\n description: 'Naver Login OAuth client ID.',\n required: false,\n examples: ['your-naver-client-id'],\n }),\n },\n\n SPFN_AUTH_NAVER_CLIENT_SECRET: {\n ...envString({\n description: 'Naver Login OAuth client secret.',\n required: false,\n sensitive: true,\n examples: ['your-naver-client-secret'],\n }),\n },\n\n SPFN_AUTH_NAVER_REDIRECT_URI: {\n ...envString({\n description: 'Naver OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/naver/callback. The override must stay on the web app origin at this exact path — the CSRF cookie for the callback is host-only and the app rewrites /_auth/:path* to the API, so a callback that lands anywhere else is refused for CSRF. Checked at boot: a value off the web app origin or off the callback path refuses to start. The one case for an override elsewhere is the direct POST /_auth/oauth/start flow on a split deployment (no Next.js interceptor, so its CSRF cookie is on the API host), which also needs SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK=off.',\n required: false,\n examples: ['https://app.example.com/_auth/oauth/naver/callback'],\n }),\n },\n\n // ============================================================================\n // OAuth Configuration - GitHub\n // ============================================================================\n SPFN_AUTH_GITHUB_CLIENT_ID: {\n ...envString({\n description: 'GitHub OAuth app client ID. When set, GitHub OAuth routes are automatically enabled.',\n required: false,\n examples: ['Iv1.abc123def456'],\n }),\n },\n\n SPFN_AUTH_GITHUB_CLIENT_SECRET: {\n ...envString({\n description: 'GitHub OAuth app client secret.',\n required: false,\n sensitive: true,\n examples: ['your-github-client-secret'],\n }),\n },\n\n SPFN_AUTH_GITHUB_SCOPES: {\n ...envString({\n description: 'Comma-separated GitHub OAuth scopes. Defaults to \"read:user,user:email\".',\n required: false,\n examples: ['read:user,user:email'],\n }),\n },\n\n SPFN_AUTH_GITHUB_REDIRECT_URI: {\n ...envString({\n description: 'GitHub OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/github/callback. The override must stay on the web app origin at this exact path — the CSRF cookie for the callback is host-only and the app rewrites /_auth/:path* to the API, so a callback that lands anywhere else is refused for CSRF. Checked at boot: a value off the web app origin or off the callback path refuses to start. The one case for an override elsewhere is the direct POST /_auth/oauth/start flow on a split deployment (no Next.js interceptor, so its CSRF cookie is on the API host), which also needs SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK=off.',\n required: false,\n examples: ['https://app.example.com/_auth/oauth/github/callback'],\n }),\n },\n\n SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK: {\n ...envString({\n description: 'Boot-time check of the four SPFN_AUTH_<PROVIDER>_REDIRECT_URI overrides: each one that is set must sit on the web app origin ({NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}) at /_auth/oauth/<provider>/callback, because the callback CSRF cookie is host-only. \"off\" is the only value that disables the check — unset, \"on\" and anything else all run it.',\n required: false,\n examples: ['off'],\n }),\n },\n\n // ============================================================================\n // Native Social Login (mobile/web id_token verification)\n //\n // 네이티브 SDK가 받은 id_token을 서버가 JWKS로 검증하는 경로 전용 설정.\n // authorization code 교환을 하지 않으므로 client secret이 필요 없다.\n // audience(aud)로 허용할 client id 목록만 지정한다.\n // ============================================================================\n SPFN_AUTH_GOOGLE_NATIVE_CLIENT_IDS: {\n ...envString({\n description: 'Comma-separated Google client IDs accepted as id_token audience for native sign-in (iOS, Android, web). When set, Google native sign-in is enabled. SPFN_AUTH_GOOGLE_CLIENT_ID is also accepted automatically.',\n required: false,\n examples: [\n '123-ios.apps.googleusercontent.com,123-android.apps.googleusercontent.com',\n ],\n }),\n },\n\n SPFN_AUTH_APPLE_CLIENT_IDS: {\n ...envString({\n description: 'Comma-separated Apple client IDs accepted as id_token audience for native sign-in (iOS bundle ID, web/Android Services ID). When set, Apple native sign-in is enabled.',\n required: false,\n examples: [\n 'com.example.app,com.example.app.service',\n ],\n }),\n },\n\n SPFN_AUTH_KAKAO_NATIVE_CLIENT_IDS: {\n ...envString({\n description: 'Comma-separated Kakao app keys accepted as id_token audience for native sign-in (native app key). SPFN_AUTH_KAKAO_CLIENT_ID (REST API key) is also accepted automatically, so native sign-in is available when either variable is set. Requires OpenID Connect to be enabled in the Kakao developer console.',\n required: false,\n examples: [\n 'your-kakao-native-app-key',\n ],\n }),\n },\n\n SPFN_AUTH_NAVER_NATIVE_CLIENT_IDS: {\n ...envString({\n description: 'Comma-separated Naver client IDs accepted as id_token audience for native sign-in. SPFN_AUTH_NAVER_CLIENT_ID is also accepted automatically, and one Naver application has a single client ID covering web and app environments — set this only when the app uses a separate application.',\n required: false,\n examples: [\n 'your-naver-app-client-id',\n ],\n }),\n },\n\n SPFN_AUTH_OAUTH_SUCCESS_URL: {\n ...envString({\n description: 'OAuth callback page URL. This page should use OAuthCallback component to finalize session.',\n required: false,\n default: '/auth/callback',\n examples: [\n '/auth/callback',\n 'https://app.example.com/auth/callback',\n ],\n }),\n },\n\n SPFN_AUTH_OAUTH_ERROR_URL: {\n ...envString({\n description: 'URL to redirect after OAuth error. Use {error} placeholder for error message.',\n required: false,\n default: '/auth/error?error={error}',\n examples: [\n 'https://app.example.com/auth/error?error={error}',\n 'http://localhost:3000/auth/error?error={error}',\n ],\n }),\n },\n});\n"],"mappings":"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","names":[]}
|
package/dist/crypto.d.ts
CHANGED
package/dist/errors.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ConflictError, ForbiddenError, NotFoundError, ValidationError, UnauthorizedError, HttpError, ErrorRegistry } from '@spfn/core/errors';
|
|
1
|
+
import { ConflictError, ForbiddenError, NotFoundError, ValidationError, UnauthorizedError, HttpError, BadRequestError, InternalServerError, ErrorRegistry } from '@spfn/core/errors';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Authentication & Authorization Error Classes
|
|
@@ -605,6 +605,106 @@ declare class LastRecoveryCredentialError extends ConflictError {
|
|
|
605
605
|
details?: Record<string, any>;
|
|
606
606
|
});
|
|
607
607
|
}
|
|
608
|
+
/**
|
|
609
|
+
* Session Renewal Required Error (401)
|
|
610
|
+
*
|
|
611
|
+
* Minted by the Next.js proxy, not by the backend: a bound session whose key has
|
|
612
|
+
* run out, on a request the proxy therefore does not forward. The browser's
|
|
613
|
+
* answer is to run the passkey ceremony `renewSession()` wraps and retry, which
|
|
614
|
+
* is the one thing a copied cookie cannot do.
|
|
615
|
+
*
|
|
616
|
+
* Registered here so the proxy's 401 arrives as this class rather than as a bare
|
|
617
|
+
* `ApiError` — the refusal is one an app branches on, and `err instanceof` is
|
|
618
|
+
* how every other branch in this package is written.
|
|
619
|
+
*/
|
|
620
|
+
declare class SessionRenewalRequiredError extends UnauthorizedError {
|
|
621
|
+
readonly code = "SESSION_RENEWAL_REQUIRED";
|
|
622
|
+
constructor(data?: {
|
|
623
|
+
message?: string;
|
|
624
|
+
details?: Record<string, any>;
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Session Renewal Refused Error (401)
|
|
629
|
+
*
|
|
630
|
+
* The one answer `session/renew/options` and `session/renew/verify` give to every
|
|
631
|
+
* refusal: a key id that names nothing, someone else's key, an unbound key, a
|
|
632
|
+
* revoked key, a key past its renewal grace, an inactive account, a spent
|
|
633
|
+
* challenge, an assertion that did not verify, a passkey belonging to another
|
|
634
|
+
* account.
|
|
635
|
+
*
|
|
636
|
+
* One answer on purpose. The routes are public — they have to be, since the
|
|
637
|
+
* session they repair is the one that stopped working — and a refusal that
|
|
638
|
+
* varied would tell an unauthenticated caller whether a key id is live, which is
|
|
639
|
+
* exactly what the cookie-copying adversary holds and would want confirmed.
|
|
640
|
+
*/
|
|
641
|
+
declare class SessionRenewalRefusedError extends UnauthorizedError {
|
|
642
|
+
readonly code = "SESSION_RENEWAL_REFUSED";
|
|
643
|
+
constructor(data?: {
|
|
644
|
+
message?: string;
|
|
645
|
+
details?: Record<string, any>;
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Session Context Changed Error (401)
|
|
650
|
+
*
|
|
651
|
+
* Minted by the Next.js proxy: a bound session presented from a different
|
|
652
|
+
* browser family than the one it was sealed from. Browsers do not share cookie
|
|
653
|
+
* jars, so a session that moves between two of them moved because somebody
|
|
654
|
+
* copied it — this is the only defence that acts before the bound key runs out,
|
|
655
|
+
* and it is the reason the three session cookies are cleared with the refusal.
|
|
656
|
+
*
|
|
657
|
+
* Only for bound sessions, and only when the request carried a `user-agent` at
|
|
658
|
+
* all. An absent one is no signal rather than a different family: a server
|
|
659
|
+
* component calling the RPC proxy sends none.
|
|
660
|
+
*/
|
|
661
|
+
declare class SessionContextChangedError extends UnauthorizedError {
|
|
662
|
+
readonly code = "SESSION_CONTEXT_CHANGED";
|
|
663
|
+
constructor(data?: {
|
|
664
|
+
message?: string;
|
|
665
|
+
details?: Record<string, any>;
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Session Binding Unavailable Error (400)
|
|
670
|
+
*
|
|
671
|
+
* Thrown when an account asks to bind its session on a deployment where the
|
|
672
|
+
* backend cannot tell a request through the trusted Next.js proxy from a direct
|
|
673
|
+
* one — `proxy-guard` unconfigured, so `clientType` is never `'web'`.
|
|
674
|
+
*
|
|
675
|
+
* A configuration refusal rather than a security one, which is why it is a 400
|
|
676
|
+
* and why the message names what to set: with nothing to distinguish the proxy,
|
|
677
|
+
* every key would be registered unbound and the setting would be a switch that
|
|
678
|
+
* reports success and protects nothing.
|
|
679
|
+
*/
|
|
680
|
+
declare class SessionBindingUnavailableError extends BadRequestError {
|
|
681
|
+
readonly code = "SESSION_BINDING_UNAVAILABLE";
|
|
682
|
+
constructor(data?: {
|
|
683
|
+
message?: string;
|
|
684
|
+
details?: Record<string, any>;
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* Session Reseal Failed Error (500)
|
|
689
|
+
*
|
|
690
|
+
* Minted by the Next.js proxy when a binding change committed on the backend but
|
|
691
|
+
* the session cookie could not be re-sealed to match it.
|
|
692
|
+
*
|
|
693
|
+
* Answered instead of the route's 200, which is the point: the setting has moved
|
|
694
|
+
* and the cookie has not, and the two disagreeing is the state the whole feature
|
|
695
|
+
* is built to avoid — a bound key with a cookie that says unbound is cleared as
|
|
696
|
+
* an ordinary expired session a day later, and an unbound key with a cookie that
|
|
697
|
+
* says bound asks for a renewal the backend will refuse. The three session
|
|
698
|
+
* cookies go with this refusal, so the repair is a sign-in, which mints a cookie
|
|
699
|
+
* that agrees with the row.
|
|
700
|
+
*/
|
|
701
|
+
declare class SessionResealFailedError extends InternalServerError {
|
|
702
|
+
readonly code = "SESSION_RESEAL_FAILED";
|
|
703
|
+
constructor(data?: {
|
|
704
|
+
message?: string;
|
|
705
|
+
details?: Record<string, any>;
|
|
706
|
+
});
|
|
707
|
+
}
|
|
608
708
|
/**
|
|
609
709
|
* Passkey Config Error (500)
|
|
610
710
|
*
|
|
@@ -753,6 +853,48 @@ declare class StepUpRequiredError extends ForbiddenError {
|
|
|
753
853
|
details?: Record<string, any>;
|
|
754
854
|
});
|
|
755
855
|
}
|
|
856
|
+
/**
|
|
857
|
+
* Session Pending Mismatch Error (401)
|
|
858
|
+
*
|
|
859
|
+
* Minted by the Next.js proxy: a second-factor verification succeeded at the
|
|
860
|
+
* backend, but the pending cookie this browser is holding was baked for a
|
|
861
|
+
* different challenge or a different device key.
|
|
862
|
+
*
|
|
863
|
+
* The comparison is the whole reason the cookie exists. Without it the proxy
|
|
864
|
+
* would seal whatever private key it happens to be holding around whatever key
|
|
865
|
+
* the verification activated — a person who starts a social login in one tab
|
|
866
|
+
* while a password step-up is outstanding in another would get a session signed
|
|
867
|
+
* with the wrong key, and `authenticate` would refuse every request it made.
|
|
868
|
+
*
|
|
869
|
+
* The key really is active by the time this is raised: the backend accepted the
|
|
870
|
+
* proof and this browser simply cannot prove it is the one that asked. Signing
|
|
871
|
+
* in again is the remedy, and it is a cheap one.
|
|
872
|
+
*/
|
|
873
|
+
declare class SessionPendingMismatchError extends UnauthorizedError {
|
|
874
|
+
readonly code = "SESSION_PENDING_MISMATCH";
|
|
875
|
+
constructor(data?: {
|
|
876
|
+
message?: string;
|
|
877
|
+
details?: Record<string, any>;
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
/**
|
|
881
|
+
* Session Pending Expired Error (401)
|
|
882
|
+
*
|
|
883
|
+
* Minted by the Next.js proxy: a second-factor verification succeeded and there
|
|
884
|
+
* is no pending cookie to seal a session from — ten minutes passed, or the
|
|
885
|
+
* browser that finished the step-up is not the browser that started it.
|
|
886
|
+
*
|
|
887
|
+
* Told apart from a mismatch on purpose. This one is the ordinary way a person
|
|
888
|
+
* meets the end of the window, and the message can say so; a mismatch is a
|
|
889
|
+
* cookie that is present and wrong, which is worth a different line in a log.
|
|
890
|
+
*/
|
|
891
|
+
declare class SessionPendingExpiredError extends UnauthorizedError {
|
|
892
|
+
readonly code = "SESSION_PENDING_EXPIRED";
|
|
893
|
+
constructor(data?: {
|
|
894
|
+
message?: string;
|
|
895
|
+
details?: Record<string, any>;
|
|
896
|
+
});
|
|
897
|
+
}
|
|
756
898
|
/**
|
|
757
899
|
* MFA Config Error (500)
|
|
758
900
|
*
|
|
@@ -864,6 +1006,20 @@ type authErrors_ReservedUsernameError = ReservedUsernameError;
|
|
|
864
1006
|
declare const authErrors_ReservedUsernameError: typeof ReservedUsernameError;
|
|
865
1007
|
type authErrors_RevokeAllLinkError = RevokeAllLinkError;
|
|
866
1008
|
declare const authErrors_RevokeAllLinkError: typeof RevokeAllLinkError;
|
|
1009
|
+
type authErrors_SessionBindingUnavailableError = SessionBindingUnavailableError;
|
|
1010
|
+
declare const authErrors_SessionBindingUnavailableError: typeof SessionBindingUnavailableError;
|
|
1011
|
+
type authErrors_SessionContextChangedError = SessionContextChangedError;
|
|
1012
|
+
declare const authErrors_SessionContextChangedError: typeof SessionContextChangedError;
|
|
1013
|
+
type authErrors_SessionPendingExpiredError = SessionPendingExpiredError;
|
|
1014
|
+
declare const authErrors_SessionPendingExpiredError: typeof SessionPendingExpiredError;
|
|
1015
|
+
type authErrors_SessionPendingMismatchError = SessionPendingMismatchError;
|
|
1016
|
+
declare const authErrors_SessionPendingMismatchError: typeof SessionPendingMismatchError;
|
|
1017
|
+
type authErrors_SessionRenewalRefusedError = SessionRenewalRefusedError;
|
|
1018
|
+
declare const authErrors_SessionRenewalRefusedError: typeof SessionRenewalRefusedError;
|
|
1019
|
+
type authErrors_SessionRenewalRequiredError = SessionRenewalRequiredError;
|
|
1020
|
+
declare const authErrors_SessionRenewalRequiredError: typeof SessionRenewalRequiredError;
|
|
1021
|
+
type authErrors_SessionResealFailedError = SessionResealFailedError;
|
|
1022
|
+
declare const authErrors_SessionResealFailedError: typeof SessionResealFailedError;
|
|
867
1023
|
type authErrors_StepUpRequiredError = StepUpRequiredError;
|
|
868
1024
|
declare const authErrors_StepUpRequiredError: typeof StepUpRequiredError;
|
|
869
1025
|
type authErrors_TokenExpiredError = TokenExpiredError;
|
|
@@ -877,7 +1033,7 @@ declare const authErrors_VerificationTokenPurposeMismatchError: typeof Verificat
|
|
|
877
1033
|
type authErrors_VerificationTokenTargetMismatchError = VerificationTokenTargetMismatchError;
|
|
878
1034
|
declare const authErrors_VerificationTokenTargetMismatchError: typeof VerificationTokenTargetMismatchError;
|
|
879
1035
|
declare namespace authErrors {
|
|
880
|
-
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_MfaAlreadyEnrolledError as MfaAlreadyEnrolledError, authErrors_MfaConfigError as MfaConfigError, authErrors_MfaNotEnrolledError as MfaNotEnrolledError, authErrors_MfaVerificationFailedError as MfaVerificationFailedError, 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_RevokeAllLinkError as RevokeAllLinkError, authErrors_StepUpRequiredError as StepUpRequiredError, authErrors_TokenExpiredError as TokenExpiredError, authErrors_UnverifiedEmailLinkError as UnverifiedEmailLinkError, authErrors_UsernameAlreadyTakenError as UsernameAlreadyTakenError, authErrors_VerificationTokenPurposeMismatchError as VerificationTokenPurposeMismatchError, authErrors_VerificationTokenTargetMismatchError as VerificationTokenTargetMismatchError };
|
|
1036
|
+
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_MfaAlreadyEnrolledError as MfaAlreadyEnrolledError, authErrors_MfaConfigError as MfaConfigError, authErrors_MfaNotEnrolledError as MfaNotEnrolledError, authErrors_MfaVerificationFailedError as MfaVerificationFailedError, 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_RevokeAllLinkError as RevokeAllLinkError, authErrors_SessionBindingUnavailableError as SessionBindingUnavailableError, authErrors_SessionContextChangedError as SessionContextChangedError, authErrors_SessionPendingExpiredError as SessionPendingExpiredError, authErrors_SessionPendingMismatchError as SessionPendingMismatchError, authErrors_SessionRenewalRefusedError as SessionRenewalRefusedError, authErrors_SessionRenewalRequiredError as SessionRenewalRequiredError, authErrors_SessionResealFailedError as SessionResealFailedError, authErrors_StepUpRequiredError as StepUpRequiredError, authErrors_TokenExpiredError as TokenExpiredError, authErrors_UnverifiedEmailLinkError as UnverifiedEmailLinkError, authErrors_UsernameAlreadyTakenError as UsernameAlreadyTakenError, authErrors_VerificationTokenPurposeMismatchError as VerificationTokenPurposeMismatchError, authErrors_VerificationTokenTargetMismatchError as VerificationTokenTargetMismatchError };
|
|
881
1037
|
}
|
|
882
1038
|
|
|
883
1039
|
/**
|
|
@@ -886,4 +1042,4 @@ declare namespace authErrors {
|
|
|
886
1042
|
|
|
887
1043
|
declare const authErrorRegistry: ErrorRegistry;
|
|
888
1044
|
|
|
889
|
-
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, MfaAlreadyEnrolledError, MfaConfigError, MfaNotEnrolledError, MfaVerificationFailedError, NativeSignInUnsupportedError, NonceKeyBindingError, OAuth2AuthorizeRedirectError, OAuth2GrantNotFoundError, OAuth2RedirectUriMismatchError, OAuth2UnknownClientError, PasskeyAlreadyRegisteredError, PasskeyChallengeError, PasskeyConfigError, PasskeyNotFoundError, PasskeyVerificationError, PasswordResetLinkError, PasswordResetSessionError, RecentAuthenticationRequiredError, RegistrationRejectedError, ReservedUsernameError, RevokeAllLinkError, StepUpRequiredError, TokenExpiredError, UnverifiedEmailLinkError, UsernameAlreadyTakenError, VerificationTokenPurposeMismatchError, VerificationTokenTargetMismatchError, authErrorRegistry };
|
|
1045
|
+
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, MfaAlreadyEnrolledError, MfaConfigError, MfaNotEnrolledError, MfaVerificationFailedError, NativeSignInUnsupportedError, NonceKeyBindingError, OAuth2AuthorizeRedirectError, OAuth2GrantNotFoundError, OAuth2RedirectUriMismatchError, OAuth2UnknownClientError, PasskeyAlreadyRegisteredError, PasskeyChallengeError, PasskeyConfigError, PasskeyNotFoundError, PasskeyVerificationError, PasswordResetLinkError, PasswordResetSessionError, RecentAuthenticationRequiredError, RegistrationRejectedError, ReservedUsernameError, RevokeAllLinkError, SessionBindingUnavailableError, SessionContextChangedError, SessionPendingExpiredError, SessionPendingMismatchError, SessionRenewalRefusedError, SessionRenewalRequiredError, SessionResealFailedError, StepUpRequiredError, TokenExpiredError, UnverifiedEmailLinkError, UsernameAlreadyTakenError, VerificationTokenPurposeMismatchError, VerificationTokenTargetMismatchError, authErrorRegistry };
|
package/dist/errors.js
CHANGED
|
@@ -56,6 +56,13 @@ __export(auth_errors_exports, {
|
|
|
56
56
|
RegistrationRejectedError: () => RegistrationRejectedError,
|
|
57
57
|
ReservedUsernameError: () => ReservedUsernameError,
|
|
58
58
|
RevokeAllLinkError: () => RevokeAllLinkError,
|
|
59
|
+
SessionBindingUnavailableError: () => SessionBindingUnavailableError,
|
|
60
|
+
SessionContextChangedError: () => SessionContextChangedError,
|
|
61
|
+
SessionPendingExpiredError: () => SessionPendingExpiredError,
|
|
62
|
+
SessionPendingMismatchError: () => SessionPendingMismatchError,
|
|
63
|
+
SessionRenewalRefusedError: () => SessionRenewalRefusedError,
|
|
64
|
+
SessionRenewalRequiredError: () => SessionRenewalRequiredError,
|
|
65
|
+
SessionResealFailedError: () => SessionResealFailedError,
|
|
59
66
|
StepUpRequiredError: () => StepUpRequiredError,
|
|
60
67
|
TokenExpiredError: () => TokenExpiredError,
|
|
61
68
|
UnverifiedEmailLinkError: () => UnverifiedEmailLinkError,
|
|
@@ -64,12 +71,14 @@ __export(auth_errors_exports, {
|
|
|
64
71
|
VerificationTokenTargetMismatchError: () => VerificationTokenTargetMismatchError
|
|
65
72
|
});
|
|
66
73
|
import {
|
|
74
|
+
BadRequestError,
|
|
67
75
|
ValidationError,
|
|
68
76
|
UnauthorizedError,
|
|
69
77
|
ForbiddenError,
|
|
70
78
|
ConflictError,
|
|
71
79
|
NotFoundError,
|
|
72
|
-
HttpError
|
|
80
|
+
HttpError,
|
|
81
|
+
InternalServerError
|
|
73
82
|
} from "@spfn/core/errors";
|
|
74
83
|
var InvalidCredentialsError = class extends UnauthorizedError {
|
|
75
84
|
constructor(data = {}) {
|
|
@@ -418,6 +427,56 @@ var LastRecoveryCredentialError = class extends ConflictError {
|
|
|
418
427
|
this.name = "LastRecoveryCredentialError";
|
|
419
428
|
}
|
|
420
429
|
};
|
|
430
|
+
var SessionRenewalRequiredError = class extends UnauthorizedError {
|
|
431
|
+
code = "SESSION_RENEWAL_REQUIRED";
|
|
432
|
+
constructor(data = {}) {
|
|
433
|
+
super({
|
|
434
|
+
message: data.message || "This session needs renewing. Confirm with your passkey to continue.",
|
|
435
|
+
details: data.details
|
|
436
|
+
});
|
|
437
|
+
this.name = "SessionRenewalRequiredError";
|
|
438
|
+
}
|
|
439
|
+
};
|
|
440
|
+
var SessionRenewalRefusedError = class extends UnauthorizedError {
|
|
441
|
+
code = "SESSION_RENEWAL_REFUSED";
|
|
442
|
+
constructor(data = {}) {
|
|
443
|
+
super({
|
|
444
|
+
message: data.message || "This session cannot be renewed. Sign in again.",
|
|
445
|
+
details: data.details
|
|
446
|
+
});
|
|
447
|
+
this.name = "SessionRenewalRefusedError";
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
var SessionContextChangedError = class extends UnauthorizedError {
|
|
451
|
+
code = "SESSION_CONTEXT_CHANGED";
|
|
452
|
+
constructor(data = {}) {
|
|
453
|
+
super({
|
|
454
|
+
message: data.message || "This session was started in a different browser. Sign in again.",
|
|
455
|
+
details: data.details
|
|
456
|
+
});
|
|
457
|
+
this.name = "SessionContextChangedError";
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
var SessionBindingUnavailableError = class extends BadRequestError {
|
|
461
|
+
code = "SESSION_BINDING_UNAVAILABLE";
|
|
462
|
+
constructor(data = {}) {
|
|
463
|
+
super({
|
|
464
|
+
message: data.message || "Session binding needs a deployment where the backend can recognise the Next.js proxy. Configure proxy-guard (SPFN_PROXY_SIGNATURE_KEYS) and try again.",
|
|
465
|
+
details: data.details
|
|
466
|
+
});
|
|
467
|
+
this.name = "SessionBindingUnavailableError";
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
var SessionResealFailedError = class extends InternalServerError {
|
|
471
|
+
code = "SESSION_RESEAL_FAILED";
|
|
472
|
+
constructor(data = {}) {
|
|
473
|
+
super({
|
|
474
|
+
message: data.message || "The setting was changed but this session could not be updated. Sign in again.",
|
|
475
|
+
details: data.details
|
|
476
|
+
});
|
|
477
|
+
this.name = "SessionResealFailedError";
|
|
478
|
+
}
|
|
479
|
+
};
|
|
421
480
|
var PasskeyConfigError = class extends HttpError {
|
|
422
481
|
constructor(data) {
|
|
423
482
|
super({
|
|
@@ -503,6 +562,26 @@ var StepUpRequiredError = class extends ForbiddenError {
|
|
|
503
562
|
this.name = "StepUpRequiredError";
|
|
504
563
|
}
|
|
505
564
|
};
|
|
565
|
+
var SessionPendingMismatchError = class extends UnauthorizedError {
|
|
566
|
+
code = "SESSION_PENDING_MISMATCH";
|
|
567
|
+
constructor(data = {}) {
|
|
568
|
+
super({
|
|
569
|
+
message: data.message || "This browser did not start that sign-in. Sign in again.",
|
|
570
|
+
details: data.details
|
|
571
|
+
});
|
|
572
|
+
this.name = "SessionPendingMismatchError";
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
var SessionPendingExpiredError = class extends UnauthorizedError {
|
|
576
|
+
code = "SESSION_PENDING_EXPIRED";
|
|
577
|
+
constructor(data = {}) {
|
|
578
|
+
super({
|
|
579
|
+
message: data.message || "That sign-in took too long. Sign in again.",
|
|
580
|
+
details: data.details
|
|
581
|
+
});
|
|
582
|
+
this.name = "SessionPendingExpiredError";
|
|
583
|
+
}
|
|
584
|
+
};
|
|
506
585
|
var MfaConfigError = class extends HttpError {
|
|
507
586
|
constructor(data) {
|
|
508
587
|
super({
|
|
@@ -560,6 +639,11 @@ authErrorRegistry.append([
|
|
|
560
639
|
RecentAuthenticationRequiredError,
|
|
561
640
|
LastRecoveryCredentialError,
|
|
562
641
|
PasskeyConfigError,
|
|
642
|
+
SessionBindingUnavailableError,
|
|
643
|
+
SessionRenewalRequiredError,
|
|
644
|
+
SessionRenewalRefusedError,
|
|
645
|
+
SessionContextChangedError,
|
|
646
|
+
SessionResealFailedError,
|
|
563
647
|
OAuth2UnknownClientError,
|
|
564
648
|
OAuth2RedirectUriMismatchError,
|
|
565
649
|
OAuth2AuthorizeRedirectError,
|
|
@@ -568,7 +652,9 @@ authErrorRegistry.append([
|
|
|
568
652
|
MfaNotEnrolledError,
|
|
569
653
|
MfaVerificationFailedError,
|
|
570
654
|
StepUpRequiredError,
|
|
571
|
-
MfaConfigError
|
|
655
|
+
MfaConfigError,
|
|
656
|
+
SessionPendingMismatchError,
|
|
657
|
+
SessionPendingExpiredError
|
|
572
658
|
]);
|
|
573
659
|
export {
|
|
574
660
|
AccountAlreadyExistsError,
|
|
@@ -618,6 +704,13 @@ export {
|
|
|
618
704
|
RegistrationRejectedError,
|
|
619
705
|
ReservedUsernameError,
|
|
620
706
|
RevokeAllLinkError,
|
|
707
|
+
SessionBindingUnavailableError,
|
|
708
|
+
SessionContextChangedError,
|
|
709
|
+
SessionPendingExpiredError,
|
|
710
|
+
SessionPendingMismatchError,
|
|
711
|
+
SessionRenewalRefusedError,
|
|
712
|
+
SessionRenewalRequiredError,
|
|
713
|
+
SessionResealFailedError,
|
|
621
714
|
StepUpRequiredError,
|
|
622
715
|
TokenExpiredError,
|
|
623
716
|
UnverifiedEmailLinkError,
|