auth: Bearer-before-cookie token extraction; JWT_CLOCK_TOLERANCE for clock skew

extractToken now checks Authorization: Bearer before the accessToken cookie so
injected headers (Electron, API clients) cannot be shadowed by stale cookies.
JWT_CLOCK_TOLERANCE env var (seconds) is passed to jwt.verify as clockTolerance
when non-zero — set in .env for local dev to absorb clock skew vs production.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Rokojori 2026-07-18 08:37:26 +02:00
parent 0b06c252a3
commit cb2fba6e8b
1 changed files with 4 additions and 3 deletions

View File

@ -19,15 +19,16 @@ const JWT_SECRET = process.env.JWT_SECRET ?? '';
const AUTH_HOST = process.env.AUTH_HOST ?? 'https://account.rokojori.com'; const AUTH_HOST = process.env.AUTH_HOST ?? 'https://account.rokojori.com';
const AUTH_INTERNAL_HOST = process.env.AUTH_INTERNAL_HOST ?? AUTH_HOST; const AUTH_INTERNAL_HOST = process.env.AUTH_INTERNAL_HOST ?? AUTH_HOST;
const COOKIE_DOMAIN = process.env.COOKIE_DOMAIN ?? '.rokojori.com'; const COOKIE_DOMAIN = process.env.COOKIE_DOMAIN ?? '.rokojori.com';
const CLOCK_TOLERANCE = parseInt( process.env.JWT_CLOCK_TOLERANCE ?? '0', 10 );
// ── Internal helpers ─────────────────────────────────────────────────────────── // ── Internal helpers ───────────────────────────────────────────────────────────
function extractToken( req: Request ): string | undefined function extractToken( req: Request ): string | undefined
{ {
const cookie = req.cookies?.accessToken as string | undefined;
if ( cookie ) return cookie;
const header = req.headers.authorization; const header = req.headers.authorization;
if ( header?.startsWith( 'Bearer ' ) ) return header.slice( 7 ); if ( header?.startsWith( 'Bearer ' ) ) return header.slice( 7 );
const cookie = req.cookies?.accessToken as string | undefined;
if ( cookie ) return cookie;
return undefined; return undefined;
} }
@ -100,7 +101,7 @@ export function jwtMiddleware( req: Request, res: Response, next: NextFunction )
try try
{ {
req.auth = jwt.verify( token, JWT_SECRET ) as AuthPayload; req.auth = jwt.verify( token, JWT_SECRET, CLOCK_TOLERANCE ? { clockTolerance: CLOCK_TOLERANCE } : {} ) as AuthPayload;
req.rawToken = token; req.rawToken = token;
next(); next();
return; return;