import { createRemoteJWKSet, jwtVerify, type JWTPayload } from "jose"; export interface SupabaseClaims extends JWTPayload { sub: string; email?: string; role?: string; } export class JwtVerificationError extends Error { constructor(message: string, readonly cause?: unknown) { super(message); this.name = "JwtVerificationError"; } } /** * Verifies a Supabase-issued access token against the project's public * JWKS (fetched once and cached). `supabaseUrl` is your project URL, e.g. * `https://xyzcompany.supabase.co`. */ export function createSupabaseJwtVerifier(supabaseUrl: string) { const jwks = createRemoteJWKSet(new URL("/auth/v1/.well-known/jwks.json", supabaseUrl)); return { async verify(token: string): Promise { try { const { payload } = await jwtVerify(token, jwks, { audience: "authenticated" }); return payload as SupabaseClaims; } catch (err) { throw new JwtVerificationError("failed to verify Supabase JWT", err); } }, }; } export function extractBearerToken(authorizationHeader: string | null | undefined): string { const [scheme, token] = (authorizationHeader ?? "").split(" "); if (scheme !== "Bearer" || !token) { throw new JwtVerificationError("Authorization header is not a Bearer token"); } return token; }