import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; import { handleOptions, json } from "../_shared/http.ts"; const supabaseUrl = Deno.env.get("SUPABASE_URL"); const supabaseAnonKey = Deno.env.get("SUPABASE_ANON_KEY"); if (!supabaseUrl || !supabaseAnonKey) { throw new Error("SUPABASE_URL and SUPABASE_ANON_KEY are required"); } type JwtClaims = { sub?: string; aud?: string | string[]; role?: string; email?: string; exp?: number; iat?: number; nbf?: number; iss?: string; [key: string]: unknown; }; function parseJwtClaims(token: string): { claims: JwtClaims | null; decodeError: string | null; partsCount: number; } { const parts = token.split("."); if (parts.length !== 3) { return { claims: null, decodeError: `JWT must have 3 parts, got ${parts.length}`, partsCount: parts.length, }; } try { const b64 = parts[1].replace(/-/g, "+").replace(/_/g, "/"); const padLen = (4 - (b64.length % 4)) % 4; const padded = b64 + "=".repeat(padLen); const decoded = atob(padded); const claims = JSON.parse(decoded) as JwtClaims; return { claims, decodeError: null, partsCount: parts.length }; } catch (error) { return { claims: null, decodeError: error instanceof Error ? error.message : "Unknown decode error", partsCount: parts.length, }; } } function unixNow() { return Math.floor(Date.now() / 1000); } Deno.serve(async (req) => { const preflight = handleOptions(req); if (preflight) return preflight; if (req.method !== "GET" && req.method !== "POST") { return json({ error: "Method not allowed" }, 405); } const authHeader = req.headers.get("Authorization"); const bearerPrefixOk = authHeader?.startsWith("Bearer ") ?? false; const token = bearerPrefixOk ? authHeader!.slice(7).trim() : ""; const tokenPresent = token.length > 0; const jwtParse = tokenPresent ? parseJwtClaims(token) : { claims: null, decodeError: "Missing bearer token", partsCount: 0 }; const claims = jwtParse.claims; const now = unixNow(); const authClient = createClient(supabaseUrl, supabaseAnonKey, { global: { headers: { Authorization: authHeader ?? "" } }, }); const { data: userData, error: userError } = await authClient.auth.getUser(); const checks = { authorizationHeaderPresent: authHeader != null, bearerPrefixOk, tokenPresent, tokenLength: token.length, tokenPreview: tokenPresent ? `${token.slice(0, 16)}...` : null, jwtPartsCount: jwtParse.partsCount, jwtDecodeError: jwtParse.decodeError, claimSub: claims?.sub ?? null, claimRole: claims?.role ?? null, claimAud: claims?.aud ?? null, claimEmail: claims?.email ?? null, claimIssuer: claims?.iss ?? null, claimExp: claims?.exp ?? null, claimIat: claims?.iat ?? null, claimNbf: claims?.nbf ?? null, nowUnix: now, isExpired: typeof claims?.exp === "number" ? claims.exp <= now : null, notYetValid: typeof claims?.nbf === "number" ? claims.nbf > now : null, authGetUserOk: !!userData.user && !userError, authGetUserError: userError?.message ?? null, authGetUserUserId: userData.user?.id ?? null, authGetUserEmail: userData.user?.email ?? null, }; return json({ function: "auth-debug", verifyJwtDisabled: true, checks, }); });