Masar / src /middleware /auth.ts
Hussien Haider
H
d448275
Raw
History Blame Contribute Delete
3.59 kB
import { Request, Response, NextFunction } from 'express';
import { z } from 'zod';
import { supabase } from '../config/supabase';
import { logError } from '../utils/logger';
import { logActivity } from '../services/activityLog';
export interface AuthRequest extends Request {
userId?: string;
userEmail?: string;
}
export const validateRequest = (schema: z.ZodSchema, target: 'body' | 'query' | 'params' = 'body') => {
return (req: Request, res: Response, next: NextFunction) => {
const result = schema.safeParse(req[target]);
if (!result.success) {
logActivity({
action: 'SUSPICIOUS_INPUT',
details: `Validation failed on ${target}: ${result.error.issues.map(i => i.message).join(', ')}`,
ip: req.ip,
user_agent: req.get('user-agent'),
});
res.status(400).json({
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid request data',
details: result.error.flatten().fieldErrors,
},
});
return;
}
req[target] = result.data;
next();
};
};
export const uuidParam = z.string().uuid();
export const authenticate = async (req: AuthRequest, res: Response, next: NextFunction) => {
try {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
logActivity({
action: 'UNAUTHENTICATED_ACCESS',
details: `Missing auth header on ${req.method} ${req.originalUrl}`,
ip: req.ip,
user_agent: req.get('user-agent'),
});
res.status(401).json({
success: false,
error: {
code: 'UNAUTHORIZED',
message: 'Missing or invalid authorization header',
},
});
return;
}
const token = authHeader.split(' ')[1];
const { data: { user }, error } = await supabase.auth.getUser(token);
if (error || !user) {
logActivity({
action: 'INVALID_TOKEN',
details: `Invalid/expired token on ${req.method} ${req.originalUrl}: ${error?.message || 'no user'}`,
ip: req.ip,
user_agent: req.get('user-agent'),
});
res.status(401).json({
success: false,
error: {
code: 'UNAUTHORIZED',
message: 'Invalid or expired token',
},
});
return;
}
// Check if user is banned
const { data: profile } = await supabase
.from('profiles')
.select('is_banned')
.eq('id', user.id)
.single();
if (profile?.is_banned) {
logActivity({
user_id: user.id,
action: 'BANNED_ACCESS_ATTEMPT',
details: `Banned user accessed ${req.method} ${req.originalUrl}`,
ip: req.ip,
user_agent: req.get('user-agent'),
});
res.status(403).json({
success: false,
error: {
code: 'ACCOUNT_BANNED',
message: 'Your account has been suspended',
},
});
return;
}
req.userId = user.id;
req.userEmail = user.email;
next();
} catch {
res.status(500).json({
success: false,
error: {
code: 'INTERNAL_ERROR',
message: 'Authentication failed',
},
});
}
};
export const errorHandler = (err: Error, _req: Request, res: Response, _next: NextFunction) => {
// Log full error server-side only — never expose to client
logError(`Unhandled error: ${err.constructor.name}`, 'auth');
res.status(500).json({
success: false,
error: {
code: 'INTERNAL_ERROR',
message: 'An unexpected error occurred',
},
});
};