fix(pitch-deck): refresh expired JWT from live DB session on cookie read
All checks were successful
Build pitch-deck / build-push-deploy (push) Successful in 1m13s
CI / go-lint (push) Has been skipped
CI / python-lint (push) Has been skipped
CI / nodejs-lint (push) Has been skipped
CI / test-go-consent (push) Successful in 37s
CI / test-python-voice (push) Successful in 36s
CI / test-bqas (push) Successful in 34s

When jwtVerify fails (JWT expired), decode the token without expiry check
to recover sessionId, validate it against the DB, and reissue a fresh 24h
JWT. Fixes investors with old 1h JWTs being locked out on magic link re-click.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Sharang Parnerkar
2026-04-16 22:18:52 +02:00
parent 0188a46afb
commit d548ce4199

View File

@@ -1,4 +1,4 @@
import { SignJWT, jwtVerify } from 'jose' import { SignJWT, jwtVerify, decodeJwt } from 'jose'
import { randomBytes, createHash } from 'crypto' import { randomBytes, createHash } from 'crypto'
import { cookies } from 'next/headers' import { cookies } from 'next/headers'
import pool from './db' import pool from './db'
@@ -125,7 +125,34 @@ export async function getSessionFromCookie(): Promise<JwtPayload | null> {
const cookieStore = await cookies() const cookieStore = await cookies()
const token = cookieStore.get(COOKIE_NAME)?.value const token = cookieStore.get(COOKIE_NAME)?.value
if (!token) return null if (!token) return null
return verifyJwt(token)
// Fast path: valid non-expired JWT
const payload = await verifyJwt(token)
if (payload) return payload
// Slow path: JWT may be expired but DB session could still be valid.
// Decode without signature/expiry check to recover sessionId + sub.
try {
const decoded = decodeJwt(token) as Partial<JwtPayload>
if (!decoded.sessionId || !decoded.sub) return null
const valid = await validateSession(decoded.sessionId, decoded.sub)
if (!valid) return null
// DB session still live — fetch email and reissue a fresh JWT
const { rows } = await pool.query(
`SELECT email FROM pitch_investors WHERE id = $1`,
[decoded.sub]
)
if (rows.length === 0) return null
const freshJwt = await createJwt({ sub: decoded.sub, email: rows[0].email, sessionId: decoded.sessionId })
await setSessionCookie(freshJwt)
return { sub: decoded.sub, email: rows[0].email, sessionId: decoded.sessionId }
} catch {
return null
}
} }
export function getClientIp(request: Request): string | null { export function getClientIp(request: Request): string | null {