diff --git a/src/Middleware/auth/auth.ts b/src/Middleware/auth/auth.ts new file mode 100644 index 0000000..ae8e63b --- /dev/null +++ b/src/Middleware/auth/auth.ts @@ -0,0 +1,88 @@ +import { NextFunction, Request, Response } from "express"; +import { AuthJWTPayload } from "../../Controllers/admin_auth.controller"; +import { authClient } from "../../lib/redis"; +import jwt, { JsonWebTokenError, JwtPayload } from "jsonwebtoken"; +import prisma from "../../lib/prisma"; + +const JWT_SECRET = process.env.JWT_SECRET || "secret"; + +const verifyAuthorizationFormat = (authorization: string) => /^Bearer .+$/.test(authorization); + +const getBearerToken = (authorization: string) => authorization.slice(7); + +const requireAuthentication = async (req: Request, res: Response, next: NextFunction) => { + const { authorization } = req.headers; + + if (!authorization) { + return res.status(403).send({ + type: "error", + payload: { + message: "The requeset did not include the Authorization header", + }, + }); + } + + if (!verifyAuthorizationFormat(authorization)) { + return res.status(400).send({ + type: "error", + payload: { + message: "Malformed Authorization header", + format: "Bearer ", + }, + }); + } + + let token_payload_: string | JwtPayload; + + try { + token_payload_ = jwt.verify(getBearerToken(authorization), JWT_SECRET); + } catch (e) { + if (e instanceof JsonWebTokenError) { + return res.status(403).json({ + type: "error", + payload: { + message: "Token could not be verified; It might be expired", + }, + }); + } + + throw e; + } + + const token_payload = token_payload_ as AuthJWTPayload; + + const { pid, revision } = token_payload; + + let db_revision = await authClient.get(pid); + + if (db_revision === null) { + // Load the revision ID from the main DB and cache it in redis + const user = await prisma.admin.findUnique({ where: { pid }, select: { revision: true } }); + + if (user) { + db_revision = user.revision; + + await authClient.set(pid, db_revision); + } + } + + if (revision !== db_revision || !revision || !db_revision) { + return res.status(403).json({ + type: "error", + payload: { + message: "Token could not be verified; It might be expired", + }, + }); + } + + req.auth = { + isAuthenticated: true, + pid: token_payload.pid, + name: token_payload.name, + permission_level: token_payload.permission_level, + groups: token_payload.groups, + revision: token_payload.revision, + }; + + next(); +}; diff --git a/src/custom.d.ts b/src/custom.d.ts new file mode 100644 index 0000000..bb6296a --- /dev/null +++ b/src/custom.d.ts @@ -0,0 +1,7 @@ +import { AuthJWTPayload } from "./Controllers/admin_auth.controller"; + +declare module "express-serve-static-core" { + interface Request { + auth?: AuthJWTPayload & { isAuthenticated: boolean }; + } +}