rojects/source/server/middleware/auth.ts

60 lines
1.6 KiB
TypeScript
Raw Normal View History

import { Request, Response, NextFunction } from 'express';
import jwt from 'jsonwebtoken';
const AUTH_HOST = process.env.AUTH_HOST ?? 'https://account.rokojori.com';
const JWT_SECRET = process.env.JWT_SECRET ?? '';
export interface JwtUser {
userId: string;
email: string;
roles: string[];
products: string[];
settings: Record<string, unknown>;
}
declare global {
namespace Express {
interface Request {
user?: JwtUser;
}
}
}
function extractToken( req: Request ): string | undefined {
const cookie = req.cookies?.accessToken as string | undefined;
if ( cookie ) return cookie;
const header = req.headers.authorization;
if ( header?.startsWith( 'Bearer ' ) ) return header.slice( 7 );
return undefined;
}
function isApiRequest( req: Request ): boolean {
return req.path.startsWith( '/api/' );
}
export function jwtMiddleware( req: Request, res: Response, next: NextFunction ): void {
const token = extractToken( req );
if ( !token ) { next(); return; }
try {
req.user = jwt.verify( token, JWT_SECRET ) as JwtUser;
next();
} catch ( err: unknown ) {
if ( err instanceof jwt.TokenExpiredError && !isApiRequest( req ) ) {
const redirect = encodeURIComponent( req.protocol + '://' + req.get( 'host' ) + req.originalUrl );
res.redirect( `${AUTH_HOST}/api/auth/refresh-session?redirect=${redirect}` );
} else {
next();
}
}
}
export function requireAuth( req: Request, res: Response, next: NextFunction ): void {
if ( !req.user ) {
res.status( 401 ).json( { error: 'Not authenticated' } );
return;
}
next();
}