@payez/next-mvp 4.0.46 → 4.0.47
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/dist/api/auth-handler.d.ts +0 -2
- package/dist/api/auth-handler.js +1 -1
- package/dist/api-handlers/auth/refresh.d.ts +4 -6
- package/dist/api-handlers/auth/refresh.js +5 -7
- package/dist/api-handlers/auth/signout.d.ts +6 -15
- package/dist/api-handlers/auth/signout.js +9 -16
- package/dist/api-handlers/auth/update-session.d.ts +6 -15
- package/dist/api-handlers/auth/update-session.js +7 -15
- package/dist/api-handlers/auth/verify-code.d.ts +6 -15
- package/dist/api-handlers/auth/verify-code.js +7 -15
- package/dist/api-handlers/session/viability.js +2 -2
- package/dist/auth/better-auth.d.ts +3 -19
- package/dist/auth/better-auth.js +7 -13
- package/dist/client/better-auth-client.d.ts +7 -8
- package/dist/client/better-auth-client.js +3 -4
- package/dist/lib/auth-secret.d.ts +17 -0
- package/dist/lib/{nextauth-secret.js → auth-secret.js} +31 -15
- package/dist/lib/demo-mode.js +3 -1
- package/dist/lib/idp-client-config.d.ts +6 -2
- package/dist/lib/idp-client-config.js +35 -21
- package/dist/lib/secret-validation.d.ts +1 -1
- package/dist/lib/secret-validation.js +2 -2
- package/dist/lib/startup-init.d.ts +3 -3
- package/dist/lib/startup-init.js +23 -18
- package/dist/lib/test-aware-get-token.js +2 -51
- package/dist/routes/account/masked-info.d.ts +1 -1
- package/dist/routes/account/masked-info.js +1 -1
- package/dist/routes/account/send-code.d.ts +1 -1
- package/dist/routes/account/send-code.js +1 -1
- package/dist/routes/account/verify-email.d.ts +1 -1
- package/dist/routes/account/verify-email.js +1 -1
- package/dist/routes/account/verify-sms.d.ts +1 -1
- package/dist/routes/account/verify-sms.js +1 -1
- package/dist/routes/auth/refresh.js +3 -8
- package/dist/server/auth.d.ts +4 -7
- package/dist/server/auth.js +3 -6
- package/dist/server/decode-session.js +2 -2
- package/dist/vibe/hooks/index.d.ts +1 -1
- package/package.json +888 -893
- package/src/api/auth-handler.ts +0 -4
- package/src/api-handlers/auth/refresh.ts +5 -8
- package/src/api-handlers/auth/signout.ts +9 -21
- package/src/api-handlers/auth/update-session.ts +7 -20
- package/src/api-handlers/auth/verify-code.ts +7 -20
- package/src/api-handlers/session/viability.ts +2 -2
- package/src/auth/better-auth.ts +7 -32
- package/src/client/better-auth-client.ts +3 -4
- package/src/lib/{nextauth-secret.ts → auth-secret.ts} +32 -16
- package/src/lib/demo-mode.ts +5 -1
- package/src/lib/idp-client-config.ts +42 -22
- package/src/lib/secret-validation.ts +1 -1
- package/src/lib/startup-init.ts +23 -18
- package/src/lib/test-aware-get-token.ts +2 -51
- package/src/routes/account/masked-info.ts +1 -1
- package/src/routes/account/send-code.ts +1 -1
- package/src/routes/account/verify-email.ts +1 -1
- package/src/routes/account/verify-sms.ts +1 -1
- package/src/routes/auth/refresh.ts +3 -8
- package/src/server/auth.ts +3 -6
- package/src/server/decode-session.ts +2 -2
- package/dist/lib/nextauth-secret.d.ts +0 -10
|
@@ -1,24 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.resolveAuthSecret = resolveAuthSecret;
|
|
4
4
|
require("server-only");
|
|
5
5
|
const secret_validation_1 = require("./secret-validation");
|
|
6
6
|
const crypto_1 = require("crypto");
|
|
7
7
|
let cachedSecret = null;
|
|
8
8
|
let lastFetchedAt = 0;
|
|
9
9
|
/**
|
|
10
|
-
* Resolve the
|
|
10
|
+
* Resolve the Better Auth signing secret (server-only).
|
|
11
11
|
*
|
|
12
12
|
* Priority:
|
|
13
|
-
* 1) Use process.env.
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* 1) Use process.env.BETTER_AUTH_SECRET (preferred) or NEXTAUTH_SECRET (legacy)
|
|
14
|
+
* if present — allows overrides/production via env.
|
|
15
|
+
* 2) Fetch from IDP broker endpoint — IDP handles all Key Vault/signing.
|
|
16
|
+
* 3) Cache result in-memory and set process.env.BETTER_AUTH_SECRET for
|
|
17
|
+
* subsequent calls.
|
|
18
|
+
*
|
|
19
|
+
* NOTE on naming: this secret is the cryptographic key Better Auth uses to
|
|
20
|
+
* sign session JWTs. The IDP backend still names the broker endpoint and
|
|
21
|
+
* response field with the legacy "next-auth" / "nextAuthSecret" names; we
|
|
22
|
+
* read both new and legacy on the wire during the migration window.
|
|
16
23
|
*/
|
|
17
|
-
async function
|
|
18
|
-
// Check if already in environment
|
|
19
|
-
|
|
24
|
+
async function resolveAuthSecret() {
|
|
25
|
+
// Check if already in environment (prefer new name, fall back to legacy)
|
|
26
|
+
const envSecret = (process.env.BETTER_AUTH_SECRET && process.env.BETTER_AUTH_SECRET.trim() !== ''
|
|
27
|
+
? process.env.BETTER_AUTH_SECRET
|
|
28
|
+
: undefined) ||
|
|
29
|
+
(process.env.NEXTAUTH_SECRET && process.env.NEXTAUTH_SECRET.trim() !== ''
|
|
30
|
+
? process.env.NEXTAUTH_SECRET
|
|
31
|
+
: undefined);
|
|
32
|
+
if (envSecret) {
|
|
20
33
|
// Silent - already configured
|
|
21
|
-
return
|
|
34
|
+
return envSecret;
|
|
22
35
|
}
|
|
23
36
|
// Check if cached and fresh (within 5 minutes)
|
|
24
37
|
if (cachedSecret && Date.now() - lastFetchedAt < 5 * 60 * 1000) {
|
|
@@ -64,7 +77,8 @@ async function resolveNextAuthSecret() {
|
|
|
64
77
|
if (!client_assertion || typeof client_assertion !== 'string') {
|
|
65
78
|
throw new Error('IDP did not return a valid signed client assertion');
|
|
66
79
|
}
|
|
67
|
-
// Step 2: Use the signed assertion to fetch the
|
|
80
|
+
// Step 2: Use the signed assertion to fetch the auth secret
|
|
81
|
+
// (Endpoint is still served at /next-auth/secret on the IDP — legacy path.)
|
|
68
82
|
const proxyUrl = new URL(`${base.replace(/\/$/, '')}/api/ExternalAuth/next-auth/secret`);
|
|
69
83
|
const proxyResp = await fetch(proxyUrl.toString(), {
|
|
70
84
|
method: 'POST',
|
|
@@ -83,18 +97,20 @@ async function resolveNextAuthSecret() {
|
|
|
83
97
|
}
|
|
84
98
|
const proxyBody = await proxyResp.json().catch(() => ({}));
|
|
85
99
|
const secret = (proxyBody?.data?.secret ?? proxyBody?.secret);
|
|
86
|
-
const configuration = (proxyBody?.data?.configuration ?? proxyBody?.configuration);
|
|
87
100
|
// Configuration is available but we don't log it verbosely
|
|
88
101
|
if (!secret || typeof secret !== 'string') {
|
|
89
|
-
throw new Error('Proxy did not return a valid
|
|
102
|
+
throw new Error('Proxy did not return a valid auth secret');
|
|
90
103
|
}
|
|
91
|
-
const validation = (0, secret_validation_1.
|
|
104
|
+
const validation = (0, secret_validation_1.validateAuthSecret)(secret);
|
|
92
105
|
if (!validation.valid) {
|
|
93
|
-
throw new Error(`Fetched
|
|
106
|
+
throw new Error(`Fetched auth secret failed validation: ${validation.reason}`);
|
|
94
107
|
}
|
|
95
108
|
cachedSecret = secret;
|
|
96
109
|
lastFetchedAt = Date.now();
|
|
110
|
+
process.env.BETTER_AUTH_SECRET = secret;
|
|
111
|
+
// Also set legacy name during transition so any consumer still reading
|
|
112
|
+
// process.env.NEXTAUTH_SECRET keeps working until they upgrade.
|
|
97
113
|
process.env.NEXTAUTH_SECRET = secret;
|
|
98
|
-
console.log('[
|
|
114
|
+
console.log('[AUTH-SECRET] Resolved from IDP (length:', secret.length + ')');
|
|
99
115
|
return secret;
|
|
100
116
|
}
|
package/dist/lib/demo-mode.js
CHANGED
|
@@ -12,5 +12,7 @@ function isDemoMode() {
|
|
|
12
12
|
function isAuthConfigured() {
|
|
13
13
|
if (isDemoMode())
|
|
14
14
|
return false;
|
|
15
|
-
return !!(process.env.
|
|
15
|
+
return !!(process.env.BETTER_AUTH_SECRET ||
|
|
16
|
+
process.env.NEXTAUTH_SECRET ||
|
|
17
|
+
(process.env.NEXT_CLIENT_ID && process.env.NEXT_CLIENT_PRIVATE_KEY_PEM));
|
|
16
18
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* - OAuth provider credentials (from Key Vault)
|
|
6
6
|
* - 2FA/MFA settings
|
|
7
7
|
* - Session configuration
|
|
8
|
-
* -
|
|
8
|
+
* - Better Auth signing secret
|
|
9
9
|
* - Branding
|
|
10
10
|
*
|
|
11
11
|
* CACHING STRATEGY:
|
|
@@ -47,7 +47,11 @@ export interface BrandingConfig {
|
|
|
47
47
|
export interface IDPClientConfig {
|
|
48
48
|
clientId: string;
|
|
49
49
|
clientSlug: string;
|
|
50
|
-
|
|
50
|
+
/**
|
|
51
|
+
* Cryptographic secret used by Better Auth to sign session JWTs.
|
|
52
|
+
* Historically named "nextAuthSecret" — kept under the new name now.
|
|
53
|
+
*/
|
|
54
|
+
authSecret: string;
|
|
51
55
|
configCacheTtlSeconds: number;
|
|
52
56
|
oauthProviders: OAuthProviderConfig[];
|
|
53
57
|
authSettings: AuthSettings;
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - OAuth provider credentials (from Key Vault)
|
|
7
7
|
* - 2FA/MFA settings
|
|
8
8
|
* - Session configuration
|
|
9
|
-
* -
|
|
9
|
+
* - Better Auth signing secret
|
|
10
10
|
* - Branding
|
|
11
11
|
*
|
|
12
12
|
* CACHING STRATEGY:
|
|
@@ -115,13 +115,14 @@ async function getIDPClientConfig(forceRefresh = false) {
|
|
|
115
115
|
// Restore to in-memory cache
|
|
116
116
|
cachedConfig = redisConfig;
|
|
117
117
|
cacheExpiry = Date.now() + ((redisConfig.configCacheTtlSeconds || 300) * 1000);
|
|
118
|
-
// Set
|
|
119
|
-
|
|
120
|
-
|
|
118
|
+
// Set BETTER_AUTH_SECRET from cached config (also set legacy
|
|
119
|
+
// NEXTAUTH_SECRET during the rename transition).
|
|
120
|
+
if (redisConfig.authSecret) {
|
|
121
|
+
process.env.BETTER_AUTH_SECRET = redisConfig.authSecret;
|
|
122
|
+
process.env.NEXTAUTH_SECRET = redisConfig.authSecret;
|
|
121
123
|
}
|
|
122
|
-
// Set IDENTITY_CLIENT_BASE_EXTERNAL_URL from cached config
|
|
123
|
-
//
|
|
124
|
-
// Only set if not already defined (allows deployment override for beta/staging)
|
|
124
|
+
// Set IDENTITY_CLIENT_BASE_EXTERNAL_URL from cached config.
|
|
125
|
+
// Only set if not already defined (allows deployment override for beta/staging).
|
|
125
126
|
if (redisConfig.baseClientUrl && !process.env.IDENTITY_CLIENT_BASE_EXTERNAL_URL) {
|
|
126
127
|
process.env.IDENTITY_CLIENT_BASE_EXTERNAL_URL = redisConfig.baseClientUrl;
|
|
127
128
|
}
|
|
@@ -149,16 +150,17 @@ async function getIDPClientConfig(forceRefresh = false) {
|
|
|
149
150
|
cacheExpiry = Date.now() + ((config.configCacheTtlSeconds || 300) * 1000);
|
|
150
151
|
// Store in Redis for persistence across module reloads
|
|
151
152
|
await setConfigInRedis(config);
|
|
152
|
-
// Set
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
// Set BETTER_AUTH_SECRET from config (also set legacy
|
|
154
|
+
// NEXTAUTH_SECRET during the rename transition).
|
|
155
|
+
if (config.authSecret) {
|
|
156
|
+
process.env.BETTER_AUTH_SECRET = config.authSecret;
|
|
157
|
+
process.env.NEXTAUTH_SECRET = config.authSecret;
|
|
155
158
|
}
|
|
156
159
|
else {
|
|
157
|
-
throw new Error('[IDP_CONFIG] FATAL: IDP did not return
|
|
160
|
+
throw new Error('[IDP_CONFIG] FATAL: IDP did not return authSecret');
|
|
158
161
|
}
|
|
159
|
-
// Set IDENTITY_CLIENT_BASE_EXTERNAL_URL from config
|
|
160
|
-
//
|
|
161
|
-
// Only set if not already defined (allows deployment override for beta/staging)
|
|
162
|
+
// Set IDENTITY_CLIENT_BASE_EXTERNAL_URL from config.
|
|
163
|
+
// Only set if not already defined (allows deployment override for beta/staging).
|
|
162
164
|
if (config.baseClientUrl && !process.env.IDENTITY_CLIENT_BASE_EXTERNAL_URL) {
|
|
163
165
|
process.env.IDENTITY_CLIENT_BASE_EXTERNAL_URL = config.baseClientUrl;
|
|
164
166
|
console.log("[IDP_CONFIG] Set IDENTITY_CLIENT_BASE_EXTERNAL_URL:", config.baseClientUrl);
|
|
@@ -230,7 +232,13 @@ async function fetchConfigFromInternalIDP(internalIdpUrl, clientIdStr) {
|
|
|
230
232
|
const config = {
|
|
231
233
|
clientId: String(rawClientId),
|
|
232
234
|
clientSlug: configData.clientSlug ?? configData.client_slug ?? configData.slug ?? '',
|
|
233
|
-
|
|
235
|
+
// Wire compatibility: accept new authSecret first, fall back to legacy
|
|
236
|
+
// nextAuthSecret/next_auth_secret while IDP rename rolls out.
|
|
237
|
+
authSecret: configData.authSecret ??
|
|
238
|
+
configData.auth_secret ??
|
|
239
|
+
configData.nextAuthSecret ??
|
|
240
|
+
configData.next_auth_secret ??
|
|
241
|
+
'',
|
|
234
242
|
configCacheTtlSeconds: configData.configCacheTtlSeconds ?? configData.config_cache_ttl_seconds ?? 300,
|
|
235
243
|
oauthProviders: (configData.oauthProviders ?? configData.oauth_providers ?? []).map((p) => ({
|
|
236
244
|
provider: p.provider ?? '',
|
|
@@ -260,8 +268,8 @@ async function fetchConfigFromInternalIDP(internalIdpUrl, clientIdStr) {
|
|
|
260
268
|
},
|
|
261
269
|
baseClientUrl: configData.baseClientUrl ?? configData.base_client_url ?? configData.BaseClientUrl
|
|
262
270
|
};
|
|
263
|
-
if (!config.
|
|
264
|
-
throw new Error('[IDP_CONFIG] FATAL: Internal IDP did not return
|
|
271
|
+
if (!config.authSecret) {
|
|
272
|
+
throw new Error('[IDP_CONFIG] FATAL: Internal IDP did not return authSecret');
|
|
265
273
|
}
|
|
266
274
|
console.log(`[IDP_CONFIG] Internal IDP config loaded for ${clientIdStr}`);
|
|
267
275
|
consecutiveFailures = 0;
|
|
@@ -366,11 +374,17 @@ async function fetchConfigFromIDP(idpUrl, clientIdStr) {
|
|
|
366
374
|
if (rawClientId === undefined || rawClientId === null) {
|
|
367
375
|
throw new Error(`[IDP_CONFIG] FATAL: IDP response missing clientId/client_id. Got: ${JSON.stringify(Object.keys(configData))}`);
|
|
368
376
|
}
|
|
369
|
-
// Map response to our interface (IDP
|
|
377
|
+
// Map response to our interface (IDP returns camelCase or snake_case).
|
|
378
|
+
// Wire compatibility: accept new authSecret first, fall back to legacy
|
|
379
|
+
// nextAuthSecret/next_auth_secret while IDP rename rolls out.
|
|
370
380
|
const config = {
|
|
371
381
|
clientId: String(rawClientId),
|
|
372
382
|
clientSlug: configData.clientSlug ?? configData.client_slug ?? configData.slug ?? '',
|
|
373
|
-
|
|
383
|
+
authSecret: configData.authSecret ??
|
|
384
|
+
configData.auth_secret ??
|
|
385
|
+
configData.nextAuthSecret ??
|
|
386
|
+
configData.next_auth_secret ??
|
|
387
|
+
'',
|
|
374
388
|
configCacheTtlSeconds: configData.configCacheTtlSeconds ?? configData.config_cache_ttl_seconds ?? 300,
|
|
375
389
|
oauthProviders: (configData.oauthProviders ?? configData.oauth_providers ?? []).map((p) => ({
|
|
376
390
|
provider: p.provider ?? '',
|
|
@@ -418,8 +432,8 @@ async function fetchConfigFromIDP(idpUrl, clientIdStr) {
|
|
|
418
432
|
if (!config.clientId) {
|
|
419
433
|
throw new Error('[IDP_CONFIG] FATAL: clientId is empty or missing after parsing');
|
|
420
434
|
}
|
|
421
|
-
if (!config.
|
|
422
|
-
throw new Error('[IDP_CONFIG] FATAL:
|
|
435
|
+
if (!config.authSecret) {
|
|
436
|
+
throw new Error('[IDP_CONFIG] FATAL: authSecret is empty after parsing');
|
|
423
437
|
}
|
|
424
438
|
// Success - reset failure tracking
|
|
425
439
|
consecutiveFailures = 0;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
function
|
|
3
|
+
exports.validateAuthSecret = validateAuthSecret;
|
|
4
|
+
function validateAuthSecret(secret) {
|
|
5
5
|
if (!secret || typeof secret !== 'string')
|
|
6
6
|
return { valid: false, reason: 'missing' };
|
|
7
7
|
if (secret.length < 32)
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
* This module ensures that critical initialization tasks are completed
|
|
5
5
|
* before the application serves requests.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* -
|
|
7
|
+
* Uses unified IDP client config for:
|
|
8
|
+
* - BETTER_AUTH_SECRET (the Better Auth signing secret)
|
|
9
9
|
* - OAuth provider configuration
|
|
10
10
|
* - Auth settings (2FA, session timeouts, etc.)
|
|
11
11
|
*/
|
|
@@ -27,7 +27,7 @@ export declare function logStartupStatus(): void;
|
|
|
27
27
|
*/
|
|
28
28
|
export declare function getStartupIDPConfig(): IDPClientConfig | null;
|
|
29
29
|
/**
|
|
30
|
-
* Check if initialization failed (
|
|
30
|
+
* Check if initialization failed (auth signing secret couldn't be retrieved)
|
|
31
31
|
*/
|
|
32
32
|
export declare function isInitializationFailed(): boolean;
|
|
33
33
|
/**
|
package/dist/lib/startup-init.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* This module ensures that critical initialization tasks are completed
|
|
6
6
|
* before the application serves requests.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
8
|
+
* Uses unified IDP client config for:
|
|
9
|
+
* - BETTER_AUTH_SECRET (the Better Auth signing secret)
|
|
10
10
|
* - OAuth provider configuration
|
|
11
11
|
* - Auth settings (2FA, session timeouts, etc.)
|
|
12
12
|
*/
|
|
@@ -105,7 +105,7 @@ function logStartupStatus() {
|
|
|
105
105
|
console.log('║ 🚀 PayEz Next MVP - Starting Up ║');
|
|
106
106
|
console.log('║ ║');
|
|
107
107
|
console.log('║ Async initialization in progress... ║');
|
|
108
|
-
console.log('║ - Resolving
|
|
108
|
+
console.log('║ - Resolving BETTER_AUTH_SECRET from IDP ║');
|
|
109
109
|
console.log('║ - Verifying environment configuration ║');
|
|
110
110
|
console.log('║ ║');
|
|
111
111
|
console.log('║ Check logs below for detailed initialization status: ║');
|
|
@@ -144,15 +144,20 @@ async function performInitialization() {
|
|
|
144
144
|
console.log('[STARTUP] Client config loaded successfully');
|
|
145
145
|
console.log('[STARTUP] - Client ID:', config.clientId);
|
|
146
146
|
console.log('[STARTUP] - Client Slug:', config.clientSlug);
|
|
147
|
-
console.log('[STARTUP] - Secret length:', config.
|
|
147
|
+
console.log('[STARTUP] - Secret length:', config.authSecret?.length || 0, 'chars');
|
|
148
148
|
console.log('[STARTUP] - OAuth Providers:', config.oauthProviders?.filter(p => p.enabled).map(p => p.provider).join(', ') || 'none');
|
|
149
149
|
console.log('[STARTUP] - Require 2FA:', config.authSettings?.require2FA);
|
|
150
150
|
console.log('[STARTUP] - Cache TTL:', config.configCacheTtlSeconds, 'seconds');
|
|
151
151
|
console.log('[STARTUP] - Base Client URL:', config.baseClientUrl || '(not set)');
|
|
152
|
-
// Set
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
152
|
+
// Set BETTER_AUTH_SECRET from IDP response if not already set.
|
|
153
|
+
// Also mirror to legacy NEXTAUTH_SECRET during the rename transition
|
|
154
|
+
// so any consumer code still reading the old name keeps working.
|
|
155
|
+
if (config.authSecret && !process.env.BETTER_AUTH_SECRET) {
|
|
156
|
+
process.env.BETTER_AUTH_SECRET = config.authSecret;
|
|
157
|
+
console.log('[STARTUP] Set BETTER_AUTH_SECRET from IDP config');
|
|
158
|
+
}
|
|
159
|
+
if (config.authSecret && !process.env.NEXTAUTH_SECRET) {
|
|
160
|
+
process.env.NEXTAUTH_SECRET = config.authSecret;
|
|
156
161
|
}
|
|
157
162
|
}
|
|
158
163
|
catch (error) {
|
|
@@ -161,15 +166,15 @@ async function performInitialization() {
|
|
|
161
166
|
// No fallback available — IDP config is the only source for the auth secret
|
|
162
167
|
console.error('[STARTUP] No fallback available for auth secret resolution');
|
|
163
168
|
}
|
|
164
|
-
// Step 2: Verify
|
|
165
|
-
console.log('[STARTUP] Step 2/2: Verifying
|
|
166
|
-
const secret = process.env.NEXTAUTH_SECRET;
|
|
169
|
+
// Step 2: Verify BETTER_AUTH_SECRET is available - FAIL FAST if not
|
|
170
|
+
console.log('[STARTUP] Step 2/2: Verifying BETTER_AUTH_SECRET...');
|
|
171
|
+
const secret = process.env.BETTER_AUTH_SECRET || process.env.NEXTAUTH_SECRET;
|
|
167
172
|
if (!secret || secret.trim() === '') {
|
|
168
173
|
console.error('');
|
|
169
174
|
console.error('╔══════════════════════════════════════════════════════════════╗');
|
|
170
|
-
console.error('║ ❌ FATAL:
|
|
175
|
+
console.error('║ ❌ FATAL: BETTER_AUTH_SECRET NOT AVAILABLE ║');
|
|
171
176
|
console.error('║ ║');
|
|
172
|
-
console.error('║ The app cannot start without a valid
|
|
177
|
+
console.error('║ The app cannot start without a valid auth signing secret. ║');
|
|
173
178
|
console.error('║ This should be fetched from IDP at startup. ║');
|
|
174
179
|
console.error('║ ║');
|
|
175
180
|
console.error('║ Possible causes: ║');
|
|
@@ -179,9 +184,9 @@ async function performInitialization() {
|
|
|
179
184
|
console.error('║ • Network connectivity issue ║');
|
|
180
185
|
console.error('╚══════════════════════════════════════════════════════════════╝');
|
|
181
186
|
console.error('');
|
|
182
|
-
throw new Error('FATAL:
|
|
187
|
+
throw new Error('FATAL: BETTER_AUTH_SECRET not available - cannot start without valid secret from IDP');
|
|
183
188
|
}
|
|
184
|
-
console.log('[STARTUP]
|
|
189
|
+
console.log('[STARTUP] BETTER_AUTH_SECRET verified (' + secret.length + ' chars)');
|
|
185
190
|
// Step 3: Validate cookie name consistency
|
|
186
191
|
// This catches bugs where getJwtCookieName() returns a different name than
|
|
187
192
|
// what auth-options.ts configures, which causes sessions to fail in production
|
|
@@ -210,9 +215,9 @@ async function performInitialization() {
|
|
|
210
215
|
: '';
|
|
211
216
|
console.error(`
|
|
212
217
|
╔══════════════════════════════════════════════════════════════╗
|
|
213
|
-
║ ❌ FATAL:
|
|
218
|
+
║ ❌ FATAL: BETTER_AUTH_SECRET NOT AVAILABLE ║
|
|
214
219
|
║ ║
|
|
215
|
-
║ The app cannot start without a valid
|
|
220
|
+
║ The app cannot start without a valid auth signing secret. ║
|
|
216
221
|
║ This should be fetched from IDP at startup. ║
|
|
217
222
|
║ ║
|
|
218
223
|
${connectionLine}║ Possible causes: ║
|
|
@@ -240,7 +245,7 @@ function getStartupIDPConfig() {
|
|
|
240
245
|
return cachedIDPConfig;
|
|
241
246
|
}
|
|
242
247
|
/**
|
|
243
|
-
* Check if initialization failed (
|
|
248
|
+
* Check if initialization failed (auth signing secret couldn't be retrieved)
|
|
244
249
|
*/
|
|
245
250
|
function isInitializationFailed() {
|
|
246
251
|
return initializationFailed;
|
|
@@ -40,11 +40,11 @@ const app_slug_1 = require("./app-slug");
|
|
|
40
40
|
async function getTokenTestAware(req) {
|
|
41
41
|
if (process.env.TEST_MODE === 'true') {
|
|
42
42
|
try {
|
|
43
|
-
let secret = process.env.NEXTAUTH_SECRET;
|
|
43
|
+
let secret = process.env.BETTER_AUTH_SECRET || process.env.NEXTAUTH_SECRET;
|
|
44
44
|
if (!secret || secret.trim() === '') {
|
|
45
45
|
const { getIDPClientConfig } = await Promise.resolve().then(() => __importStar(require('./idp-client-config')));
|
|
46
46
|
const idpConfig = await getIDPClientConfig();
|
|
47
|
-
secret = idpConfig.
|
|
47
|
+
secret = idpConfig.authSecret;
|
|
48
48
|
}
|
|
49
49
|
// Use app-slug prefixed cookie name
|
|
50
50
|
const cookieName = (0, app_slug_1.getSessionCookieName)();
|
|
@@ -81,54 +81,5 @@ async function getTokenTestAware(req) {
|
|
|
81
81
|
...session.user,
|
|
82
82
|
};
|
|
83
83
|
}
|
|
84
|
-
// Fallback for legacy NextAuth-based sites: try next-auth/jwt with the
|
|
85
|
-
// app-slug-prefixed cookie name. Uses runtime require so consumers without
|
|
86
|
-
// next-auth installed are unaffected.
|
|
87
|
-
try {
|
|
88
|
-
// eslint-disable-next-line @typescript-eslint/no-implied-eval, no-eval
|
|
89
|
-
const dynamicRequire = eval('require');
|
|
90
|
-
let nextAuthJwt = null;
|
|
91
|
-
try {
|
|
92
|
-
nextAuthJwt = dynamicRequire('next-auth/jwt');
|
|
93
|
-
}
|
|
94
|
-
catch {
|
|
95
|
-
return null; // next-auth not installed → BA-only consumer
|
|
96
|
-
}
|
|
97
|
-
if (!nextAuthJwt?.getToken)
|
|
98
|
-
return null;
|
|
99
|
-
const { resolveNextAuthSecret } = await Promise.resolve().then(() => __importStar(require('./nextauth-secret')));
|
|
100
|
-
const secret = await resolveNextAuthSecret();
|
|
101
|
-
if (!secret)
|
|
102
|
-
return null;
|
|
103
|
-
const cookieName = (0, app_slug_1.getSessionCookieName)();
|
|
104
|
-
let nextAuthToken = await nextAuthJwt.getToken({
|
|
105
|
-
req,
|
|
106
|
-
secret,
|
|
107
|
-
cookieName,
|
|
108
|
-
secureCookie: false,
|
|
109
|
-
});
|
|
110
|
-
if (nextAuthToken) {
|
|
111
|
-
logger_1.logger.debug('[GET_TOKEN] Resolved via NextAuth JWT (cookieName=' + cookieName + ')');
|
|
112
|
-
return nextAuthToken;
|
|
113
|
-
}
|
|
114
|
-
// Try secure cookie variant for production
|
|
115
|
-
const { getSecureSessionCookieName } = await Promise.resolve().then(() => __importStar(require('./app-slug')));
|
|
116
|
-
const secureCookieName = getSecureSessionCookieName();
|
|
117
|
-
nextAuthToken = await nextAuthJwt.getToken({
|
|
118
|
-
req,
|
|
119
|
-
secret,
|
|
120
|
-
cookieName: secureCookieName,
|
|
121
|
-
secureCookie: true,
|
|
122
|
-
});
|
|
123
|
-
if (nextAuthToken) {
|
|
124
|
-
logger_1.logger.debug('[GET_TOKEN] Resolved via NextAuth JWT (secure cookie)');
|
|
125
|
-
return nextAuthToken;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
logger_1.logger.debug('[GET_TOKEN] NextAuth fallback error', {
|
|
130
|
-
error: error instanceof Error ? error.message : String(error),
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
84
|
return null;
|
|
134
85
|
}
|
|
@@ -24,7 +24,7 @@ export { POST } from '../../api-handlers/account/masked-info';
|
|
|
24
24
|
* Environment variables used:
|
|
25
25
|
* - IDP_URL or NEXT_PUBLIC_IDP_URL (default: http://localhost:32785)
|
|
26
26
|
* - CLIENT_ID or NEXT_PUBLIC_IDP_CLIENT_ID (required)
|
|
27
|
-
* -
|
|
27
|
+
* - BETTER_AUTH_SECRET (required — fetched from IDP at startup)
|
|
28
28
|
*
|
|
29
29
|
* Returns:
|
|
30
30
|
* - Masked email addresses
|
|
@@ -30,7 +30,7 @@ Object.defineProperty(exports, "POST", { enumerable: true, get: function () { re
|
|
|
30
30
|
* Environment variables used:
|
|
31
31
|
* - IDP_URL or NEXT_PUBLIC_IDP_URL (default: http://localhost:32785)
|
|
32
32
|
* - CLIENT_ID or NEXT_PUBLIC_IDP_CLIENT_ID (required)
|
|
33
|
-
* -
|
|
33
|
+
* - BETTER_AUTH_SECRET (required — fetched from IDP at startup)
|
|
34
34
|
*
|
|
35
35
|
* Returns:
|
|
36
36
|
* - Masked email addresses
|
|
@@ -28,7 +28,7 @@ export { POST } from '../../api-handlers/account/send-code';
|
|
|
28
28
|
* Environment variables used:
|
|
29
29
|
* - IDP_URL or NEXT_PUBLIC_IDP_URL (default: http://localhost:32785)
|
|
30
30
|
* - CLIENT_ID or NEXT_PUBLIC_IDP_CLIENT_ID (required)
|
|
31
|
-
* -
|
|
31
|
+
* - BETTER_AUTH_SECRET (required — fetched from IDP at startup)
|
|
32
32
|
*
|
|
33
33
|
* Returns:
|
|
34
34
|
* - Success status
|
|
@@ -33,7 +33,7 @@ Object.defineProperty(exports, "POST", { enumerable: true, get: function () { re
|
|
|
33
33
|
* Environment variables used:
|
|
34
34
|
* - IDP_URL or NEXT_PUBLIC_IDP_URL (default: http://localhost:32785)
|
|
35
35
|
* - CLIENT_ID or NEXT_PUBLIC_IDP_CLIENT_ID (required)
|
|
36
|
-
* -
|
|
36
|
+
* - BETTER_AUTH_SECRET (required — fetched from IDP at startup)
|
|
37
37
|
*
|
|
38
38
|
* Returns:
|
|
39
39
|
* - Success status
|
|
@@ -27,7 +27,7 @@ export { POST } from '../../api-handlers/account/verify-email';
|
|
|
27
27
|
* Environment variables used:
|
|
28
28
|
* - IDP_URL or NEXT_PUBLIC_IDP_URL (default: http://localhost:32785)
|
|
29
29
|
* - CLIENT_ID or NEXT_PUBLIC_IDP_CLIENT_ID (required)
|
|
30
|
-
* -
|
|
30
|
+
* - BETTER_AUTH_SECRET (required — fetched from IDP at startup)
|
|
31
31
|
*
|
|
32
32
|
* Returns:
|
|
33
33
|
* - Upgraded access token with MFA claim
|
|
@@ -32,7 +32,7 @@ Object.defineProperty(exports, "POST", { enumerable: true, get: function () { re
|
|
|
32
32
|
* Environment variables used:
|
|
33
33
|
* - IDP_URL or NEXT_PUBLIC_IDP_URL (default: http://localhost:32785)
|
|
34
34
|
* - CLIENT_ID or NEXT_PUBLIC_IDP_CLIENT_ID (required)
|
|
35
|
-
* -
|
|
35
|
+
* - BETTER_AUTH_SECRET (required — fetched from IDP at startup)
|
|
36
36
|
*
|
|
37
37
|
* Returns:
|
|
38
38
|
* - Upgraded access token with MFA claim
|
|
@@ -27,7 +27,7 @@ export { POST } from '../../api-handlers/account/verify-sms';
|
|
|
27
27
|
* Environment variables used:
|
|
28
28
|
* - IDP_URL or NEXT_PUBLIC_IDP_URL (default: http://localhost:32785)
|
|
29
29
|
* - CLIENT_ID or NEXT_PUBLIC_IDP_CLIENT_ID (required)
|
|
30
|
-
* -
|
|
30
|
+
* - BETTER_AUTH_SECRET (required — fetched from IDP at startup)
|
|
31
31
|
*
|
|
32
32
|
* Returns:
|
|
33
33
|
* - Upgraded access token with MFA claim
|
|
@@ -32,7 +32,7 @@ Object.defineProperty(exports, "POST", { enumerable: true, get: function () { re
|
|
|
32
32
|
* Environment variables used:
|
|
33
33
|
* - IDP_URL or NEXT_PUBLIC_IDP_URL (default: http://localhost:32785)
|
|
34
34
|
* - CLIENT_ID or NEXT_PUBLIC_IDP_CLIENT_ID (required)
|
|
35
|
-
* -
|
|
35
|
+
* - BETTER_AUTH_SECRET (required — fetched from IDP at startup)
|
|
36
36
|
*
|
|
37
37
|
* Returns:
|
|
38
38
|
* - Upgraded access token with MFA claim
|
|
@@ -17,10 +17,8 @@
|
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.POST = POST;
|
|
19
19
|
const refresh_1 = require("../../api-handlers/auth/refresh");
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
async function getConfig() {
|
|
23
|
-
const idpConfig = await (0, idp_client_config_1.getIDPClientConfig)();
|
|
20
|
+
// Configuration is read at runtime from environment.
|
|
21
|
+
function getConfig() {
|
|
24
22
|
const idpBaseUrl = process.env.IDP_URL;
|
|
25
23
|
if (!idpBaseUrl) {
|
|
26
24
|
throw new Error('[IDP_URL] FATAL: IDP_URL environment variable is REQUIRED.');
|
|
@@ -28,7 +26,6 @@ async function getConfig() {
|
|
|
28
26
|
return {
|
|
29
27
|
idpBaseUrl,
|
|
30
28
|
clientId: process.env.CLIENT_ID || process.env.NEXT_PUBLIC_IDP_CLIENT_ID || '',
|
|
31
|
-
nextAuthSecret: idpConfig.nextAuthSecret || '',
|
|
32
29
|
refreshEndpoint: process.env.REFRESH_ENDPOINT || '/api/ExternalAuth/refresh',
|
|
33
30
|
};
|
|
34
31
|
}
|
|
@@ -38,14 +35,12 @@ async function getConfig() {
|
|
|
38
35
|
* Environment variables used:
|
|
39
36
|
* - IDP_URL (REQUIRED)
|
|
40
37
|
* - CLIENT_ID or NEXT_PUBLIC_IDP_CLIENT_ID (required)
|
|
41
|
-
* - NEXTAUTH_SECRET (required)
|
|
42
38
|
* - REFRESH_ENDPOINT (default: /api/ExternalAuth/refresh)
|
|
43
39
|
*/
|
|
44
40
|
let _handler = null;
|
|
45
41
|
async function POST(req) {
|
|
46
42
|
if (!_handler) {
|
|
47
|
-
|
|
48
|
-
_handler = (0, refresh_1.createRefreshHandler)(config);
|
|
43
|
+
_handler = (0, refresh_1.createRefreshHandler)(getConfig());
|
|
49
44
|
}
|
|
50
45
|
return _handler(req);
|
|
51
46
|
}
|
package/dist/server/auth.d.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Server-side auth utilities for Better Auth
|
|
2
|
+
* Server-side auth utilities for Better Auth.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - getServerSession() from next-auth
|
|
7
|
-
*
|
|
8
|
-
* All server-side auth flows go through the Better Auth instance.
|
|
4
|
+
* All server-side auth flows go through the Better Auth instance returned by
|
|
5
|
+
* getAuthInstance(); use getSession(req) for the request-scoped session.
|
|
9
6
|
*/
|
|
10
7
|
import 'server-only';
|
|
11
8
|
/**
|
|
@@ -36,7 +33,7 @@ export declare function getAuthInstance(): Promise<import("better-auth/types").A
|
|
|
36
33
|
};
|
|
37
34
|
};
|
|
38
35
|
};
|
|
39
|
-
plugins: [
|
|
36
|
+
plugins: [{
|
|
40
37
|
id: "next-cookies";
|
|
41
38
|
hooks: {
|
|
42
39
|
before: {
|
package/dist/server/auth.js
CHANGED
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* Server-side auth utilities for Better Auth
|
|
3
|
+
* Server-side auth utilities for Better Auth.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* - getServerSession() from next-auth
|
|
8
|
-
*
|
|
9
|
-
* All server-side auth flows go through the Better Auth instance.
|
|
5
|
+
* All server-side auth flows go through the Better Auth instance returned by
|
|
6
|
+
* getAuthInstance(); use getSession(req) for the request-scoped session.
|
|
10
7
|
*/
|
|
11
8
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
9
|
if (k2 === undefined) k2 = k;
|
|
@@ -166,9 +166,9 @@ async function decodeSession(requestCookies) {
|
|
|
166
166
|
return null;
|
|
167
167
|
}
|
|
168
168
|
const config = await (0, idp_client_config_1.getIDPClientConfig)();
|
|
169
|
-
const secret = config.
|
|
169
|
+
const secret = config.authSecret;
|
|
170
170
|
if (!secret) {
|
|
171
|
-
console.error('[DECODE-SESSION] No
|
|
171
|
+
console.error('[DECODE-SESSION] No authSecret available from IDP config');
|
|
172
172
|
return null;
|
|
173
173
|
}
|
|
174
174
|
const secretKey = new TextEncoder().encode(secret);
|
|
@@ -141,7 +141,7 @@ export declare function useVibeMutation<T extends VibeTableName, Op extends Muta
|
|
|
141
141
|
/**
|
|
142
142
|
* Convenience hook for creating records.
|
|
143
143
|
*/
|
|
144
|
-
export declare function useVibeCreate<T extends VibeTableName>(table: T, options?: Omit<UseVibeMutationOptions<T, 'create'>, never>): import("@tanstack/react-query").UseMutationResult<VibeTableType<T>, VibeError, Omit<VibeTableType<T>, "
|
|
144
|
+
export declare function useVibeCreate<T extends VibeTableName>(table: T, options?: Omit<UseVibeMutationOptions<T, 'create'>, never>): import("@tanstack/react-query").UseMutationResult<VibeTableType<T>, VibeError, Omit<VibeTableType<T>, "created_at" | "id" | "updated_at">, unknown>;
|
|
145
145
|
/**
|
|
146
146
|
* Convenience hook for updating records.
|
|
147
147
|
*/
|