Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 12 additions & 43 deletions src/middleware/auth.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,24 @@
import { NextFunction, Request, Response } from 'express';
import jwt, { JwtPayload } from 'jsonwebtoken';
import { HttpError } from '../utils/httpError';
import env from '../config/env';
import { Request, Response, NextFunction } from 'express';
import authenticateCanonical from './authenticate';
import AppError from '../utils/AppError';

const jwtSecret = env.JWT_SECRET;

export interface AuthenticatedRequest extends Request {
user?: JwtPayload & {
role?: string;
sub?: string;
};
}
export type AuthenticatedRequest = Request & {
user?: { role?: string; userId?: string; id?: string; _id?: unknown };
};

export const authenticate = (
/** Canonical database-backed authentication, retained under the old import. */
export const authenticate = authenticateCanonical as (
req: AuthenticatedRequest,
res: Response,
next: NextFunction,
): void => {
const authHeader = req.headers.authorization;

if (!authHeader || !authHeader.startsWith('Bearer ')) {
next(new HttpError(401, 'Authorization header missing or malformed'));
return;
}

const token = authHeader.split(' ')[1];

try {
const payload = jwt.verify(token, jwtSecret);

if (typeof payload === 'string') {
next(new HttpError(401, 'Invalid authorization token'));
return;
}

req.user = payload;
next();
} catch (error) {
next(new HttpError(401, 'Invalid or expired authorization token'));
}
};
) => void;

export const authorize = (allowedRoles: string[] = ['driver', 'admin']) => {
return (req: AuthenticatedRequest, res: Response, next: NextFunction): void => {
const role = req.user?.role;

if (!role || !allowedRoles.includes(role)) {
next(new HttpError(403, 'Insufficient permissions to perform this action'));
return (req: AuthenticatedRequest, _res: Response, next: NextFunction): void => {
if (!req.user?.role || !allowedRoles.includes(String(req.user.role))) {
next(new AppError('Insufficient permissions to perform this action', 403));
return;
}

next();
};
};
63 changes: 3 additions & 60 deletions src/middlewares/authMiddleware.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,3 @@
import { Request, Response, NextFunction } from 'express';
import jwt, { JwtPayload } from 'jsonwebtoken';
import { StatusCodes } from 'http-status-codes';
import AppError from '../utils/AppError';
import env from '../config/env';

export interface AuthenticatedRequest extends Request {
user?: JwtPayload & {
userId?: string;
id?: string;
role?: string;
};
}

/**
* Verifies the `Authorization: Bearer <token>` header and attaches the
* decoded JWT payload to `req.user` for downstream handlers.
*
* Throws 401 if the header is missing/malformed or the token is invalid/expired.
*/
export const authMiddleware = (
req: AuthenticatedRequest,
_res: Response,
next: NextFunction,
): void => {
const authHeader = req.headers.authorization;

if (!authHeader || !authHeader.startsWith('Bearer ')) {
next(
new AppError(
'Authentication required. Please provide a valid Bearer token.',
StatusCodes.UNAUTHORIZED,
),
);
return;
}

const token = authHeader.split(' ')[1];

if (!token) {
next(new AppError('Authentication token missing.', StatusCodes.UNAUTHORIZED));
return;
}

try {
const decoded = jwt.verify(token, env.JWT_SECRET);

if (typeof decoded === 'string') {
next(new AppError('Invalid authorization token.', StatusCodes.UNAUTHORIZED));
return;
}

req.user = decoded;
next();
} catch (error) {
next(new AppError('Invalid or expired token. Please log in again.', StatusCodes.UNAUTHORIZED));
}
};

export default authMiddleware;
/** Backwards-compatible path for the canonical database-backed middleware. */
export { default, default as authMiddleware } from '../middleware/authenticate';
export type { AuthenticatedRequest } from '../middleware/auth';
Loading