mirror of
https://github.com/detleph/server.git
synced 2026-09-04 16:46:04 +02:00
84 lines
2.2 KiB
TypeScript
84 lines
2.2 KiB
TypeScript
import { Request, Response } from "express";
|
|
import { Admin, AdminLevel, Group } from "@prisma/client";
|
|
import { authClient } from "../lib/redis";
|
|
import prisma from "../lib/prisma";
|
|
import argon2 from "argon2";
|
|
import jwt from "jsonwebtoken";
|
|
|
|
const JWT_SECRET = process.env.JWT_SECRET || "secret";
|
|
const TOKEN_EXPIRY = "4 days";
|
|
|
|
export interface AuthJWTPayload {
|
|
pid: string;
|
|
name: string;
|
|
permission_level: AdminLevel;
|
|
revision: string;
|
|
groups: string[];
|
|
}
|
|
|
|
function createAdminJWT(admin: Admin & { groups: Group[] }) {
|
|
const payload: AuthJWTPayload = {
|
|
pid: admin.pid,
|
|
name: admin.name,
|
|
permission_level: admin.permission_level,
|
|
revision: admin.revision,
|
|
groups: admin.groups.map((group) => group.pid),
|
|
};
|
|
|
|
return jwt.sign(payload, JWT_SECRET, { expiresIn: TOKEN_EXPIRY });
|
|
}
|
|
|
|
interface AuthenticateUserBody {
|
|
name?: string;
|
|
password?: string;
|
|
}
|
|
|
|
export const authenticateUser = async (req: Request<{}, {}, AuthenticateUserBody>, res: Response) => {
|
|
// TODO: Provide more useful error messages (Maybe express-validator?)
|
|
|
|
const { name, password } = req.body || {};
|
|
|
|
if (name == null || password == null) {
|
|
return res.status(400).json({
|
|
type: "error",
|
|
payload: {
|
|
message: "The body must contain 'name' and 'password' attributes of type string",
|
|
},
|
|
});
|
|
}
|
|
|
|
const user = await prisma.admin.findFirst({ where: { name }, include: { groups: true } });
|
|
|
|
// REVIEW: It is possible to initiate a timing attack here (To see which users exist)
|
|
// This should, however, not be of too much concern, as one can basically do nothing
|
|
// with only the username
|
|
|
|
if (!user) {
|
|
return res.status(404).json({
|
|
type: "error",
|
|
payload: {
|
|
message: `The provided credentials are not valid`,
|
|
},
|
|
});
|
|
}
|
|
|
|
if (await argon2.verify(user.password, password, { type: argon2.argon2id })) {
|
|
// Set password revision ID in redis
|
|
await authClient.set(user.pid, user.revision);
|
|
|
|
return res.status(200).json({
|
|
type: "success",
|
|
payload: {
|
|
token: createAdminJWT(user),
|
|
},
|
|
});
|
|
}
|
|
|
|
return res.status(403).json({
|
|
type: "error",
|
|
payload: {
|
|
message: "The provided credentials are not valid",
|
|
},
|
|
});
|
|
};
|