From 9b784791bbdc7b74cdabf3e59c45e7628f0aad44 Mon Sep 17 00:00:00 2001 From: Martin DONADIEU Date: Fri, 14 Nov 2025 11:37:17 +0100 Subject: [PATCH 01/13] Document region runtime information for functions (#40280) * Document region runtime information for functions Added section on region runtime information and environment variables. * Fix spelling and capitalization in regional invocation guide Corrected spelling and capitalization errors in the documentation. --- .../content/guides/functions/regional-invocation.mdx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/docs/content/guides/functions/regional-invocation.mdx b/apps/docs/content/guides/functions/regional-invocation.mdx index b2bd30ddb14c8..dd59ecb023b94 100644 --- a/apps/docs/content/guides/functions/regional-invocation.mdx +++ b/apps/docs/content/guides/functions/regional-invocation.mdx @@ -79,6 +79,16 @@ You can verify the execution region by looking at the `x-sb-edge-region` HTTP he --- +## Region runtime information + +Functions have access to the following environment variables: + +SB_REGION: The AWS region function was invoked + +This is useful if you have read replicate and want to Postgres connect to a different replicate according of the Region. + +--- + ## Region outages When you explicitly specify a region via the `x-region` header, requests will NOT be automatically From 27f8143b6663ab65ba86e489678f5a4c12ecb05e Mon Sep 17 00:00:00 2001 From: Kalleby Santos <105971119+kallebysantos@users.noreply.github.com> Date: Fri, 14 Nov 2025 10:45:38 +0000 Subject: [PATCH 02/13] chore: add custom-jwt-validation example (#40438) --- .../supabase/functions/_shared/jwt/clerk.ts | 60 +++++++++++++++++++ .../supabase/functions/_shared/jwt/default.ts | 55 +++++++++++++++++ .../functions/custom-jwt-validation/README.md | 21 +++++++ .../functions/custom-jwt-validation/index.ts | 19 ++++++ 4 files changed, 155 insertions(+) create mode 100644 examples/edge-functions/supabase/functions/_shared/jwt/clerk.ts create mode 100644 examples/edge-functions/supabase/functions/_shared/jwt/default.ts create mode 100644 examples/edge-functions/supabase/functions/custom-jwt-validation/README.md create mode 100644 examples/edge-functions/supabase/functions/custom-jwt-validation/index.ts diff --git a/examples/edge-functions/supabase/functions/_shared/jwt/clerk.ts b/examples/edge-functions/supabase/functions/_shared/jwt/clerk.ts new file mode 100644 index 0000000000000..8c170902010e1 --- /dev/null +++ b/examples/edge-functions/supabase/functions/_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/edge-functions/supabase/functions/_shared/jwt/default.ts b/examples/edge-functions/supabase/functions/_shared/jwt/default.ts new file mode 100644 index 0000000000000..a7b8df5e3c874 --- /dev/null +++ b/examples/edge-functions/supabase/functions/_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/edge-functions/supabase/functions/custom-jwt-validation/README.md b/examples/edge-functions/supabase/functions/custom-jwt-validation/README.md new file mode 100644 index 0000000000000..02af65755f7bc --- /dev/null +++ b/examples/edge-functions/supabase/functions/custom-jwt-validation/README.md @@ -0,0 +1,21 @@ +# 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/supabase/blob/main/examples/edge-functions/supabase/functions/_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/supabase/refs/heads/main/examples/edge-functions/supabase/functions/_shared/jwt/