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
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:
@@ -1,4 +1,4 @@
|
||||
import { SignJWT, jwtVerify } from 'jose'
|
||||
import { SignJWT, jwtVerify, decodeJwt } from 'jose'
|
||||
import { randomBytes, createHash } from 'crypto'
|
||||
import { cookies } from 'next/headers'
|
||||
import pool from './db'
|
||||
@@ -125,7 +125,34 @@ export async function getSessionFromCookie(): Promise<JwtPayload | null> {
|
||||
const cookieStore = await cookies()
|
||||
const token = cookieStore.get(COOKIE_NAME)?.value
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user