170 lines
3.6 KiB
TypeScript
170 lines
3.6 KiB
TypeScript
|
|
import { headers } from "next/headers";
|
||
|
|
import { NextResponse } from "next/server";
|
||
|
|
import { auth } from "./auth";
|
||
|
|
import { hasPermission, type Permission } from "./permissions";
|
||
|
|
|
||
|
|
// Extended user type with custom fields
|
||
|
|
type UserWithRole = {
|
||
|
|
id: string;
|
||
|
|
email: string;
|
||
|
|
name: string;
|
||
|
|
role?: string;
|
||
|
|
requires_setup?: boolean;
|
||
|
|
[key: string]: unknown;
|
||
|
|
};
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Get the current session from the request headers
|
||
|
|
* Use this in API routes and server components
|
||
|
|
*/
|
||
|
|
export async function getSession() {
|
||
|
|
const session = await auth.api.getSession({
|
||
|
|
headers: await headers(),
|
||
|
|
});
|
||
|
|
return session;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Require authentication for an API route
|
||
|
|
* Returns the session if authenticated, or a 401 response if not
|
||
|
|
*/
|
||
|
|
export async function requireAuth() {
|
||
|
|
const session = await getSession();
|
||
|
|
|
||
|
|
if (!session) {
|
||
|
|
return {
|
||
|
|
session: null,
|
||
|
|
error: NextResponse.json(
|
||
|
|
{ error: "Unauthorized" },
|
||
|
|
{ status: 401 }
|
||
|
|
),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
return { session, error: null };
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Require specific permissions for an API route
|
||
|
|
* Returns the session if authorized, or a 403 response if not
|
||
|
|
*/
|
||
|
|
export async function requirePermission(
|
||
|
|
resource: Permission["resource"],
|
||
|
|
action: string
|
||
|
|
) {
|
||
|
|
const { session, error } = await requireAuth();
|
||
|
|
|
||
|
|
if (error) {
|
||
|
|
return { session: null, error };
|
||
|
|
}
|
||
|
|
|
||
|
|
const userRole = (session!.user as UserWithRole).role || "user";
|
||
|
|
|
||
|
|
if (!hasPermission(userRole, resource, action)) {
|
||
|
|
return {
|
||
|
|
session: null,
|
||
|
|
error: NextResponse.json(
|
||
|
|
{ error: "Forbidden" },
|
||
|
|
{ status: 403 }
|
||
|
|
),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
return { session, error: null };
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Require admin or super-admin role
|
||
|
|
*/
|
||
|
|
export async function requireAdmin() {
|
||
|
|
const { session, error } = await requireAuth();
|
||
|
|
|
||
|
|
if (error) {
|
||
|
|
return { session: null, error };
|
||
|
|
}
|
||
|
|
|
||
|
|
const userRole = (session!.user as UserWithRole).role || "user";
|
||
|
|
|
||
|
|
if (userRole !== "admin" && userRole !== "super-admin") {
|
||
|
|
return {
|
||
|
|
session: null,
|
||
|
|
error: NextResponse.json(
|
||
|
|
{ error: "Forbidden - Admin access required" },
|
||
|
|
{ status: 403 }
|
||
|
|
),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
return { session, error: null };
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Require super-admin role
|
||
|
|
*/
|
||
|
|
export async function requireSuperAdmin() {
|
||
|
|
const { session, error } = await requireAuth();
|
||
|
|
|
||
|
|
if (error) {
|
||
|
|
return { session: null, error };
|
||
|
|
}
|
||
|
|
|
||
|
|
const userRole = (session!.user as UserWithRole).role || "user";
|
||
|
|
|
||
|
|
if (userRole !== "super-admin") {
|
||
|
|
return {
|
||
|
|
session: null,
|
||
|
|
error: NextResponse.json(
|
||
|
|
{ error: "Forbidden - Super admin access required" },
|
||
|
|
{ status: 403 }
|
||
|
|
),
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
return { session, error: null };
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if the current user has a specific permission
|
||
|
|
* Use this in server components for conditional rendering
|
||
|
|
*/
|
||
|
|
export async function checkPermission(
|
||
|
|
resource: Permission["resource"],
|
||
|
|
action: string
|
||
|
|
): Promise<boolean> {
|
||
|
|
const session = await getSession();
|
||
|
|
|
||
|
|
if (!session) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
const userRole = (session.user as UserWithRole).role || "user";
|
||
|
|
return hasPermission(userRole, resource, action);
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if the current user is an admin or super-admin
|
||
|
|
*/
|
||
|
|
export async function isAdmin(): Promise<boolean> {
|
||
|
|
const session = await getSession();
|
||
|
|
|
||
|
|
if (!session) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
const userRole = (session.user as UserWithRole).role || "user";
|
||
|
|
return userRole === "admin" || userRole === "super-admin";
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check if the current user is a super-admin
|
||
|
|
*/
|
||
|
|
export async function isSuperAdmin(): Promise<boolean> {
|
||
|
|
const session = await getSession();
|
||
|
|
|
||
|
|
if (!session) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
return (session.user as UserWithRole).role === "super-admin";
|
||
|
|
}
|