fix(session): validate SESSION_SECRET lazily, not at module load
Deploy to VPS / deploy (push) Has been cancelled
Deploy to VPS / deploy (push) Has been cancelled
The module-level SESSION_SECRET check threw during `next build` (page-data collection imports session.ts but has no runtime env vars), breaking the Docker build at /hq-command/dashboard. Move the check inside getEncodedKey() so it runs at runtime when a session is actually created. Security is unchanged: the secret is still never defaulted, and any attempt to mint a session without a valid 32+ char secret throws loudly. It just no longer blocks the build. Verified: `next build` now compiles with SESSION_SECRET unset (replicating the Docker builder stage) — 56/56 pages generated. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+16
-8
@@ -4,14 +4,22 @@ import { cookies } from "next/headers";
|
|||||||
// SESSION_SECRET is REQUIRED. No fallback: a public default would let anyone
|
// SESSION_SECRET is REQUIRED. No fallback: a public default would let anyone
|
||||||
// forge a 7-day admin JWT if the env var ever fails to load in production.
|
// forge a 7-day admin JWT if the env var ever fails to load in production.
|
||||||
// Generate a strong value with: openssl rand -base64 48
|
// Generate a strong value with: openssl rand -base64 48
|
||||||
const secretKey = process.env.SESSION_SECRET;
|
//
|
||||||
if (!secretKey || secretKey.length < 32) {
|
// Validated LAZILY (inside getEncodedKey, not at module load). `next build`
|
||||||
throw new Error(
|
// imports this module during page-data collection without runtime env vars,
|
||||||
"SESSION_SECRET environment variable is required (min 32 chars). " +
|
// so a top-level throw would break the build. At runtime, any attempt to
|
||||||
"Generate one with: openssl rand -base64 48"
|
// create a session without a valid secret still throws loudly — the secret
|
||||||
);
|
// is never defaulted, so admin JWTs can't be forged.
|
||||||
|
function getEncodedKey(): Uint8Array {
|
||||||
|
const secretKey = process.env.SESSION_SECRET;
|
||||||
|
if (!secretKey || secretKey.length < 32) {
|
||||||
|
throw new Error(
|
||||||
|
"SESSION_SECRET environment variable is required (min 32 chars). " +
|
||||||
|
"Generate one with: openssl rand -base64 48"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return new TextEncoder().encode(secretKey);
|
||||||
}
|
}
|
||||||
const encodedKey = new TextEncoder().encode(secretKey);
|
|
||||||
|
|
||||||
export async function createSession(userId: string, username: string) {
|
export async function createSession(userId: string, username: string) {
|
||||||
const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 días de duración
|
const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); // 7 días de duración
|
||||||
@@ -21,7 +29,7 @@ export async function createSession(userId: string, username: string) {
|
|||||||
.setProtectedHeader({ alg: "HS256" })
|
.setProtectedHeader({ alg: "HS256" })
|
||||||
.setIssuedAt()
|
.setIssuedAt()
|
||||||
.setExpirationTime("7d")
|
.setExpirationTime("7d")
|
||||||
.sign(encodedKey);
|
.sign(getEncodedKey());
|
||||||
|
|
||||||
// En Next.js 15+, cookies() es una Promesa
|
// En Next.js 15+, cookies() es una Promesa
|
||||||
const cookieStore = await cookies();
|
const cookieStore = await cookies();
|
||||||
|
|||||||
Reference in New Issue
Block a user