Skip to content

middleware.ts falls back to a hardcoded JWT secret: forged auth-token cookies verify when AUTH_SECRET is unset #814

Description

@nanaf6203-bit

middleware.ts falls back to a hardcoded JWT secret: forged auth-token cookies verify when AUTH_SECRET is unset

Labels / Complexity: bug · security · High Complexity — High

Problem

The auth middleware signs/verifies JWTs with a publicly-known secret whenever AUTH_SECRET is not set. middleware.ts (line 27):

// Ensure AUTH_SECRET is set in .env
const secretKey = process.env.AUTH_SECRET || 'default-fallback-secret-for-dev-only-do-not-use-in-prod';
const secret = new TextEncoder().encode(secretKey);

// Verify signature and expiry with 15s clock tolerance
await jwtVerify(token, secret, {
  clockTolerance: 15,
});

The fallback string is in the repository, so anyone can forge an auth-token cookie signed with it. In any deployment where AUTH_SECRET is missing or empty, the middleware's jwtVerify accepts attacker-crafted tokens and the protected routes (/dashboard, /portfolio, /settings, /invest per PROTECTED_ROUTES at the top of the file) are bypassable. Consequences:

Root cause

middleware.ts line 27: the || 'default-fallback-secret-...' fallback. The comment on the line above ("Ensure AUTH_SECRET is set in .env") shows the intent, but the code does not enforce it.

Why this is architecturally hard

  1. Middleware has no startup hook. The BackEnd fix works because bootstrap() runs before serving; Next.js middleware runs per-request in the Edge runtime, so the "fail fast at boot" pattern does not apply directly. The contributor must choose: fail closed per-request when the env var is absent (redirect with an explicit server error), or validate the variable at build/startup via next.config.ts/a config module that middleware can import.
  2. Failing closed changes user experience. If the fix rejects all protected-route requests when AUTH_SECRET is unset, every environment (local dev included) must set the variable — scripts/validate-env.js exists and may need the variable added to its checks so the failure surfaces early and clearly.
  3. The verification call site must keep working. The jwtVerify/clockTolerance logic and the cookie-clear-on-invalid branch should be preserved; the fix is about the key material source, not the verification flow, so the change must be minimal and covered by whatever middleware tests exist (check __tests__/ for middleware coverage before assuming none).

Downstream impact

None for other repos: middleware.ts is frontend-only. PropChain-BackEnd already validates its own secrets, so there is no shared secret contract to change.

Acceptance criteria

  • With AUTH_SECRET unset or empty, protected routes reject requests (redirect) instead of verifying with the fallback secret; with it set, normal verification behaves as today.
  • The literal fallback string 'default-fallback-secret-for-dev-only-do-not-use-in-prod' is gone from middleware.ts.
  • A test proves a token signed with the old fallback string is rejected when AUTH_SECRET is set, and that a missing AUTH_SECRET fails closed.
  • npm run typecheck, npm test, and npm run lint pass.

Out of scope

Rotating the signing algorithm, adding refresh-token handling, and changing PROTECTED_ROUTES are out of scope.

Getting started

  • middleware.ts — the fallback at line 27 and the verify block below it
  • scripts/validate-env.js — where the env var should be required so failures surface at startup
  • __tests__/ — check for existing middleware tests to extend

Commands: npm run typecheck, npm test, npm run lint, npm run validate:env.

Good first files to read: middleware.ts, scripts/validate-env.js.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

High ComplexityStellar WaveIssues in the Stellar wave programbugSomething isn't working correctlysecuritySecurity issue or hardening opportunity

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions