Last active
April 8, 2024 12:40
-
-
Save raae/166821dea48368da65027ea1f207a54a to your computer and use it in GitHub Desktop.
Supabase Edge Function to exchange an Outseta-signed JWT with a Supabase-signed JWT
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Deploy as a Supabase function with --no-verify-jwt | |
// as we are providing an Outseta token, not a Supabase token | |
// command: supabase functions deploy exchange --no-verify-jwt | |
import * as jose from "https://deno.land/x/jose@v4.14.4/index.ts"; | |
const corsHeaders = { | |
"Access-Control-Allow-Origin": "*", | |
"Access-Control-Allow-Headers": | |
"authorization, x-client-info, apikey, content-type", | |
}; | |
Deno.serve(async (req) => { | |
if (req.method === "OPTIONS") { | |
console.log("OPTIONS request"); | |
return new Response("ok", { headers: corsHeaders }); | |
} | |
if (req.method === "GET") { | |
console.log("GET request"); | |
return new Response("Method not allowed", { | |
headers: corsHeaders, | |
status: 405, | |
}); | |
} | |
const outsetaDomain = Deno.env.get("OUTSETA_DOMAIN"); | |
if (!outsetaDomain) { | |
throw new Error("No OUTSETA_DOMAIN env variable set"); | |
} | |
const supabaseJwtSecret = Deno.env.get("SUPA_JWT_SECRET"); | |
if (!supabaseJwtSecret) { | |
throw new Error("No SUPA_JWT_SECRET env variable set"); | |
} | |
// Get the Outseta signed JWT from the Authorization header | |
const authHeader = req.headers.get("Authorization"); | |
const outsetaJwtAccessToken = authHeader?.split(" ")[1] || ""; | |
if (!outsetaJwtAccessToken) { | |
throw new Error("No Outseta JWT provided"); | |
} | |
try { | |
console.log("Verify Outseta JWT"); | |
const JWKS = jose.createRemoteJWKSet( | |
new URL(`https://${outsetaDomain}/.well-known/jwks`) | |
); | |
// Use the JSON Web Key (JWK) to verify the token | |
const { payload } = await jose.jwtVerify(outsetaJwtAccessToken, JWKS); | |
console.log("JWT is valid"); | |
console.log("Payload:", payload); | |
// Update the JWT for Supabase and sign with the Supabase secret | |
payload.role = "authenticated"; | |
const supabaseEncodedJwtSecret = new TextEncoder().encode( | |
supabaseJwtSecret | |
); | |
const jwtAlg = "HS256"; | |
const supabaseJwt = await new jose.SignJWT(payload) | |
.setProtectedHeader({ alg: jwtAlg, typ: "JWT" }) | |
.setIssuer("supabase") | |
.setIssuedAt(payload.iat) | |
.setExpirationTime(payload.exp || "") | |
.sign(supabaseEncodedJwtSecret); | |
console.log("Supabase Signed JWT:", supabaseJwt); | |
// Respond with the new Supabase JWT | |
return new Response(JSON.stringify({ supabaseJwt }), { | |
headers: { ...corsHeaders, "Content-Type": "application/json" }, | |
status: 200, | |
}); | |
} catch (error) { | |
console.error(error.message, { | |
outsetaJwtAccessToken, | |
}); | |
return new Response(JSON.stringify({ error: "Invalid" }), { | |
headers: { ...corsHeaders, "Content-Type": "application/json" }, | |
status: 401, | |
}); | |
} | |
}); |
Remember to use --no-verify-jwt
when deploying:
supabase functions deploy exchange --no-verify-jwt
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Then, one can do the below in policies:
((auth.jwt() ->> 'outseta:accountUid'::text) = (account_uid)::text)
-> to match on account uid((auth.jwt() ->> 'sub'::text) = (person_uid)::text)
-> to match on person uid