feat: add JWT utility and requireAuth/requireCampaignRole middleware
This commit is contained in:
parent
39f8220eb7
commit
bd433286ae
2 changed files with 66 additions and 0 deletions
19
server/src/auth/jwt.ts
Normal file
19
server/src/auth/jwt.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import jwt from "jsonwebtoken";
|
||||
|
||||
const SECRET = process.env.JWT_SECRET ?? "dev_jwt_secret_change_me";
|
||||
export const COOKIE_NAME = "darkwatch_token";
|
||||
const EXPIRY = "7d";
|
||||
|
||||
export interface JWTPayload {
|
||||
userId: number;
|
||||
email: string;
|
||||
username: string;
|
||||
}
|
||||
|
||||
export function signToken(payload: JWTPayload): string {
|
||||
return jwt.sign(payload, SECRET, { expiresIn: EXPIRY });
|
||||
}
|
||||
|
||||
export function verifyToken(token: string): JWTPayload {
|
||||
return jwt.verify(token, SECRET) as JWTPayload;
|
||||
}
|
||||
47
server/src/auth/middleware.ts
Normal file
47
server/src/auth/middleware.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import type { Request, Response, NextFunction } from "express";
|
||||
import type { RowDataPacket } from "mysql2";
|
||||
import db from "../db.js";
|
||||
import { verifyToken, COOKIE_NAME } from "./jwt.js";
|
||||
import type { JWTPayload } from "./jwt.js";
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
user?: JWTPayload;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function requireAuth(req: Request, res: Response, next: NextFunction): void {
|
||||
const token = req.cookies?.[COOKIE_NAME];
|
||||
if (!token) {
|
||||
res.status(401).json({ error: "Unauthorized" });
|
||||
return;
|
||||
}
|
||||
try {
|
||||
req.user = verifyToken(token);
|
||||
next();
|
||||
} catch {
|
||||
res.status(401).json({ error: "Unauthorized" });
|
||||
}
|
||||
}
|
||||
|
||||
export function requireCampaignRole(role: "dm" | "player") {
|
||||
return async (req: Request, res: Response, next: NextFunction): Promise<void> => {
|
||||
const campaignId = req.params.campaignId ?? req.params.id;
|
||||
const userId = req.user!.userId;
|
||||
const [rows] = await db.execute<RowDataPacket[]>(
|
||||
"SELECT role FROM campaign_members WHERE campaign_id = ? AND user_id = ?",
|
||||
[campaignId, userId]
|
||||
);
|
||||
if (rows.length === 0) {
|
||||
res.status(403).json({ error: "Not a campaign member" });
|
||||
return;
|
||||
}
|
||||
if (role === "dm" && rows[0].role !== "dm") {
|
||||
res.status(403).json({ error: "DM access required" });
|
||||
return;
|
||||
}
|
||||
next();
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue