60e7e43e94
Co-authored-by: wolfoftyreso-debug <250630591+wolfoftyreso-debug@users.noreply.github.com>
75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
import { createClient } from "https://esm.sh/@supabase/supabase-js@2.45.0";
|
|
|
|
/**
|
|
* Verifies the caller is an authenticated admin.
|
|
* Returns { ok: true } on success or { ok: false, response } with a
|
|
* ready-to-return Response on failure.
|
|
*/
|
|
export async function requireAdmin(
|
|
req: Request,
|
|
corsHeaders: Record<string, string>,
|
|
): Promise<{ ok: true; userId: string } | { ok: false; response: Response }> {
|
|
const authHeader = req.headers.get("Authorization");
|
|
if (!authHeader?.startsWith("Bearer ")) {
|
|
return {
|
|
ok: false,
|
|
response: new Response(JSON.stringify({ error: "Unauthorized" }), {
|
|
status: 401,
|
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
}),
|
|
};
|
|
}
|
|
|
|
const supabaseUrl = Deno.env.get("SUPABASE_URL");
|
|
const anonKey = Deno.env.get("SUPABASE_ANON_KEY");
|
|
const serviceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY");
|
|
if (!supabaseUrl || !anonKey || !serviceKey) {
|
|
return {
|
|
ok: false,
|
|
response: new Response(JSON.stringify({ error: "Server misconfigured" }), {
|
|
status: 500,
|
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
}),
|
|
};
|
|
}
|
|
|
|
const supabase = createClient(supabaseUrl, anonKey, {
|
|
global: { headers: { Authorization: authHeader } },
|
|
});
|
|
|
|
const token = authHeader.replace("Bearer ", "");
|
|
const { data, error } = await supabase.auth.getClaims(token);
|
|
if (error || !data?.claims?.sub) {
|
|
return {
|
|
ok: false,
|
|
response: new Response(JSON.stringify({ error: "Unauthorized" }), {
|
|
status: 401,
|
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
}),
|
|
};
|
|
}
|
|
|
|
const userId = data.claims.sub as string;
|
|
|
|
// Verify admin role via service-role client (bypasses RLS deterministically)
|
|
const admin = createClient(supabaseUrl, serviceKey);
|
|
const { data: role, error: roleErr } = await admin
|
|
.from("user_roles")
|
|
.select("role")
|
|
.eq("user_id", userId)
|
|
.eq("role", "admin")
|
|
.maybeSingle();
|
|
|
|
if (roleErr || !role) {
|
|
return {
|
|
ok: false,
|
|
response: new Response(JSON.stringify({ error: "Forbidden" }), {
|
|
status: 403,
|
|
headers: { ...corsHeaders, "Content-Type": "application/json" },
|
|
}),
|
|
};
|
|
}
|
|
|
|
return { ok: true, userId };
|
|
}
|