Files
linumiq_net-web_app/lib/auth/admin-guard.ts
T

50 lines
1.5 KiB
TypeScript

import { redirect } from 'next/navigation';
import type { NextResponse } from 'next/server';
import type { User } from '@supabase/supabase-js';
import { createSupabaseServerClient } from '@/lib/supabase/server';
import { jsonNoStore } from '@/lib/admin/response';
export function isAdmin(user: User | null | undefined): boolean {
return user?.app_metadata?.role === 'admin';
}
/**
* For server components / pages. Redirects to /login if unauthenticated,
* or /dashboard if authenticated but not an admin (does not leak /admin).
*/
export async function requireAdmin(): Promise<User> {
const supabase = createSupabaseServerClient();
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) redirect('/login');
if (!isAdmin(user)) redirect('/dashboard');
return user;
}
/**
* For route handlers. Returns the authenticated admin user, or a NextResponse
* (401 when no session, 403 when not admin) that the caller must return.
*/
export async function requireAdminApi(): Promise<
{ ok: true; user: User } | { ok: false; response: NextResponse }
> {
const supabase = createSupabaseServerClient();
const {
data: { user },
} = await supabase.auth.getUser();
if (!user) {
return {
ok: false,
response: jsonNoStore({ error: 'unauthorized' }, { status: 401 }),
};
}
if (!isAdmin(user)) {
return {
ok: false,
response: jsonNoStore({ error: 'forbidden' }, { status: 403 }),
};
}
return { ok: true, user };
}