feat: Security

This commit is contained in:
2026-06-05 18:34:01 +02:00
parent ee293d7daf
commit 45675192a5
134 changed files with 4128 additions and 446 deletions

View File

@@ -0,0 +1,72 @@
import '../types/express-augmentation';
import {
NextFunction,
Request,
Response
} from 'express';
import { consumeSessionToken } from '../services/session-auth.service';
function readBearerToken(req: Request): string | null {
const header = req.header('authorization');
if (!header || !header.toLowerCase().startsWith('bearer ')) {
return null;
}
const token = header.slice(7).trim();
return token || null;
}
export async function requireAuth(req: Request, res: Response, next: NextFunction): Promise<void> {
const token = readBearerToken(req);
if (!token) {
res.status(401).json({ error: 'Missing or invalid authorization token', errorCode: 'UNAUTHORIZED' });
return;
}
const session = await consumeSessionToken(token);
if (!session) {
res.status(401).json({ error: 'Missing or invalid authorization token', errorCode: 'UNAUTHORIZED' });
return;
}
req.authToken = session.token;
req.authUserId = session.user.id;
req.authUser = session.user;
next();
}
export function getAuthenticatedUserId(req: Request): string {
const userId = req.authUserId;
if (!userId) {
throw new Error('Authenticated user id missing after requireAuth');
}
return userId;
}
export function rejectSpoofedUserId(
req: Request,
res: Response,
bodyUserId: unknown,
fieldName: string
): bodyUserId is string {
if (typeof bodyUserId !== 'string' || !bodyUserId.trim()) {
return false;
}
if (bodyUserId !== req.authUserId) {
res.status(400).json({
error: `${fieldName} must match the authenticated user`,
errorCode: 'USER_ID_MISMATCH'
});
return false;
}
return true;
}