54 lines
2.0 KiB
TypeScript
54 lines
2.0 KiB
TypeScript
import { Request, Response, NextFunction } from 'express';
|
|
import { Pool } from 'pg';
|
|
|
|
// Helper utility to parse cookies manually without adding extra npm packages
|
|
function parseCookieString(cookieHeader: string | undefined, name: string): string | null {
|
|
if (!cookieHeader) return null;
|
|
const match = cookieHeader.match(new RegExp('(^| )' + name + '=([^;]+)'));
|
|
return match ? match[2] : null;
|
|
}
|
|
|
|
export function createAuthMiddleware(db: Pool) {
|
|
return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
|
try {
|
|
const cookieHeader = req.headers.cookie;
|
|
const token = parseCookieString(cookieHeader, 'session_token');
|
|
|
|
if (!token) {
|
|
res.status(401).json({ success: false, error: 'Unauthorized. Session cookie missing.' });
|
|
return;
|
|
}
|
|
|
|
// Query the database to find an active session matching the token
|
|
const query = `
|
|
SELECT user_id, expires_at
|
|
FROM sessions
|
|
WHERE id = $1;
|
|
`;
|
|
const result = await db.query(query, [token]);
|
|
|
|
if (result.rows.length === 0) {
|
|
res.status(401).json({ success: false, error: 'Unauthorized. Invalid session token.' });
|
|
return;
|
|
}
|
|
|
|
const session = result.rows[0];
|
|
const now = new Date();
|
|
|
|
// Validate expiration constraint
|
|
if (new Date(session.expires_at) < now) {
|
|
res.status(401).json({ success: false, error: 'Unauthorized. Session expired.' });
|
|
return;
|
|
}
|
|
|
|
// Inject the authenticated identity directly into the Request object for use in down-stream router handlers
|
|
(req as any).userId = session.user_id;
|
|
|
|
next(); // Execution matches, pass cleanly to route target
|
|
} catch (error) {
|
|
console.error('Auth middleware failure:', error);
|
|
res.status(500).json({ success: false, error: 'Internal Server Error' });
|
|
}
|
|
};
|
|
}
|