wiki_ghostguild/app/server/utils/auth.ts

100 lines
2.2 KiB
TypeScript

import jwt from "jsonwebtoken";
import type { H3Event } from "h3";
import { User } from "../models/User";
export interface AuthUser {
userId: string;
username: string;
roles: string[];
permissions: {
canEdit: boolean;
canModerate: boolean;
canAdmin: boolean;
};
}
export async function verifyAuth(event: H3Event): Promise<AuthUser | null> {
const config = useRuntimeConfig();
const token = getCookie(event, "auth-token");
if (!token) {
return null;
}
try {
const decoded = jwt.verify(token, config.jwtSecret as string) as AuthUser;
return decoded;
} catch (error) {
return null;
}
}
export async function requireAuth(event: H3Event): Promise<AuthUser> {
const user = await verifyAuth(event);
if (!user) {
throw createError({
statusCode: 401,
statusMessage: "Authentication required",
});
}
return user;
}
export async function requireRole(
event: H3Event,
requiredRoles: string[],
): Promise<AuthUser> {
const user = await requireAuth(event);
const hasRole = requiredRoles.some((role) => user.roles.includes(role));
if (!hasRole) {
throw createError({
statusCode: 403,
statusMessage: "Insufficient permissions",
});
}
return user;
}
export async function checkAccessLevel(
event: H3Event,
accessLevel: "public" | "member" | "cohort" | "admin",
cohorts?: string[],
): Promise<boolean> {
// Public content is always accessible
if (accessLevel === "public") {
return true;
}
const user = await verifyAuth(event);
// No user = no access to protected content
if (!user) {
return false;
}
// Check access levels
switch (accessLevel) {
case "member":
// Any authenticated user has member access
return true;
case "cohort":
// Check if user belongs to required cohorts
if (!cohorts || cohorts.length === 0) {
return true; // No specific cohort required
}
return cohorts.some((cohort) => user.roles.includes(`cohort-${cohort}`));
case "admin":
// Only admins have admin access
return user.permissions.canAdmin || user.roles.includes("admin");
default:
return false;
}
}