diff --git a/examples/_shared/jwt/clerk.ts b/examples/_shared/jwt/clerk.ts new file mode 100644 index 000000000..8c1709020 --- /dev/null +++ b/examples/_shared/jwt/clerk.ts @@ -0,0 +1,60 @@ +// Clerk as a third-party provider alongside Supabase Auth. +// Use this template to validate tokens issued by Clerk integration +import * as jose from "https://deno.land/x/jose@v4.14.4/index.ts"; + +// Obtain from https://clerk.com/setup/supabase +// Must supply this value from function env +const AUTH_THIRD_PARTY_CLERK_DOMAIN = Deno.env.get( + "AUTH_THIRD_PARTY_CLERK_DOMAIN", +); + +export function getAuthToken(req: Request) { + const authHeader = req.headers.get("authorization"); + if (!authHeader) { + throw new Error("Missing authorization header"); + } + const [bearer, token] = authHeader.split(" "); + if (bearer !== "Bearer") { + throw new Error(`Auth header is not 'Bearer {token}'`); + } + return token; +} + +async function verifyJWT(jwt: string): Promise { + try { + const JWK = jose.createRemoteJWKSet( + new URL(AUTH_THIRD_PARTY_CLERK_DOMAIN ?? ""), + ); + await jose.jwtVerify(jwt, JWK, { + algorithms: ["RS256"], + }); + } catch (err) { + console.error(err); + return false; + } + return true; +} + +// Validates authorization header +export async function AuthMiddleware( + req: Request, + next: (req: Request) => Promise, +) { + if (req.method === "OPTIONS") return await next(req); + + try { + const token = getAuthToken(req); + const isValidJWT = await verifyJWT(token); + + if (isValidJWT) return await next(req); + + return Response.json({ msg: "Invalid JWT" }, { + status: 401, + }); + } catch (e) { + console.error(e); + return Response.json({ msg: e?.toString() }, { + status: 401, + }); + } +} diff --git a/examples/_shared/jwt/default.ts b/examples/_shared/jwt/default.ts new file mode 100644 index 000000000..a7b8df5e3 --- /dev/null +++ b/examples/_shared/jwt/default.ts @@ -0,0 +1,55 @@ +// Default supabase JWT verification +// Use this template to validate tokens issued by Supabase default auth + +import * as jose from "https://deno.land/x/jose@v4.14.4/index.ts"; + +// Automatically supplied by Supabase +const JWT_SECRET = Deno.env.get("JWT_SECRET"); + +export function getAuthToken(req: Request) { + const authHeader = req.headers.get("authorization"); + if (!authHeader) { + throw new Error("Missing authorization header"); + } + const [bearer, token] = authHeader.split(" "); + if (bearer !== "Bearer") { + throw new Error(`Auth header is not 'Bearer {token}'`); + } + return token; +} + +async function verifyJWT(jwt: string): Promise { + const encoder = new TextEncoder(); + const secretKey = encoder.encode(JWT_SECRET); + try { + await jose.jwtVerify(jwt, secretKey); + } catch (err) { + console.error(err); + return false; + } + return true; +} + +// Validates authorization header +export async function AuthMiddleware( + req: Request, + next: (req: Request) => Promise, +) { + if (req.method === "OPTIONS") return await next(req); + + try { + const token = getAuthToken(req); + const isValidJWT = await verifyJWT(token); + + if (isValidJWT) return await next(req); + + return Response.json({ msg: "Invalid JWT" }, { + status: 401, + }); + } catch (e) { + console.error(e); + return Response.json({ msg: e?.toString() }, { + status: 401, + }); + } +} diff --git a/examples/custom-jwt-validation/README.md b/examples/custom-jwt-validation/README.md new file mode 100644 index 000000000..e48b478a6 --- /dev/null +++ b/examples/custom-jwt-validation/README.md @@ -0,0 +1,20 @@ +# custom-jwt-validation + +This function exemplifies how to use a custom JWT validation. + +Since Supabase legacy JWT Secret will be deprecated, users that would like to verify JWT or integrate with a custom provider should implement it manually. +> see [Upcoming changes to Supabase API Keys #29260](https://github.com/orgs/supabase/discussions/29260) + +To simplify this task, Supabase provides a collection of JWT validation examples +that can be found at [`_shared/jwt/`](https://github.com/supabase/edge-runtime/blob/main/examples/_shared/jwt) folder. + +## Setup + +1. Copy/download the JWT template, then import and use it inside your edge function. + +```bash +wget https://raw.githubusercontent.com/supabase/edge-runtime/refs/heads/main/examples/_shared/jwt/