@spfn/auth 0.3.0-beta.2 → 0.3.0-beta.20
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 +1382 -23
- package/dist/client-proof.d.ts +45 -15
- package/dist/client-proof.js +198 -4
- package/dist/client-proof.js.map +1 -1
- package/dist/client.d.ts +92 -1
- package/dist/client.js +58 -0
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +302 -0
- package/dist/config.js +134 -4
- package/dist/config.js.map +1 -1
- package/dist/errors.d.ts +370 -3
- package/dist/errors.js +245 -2
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +185 -2
- package/dist/index.js +256 -2
- package/dist/index.js.map +1 -1
- package/dist/machine-principals-BD4tnASp.d.ts +2739 -0
- package/dist/nextjs/api.js +350 -12
- package/dist/nextjs/api.js.map +1 -1
- package/dist/nextjs/client.d.ts +28 -1
- package/dist/nextjs/client.js +24 -3
- package/dist/nextjs/client.js.map +1 -1
- package/dist/nextjs/server.d.ts +173 -3
- package/dist/nextjs/server.js +372 -10
- package/dist/nextjs/server.js.map +1 -1
- package/dist/server.d.ts +3761 -414
- package/dist/server.js +5865 -1043
- package/dist/server.js.map +1 -1
- package/dist/{session-DTHahDQ9.d.ts → session-Dfwu5g2W.d.ts} +28 -1
- package/migrations/20260810112144_colorful_tomorrow_man/migration.sql +18 -0
- package/migrations/20260810112144_colorful_tomorrow_man/snapshot.json +3576 -0
- package/migrations/20260901091716_fine_arclight/migration.sql +21 -0
- package/migrations/20260901091716_fine_arclight/snapshot.json +3849 -0
- package/migrations/20260906155957_natural_moonstone/migration.sql +33 -0
- package/migrations/20260906155957_natural_moonstone/snapshot.json +4275 -0
- package/migrations/20260907020904_giant_eternals/migration.sql +21 -0
- package/migrations/20260907020904_giant_eternals/snapshot.json +4561 -0
- package/migrations/20260907044807_eminent_angel/migration.sql +2 -0
- package/migrations/20260907044807_eminent_angel/snapshot.json +4561 -0
- package/migrations/20260918083158_foamy_roughhouse/migration.sql +55 -0
- package/migrations/20260918083158_foamy_roughhouse/snapshot.json +5271 -0
- package/package.json +9 -6
- package/dist/authenticate-55LeXHqZ.d.ts +0 -1447
package/dist/config.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
envString,
|
|
8
8
|
envNumber,
|
|
9
9
|
envBoolean,
|
|
10
|
+
envEnum,
|
|
10
11
|
createSecureSecretParser,
|
|
11
12
|
createPasswordParser
|
|
12
13
|
} from "@spfn/core/env";
|
|
@@ -76,6 +77,15 @@ var authEnvSchema = defineEnvSchema({
|
|
|
76
77
|
examples: [true, false]
|
|
77
78
|
})
|
|
78
79
|
},
|
|
80
|
+
SPFN_AUTH_CSRF: {
|
|
81
|
+
...envString({
|
|
82
|
+
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.',
|
|
83
|
+
required: false,
|
|
84
|
+
nextjs: true,
|
|
85
|
+
// The check runs in the Next.js proxy
|
|
86
|
+
examples: ["enforce", "warn", "off"]
|
|
87
|
+
})
|
|
88
|
+
},
|
|
79
89
|
SPFN_AUTH_BCRYPT_SALT_ROUNDS: {
|
|
80
90
|
...envNumber({
|
|
81
91
|
description: "Bcrypt salt rounds (cost factor, higher = more secure but slower)",
|
|
@@ -201,6 +211,119 @@ var authEnvSchema = defineEnvSchema({
|
|
|
201
211
|
})
|
|
202
212
|
},
|
|
203
213
|
// ============================================================================
|
|
214
|
+
// Verified-email signup
|
|
215
|
+
// ============================================================================
|
|
216
|
+
SPFN_AUTH_SIGNUP_LINK_TTL_MINUTES: {
|
|
217
|
+
...envNumber({
|
|
218
|
+
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.",
|
|
219
|
+
default: 30,
|
|
220
|
+
required: false,
|
|
221
|
+
examples: [15, 30, 60]
|
|
222
|
+
})
|
|
223
|
+
},
|
|
224
|
+
SPFN_AUTH_SIGNUP_SETUP_TTL_MINUTES: {
|
|
225
|
+
...envNumber({
|
|
226
|
+
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.",
|
|
227
|
+
default: 15,
|
|
228
|
+
required: false,
|
|
229
|
+
examples: [10, 15, 30]
|
|
230
|
+
})
|
|
231
|
+
},
|
|
232
|
+
SPFN_AUTH_SIGNUP_CONFIRM_PATH: {
|
|
233
|
+
...envString({
|
|
234
|
+
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.",
|
|
235
|
+
default: "/signup/confirm",
|
|
236
|
+
required: false,
|
|
237
|
+
examples: ["/signup/confirm", "/auth/confirm", "/join/verify"]
|
|
238
|
+
})
|
|
239
|
+
},
|
|
240
|
+
// ============================================================================
|
|
241
|
+
// Password reset (verified email)
|
|
242
|
+
// ============================================================================
|
|
243
|
+
SPFN_AUTH_PASSWORD_RESET_LINK_TTL_MINUTES: {
|
|
244
|
+
...envNumber({
|
|
245
|
+
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.",
|
|
246
|
+
default: 30,
|
|
247
|
+
required: false,
|
|
248
|
+
examples: [15, 30, 60]
|
|
249
|
+
})
|
|
250
|
+
},
|
|
251
|
+
SPFN_AUTH_PASSWORD_RESET_SETUP_TTL_MINUTES: {
|
|
252
|
+
...envNumber({
|
|
253
|
+
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.",
|
|
254
|
+
default: 15,
|
|
255
|
+
required: false,
|
|
256
|
+
examples: [10, 15, 30]
|
|
257
|
+
})
|
|
258
|
+
},
|
|
259
|
+
SPFN_AUTH_PASSWORD_RESET_CONFIRM_PATH: {
|
|
260
|
+
...envString({
|
|
261
|
+
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.",
|
|
262
|
+
default: "/password/reset",
|
|
263
|
+
required: false,
|
|
264
|
+
examples: ["/password/reset", "/auth/reset", "/forgot/new-password"]
|
|
265
|
+
})
|
|
266
|
+
},
|
|
267
|
+
// ============================================================================
|
|
268
|
+
// Link mail delivery
|
|
269
|
+
// ============================================================================
|
|
270
|
+
SPFN_AUTH_LINK_MAIL_DELIVERY: {
|
|
271
|
+
...envEnum(["auto", "inline", "queued"], {
|
|
272
|
+
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.",
|
|
273
|
+
default: "auto",
|
|
274
|
+
required: false,
|
|
275
|
+
examples: ["auto", "inline", "queued"]
|
|
276
|
+
})
|
|
277
|
+
},
|
|
278
|
+
// ============================================================================
|
|
279
|
+
// Passkeys (WebAuthn)
|
|
280
|
+
// ============================================================================
|
|
281
|
+
SPFN_AUTH_PASSKEY_RP_ID: {
|
|
282
|
+
...envString({
|
|
283
|
+
description: "Domain passkeys are bound to \u2014 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.",
|
|
284
|
+
required: false,
|
|
285
|
+
examples: ["example.com", "app.example.com", "localhost"]
|
|
286
|
+
})
|
|
287
|
+
},
|
|
288
|
+
SPFN_AUTH_PASSKEY_RP_NAME: {
|
|
289
|
+
...envString({
|
|
290
|
+
description: "Name the authenticator's own prompt shows the user. Defaults to the relying party ID.",
|
|
291
|
+
required: false,
|
|
292
|
+
examples: ["Acme", "Acme Staging"]
|
|
293
|
+
})
|
|
294
|
+
},
|
|
295
|
+
SPFN_AUTH_PASSKEY_ORIGINS: {
|
|
296
|
+
...envString({
|
|
297
|
+
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.",
|
|
298
|
+
required: false,
|
|
299
|
+
examples: ["https://app.example.com", "https://app.example.com,https://admin.example.com", "http://localhost:3000"]
|
|
300
|
+
})
|
|
301
|
+
},
|
|
302
|
+
SPFN_AUTH_PASSKEY_USER_VERIFICATION: {
|
|
303
|
+
...envString({
|
|
304
|
+
description: "How hard the authenticator must prove the person is present: 'preferred' or 'required'. 'discouraged' is refused at boot \u2014 a passkey is the whole credential here, so an assertion that skipped user verification would sign someone in on an unlocked device alone.",
|
|
305
|
+
default: "preferred",
|
|
306
|
+
required: false,
|
|
307
|
+
examples: ["preferred", "required"]
|
|
308
|
+
})
|
|
309
|
+
},
|
|
310
|
+
SPFN_AUTH_PASSKEY_CHALLENGE_TTL_SECONDS: {
|
|
311
|
+
...envNumber({
|
|
312
|
+
description: "How long the challenge minted by a passkey options call stays presentable. One ceremony at the authenticator, not an abandoned tab.",
|
|
313
|
+
default: 300,
|
|
314
|
+
required: false,
|
|
315
|
+
examples: [120, 300, 600]
|
|
316
|
+
})
|
|
317
|
+
},
|
|
318
|
+
SPFN_AUTH_PASSKEY_RECENT_AUTH_MINUTES: {
|
|
319
|
+
...envNumber({
|
|
320
|
+
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.",
|
|
321
|
+
default: 10,
|
|
322
|
+
required: false,
|
|
323
|
+
examples: [5, 10, 30]
|
|
324
|
+
})
|
|
325
|
+
},
|
|
326
|
+
// ============================================================================
|
|
204
327
|
// API Configuration
|
|
205
328
|
// ============================================================================
|
|
206
329
|
SPFN_API_URL: {
|
|
@@ -276,7 +399,7 @@ var authEnvSchema = defineEnvSchema({
|
|
|
276
399
|
},
|
|
277
400
|
SPFN_AUTH_GOOGLE_REDIRECT_URI: {
|
|
278
401
|
...envString({
|
|
279
|
-
description: "Google OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/google/callback
|
|
402
|
+
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 \u2014 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.",
|
|
280
403
|
required: false,
|
|
281
404
|
examples: [
|
|
282
405
|
"https://app.example.com/_auth/oauth/google/callback",
|
|
@@ -319,7 +442,7 @@ var authEnvSchema = defineEnvSchema({
|
|
|
319
442
|
},
|
|
320
443
|
SPFN_AUTH_KAKAO_REDIRECT_URI: {
|
|
321
444
|
...envString({
|
|
322
|
-
description: "Kakao OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/kakao/callback.",
|
|
445
|
+
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 \u2014 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.",
|
|
323
446
|
required: false,
|
|
324
447
|
examples: ["https://app.example.com/_auth/oauth/kakao/callback"]
|
|
325
448
|
})
|
|
@@ -344,7 +467,7 @@ var authEnvSchema = defineEnvSchema({
|
|
|
344
467
|
},
|
|
345
468
|
SPFN_AUTH_NAVER_REDIRECT_URI: {
|
|
346
469
|
...envString({
|
|
347
|
-
description: "Naver OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/naver/callback.",
|
|
470
|
+
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 \u2014 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.",
|
|
348
471
|
required: false,
|
|
349
472
|
examples: ["https://app.example.com/_auth/oauth/naver/callback"]
|
|
350
473
|
})
|
|
@@ -376,11 +499,18 @@ var authEnvSchema = defineEnvSchema({
|
|
|
376
499
|
},
|
|
377
500
|
SPFN_AUTH_GITHUB_REDIRECT_URI: {
|
|
378
501
|
...envString({
|
|
379
|
-
description: "GitHub OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/github/callback.",
|
|
502
|
+
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 \u2014 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.",
|
|
380
503
|
required: false,
|
|
381
504
|
examples: ["https://app.example.com/_auth/oauth/github/callback"]
|
|
382
505
|
})
|
|
383
506
|
},
|
|
507
|
+
SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK: {
|
|
508
|
+
...envString({
|
|
509
|
+
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 \u2014 unset, "on" and anything else all run it.',
|
|
510
|
+
required: false,
|
|
511
|
+
examples: ["off"]
|
|
512
|
+
})
|
|
513
|
+
},
|
|
384
514
|
// ============================================================================
|
|
385
515
|
// Native Social Login (mobile/web id_token verification)
|
|
386
516
|
//
|
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 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_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 // 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 callback must return to the web app origin that set the oauth_csrf cookie (the app rewrites /_auth/:path* to the API). Set this explicitly only when the callback should hit a different host (e.g. the API host for the direct oauthStart flow).',\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.',\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.',\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.',\n required: false,\n examples: ['https://app.example.com/_auth/oauth/github/callback'],\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 // 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 // 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":[]}
|