Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions examples/edge-functions/supabase/functions/_shared/jwt/clerk.ts
Original file line number Diff line number Diff line change
@@ -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<boolean> {
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<Response>,
) {
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,
});
}
}
55 changes: 55 additions & 0 deletions examples/edge-functions/supabase/functions/_shared/jwt/default.ts
Original file line number Diff line number Diff line change
@@ -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<boolean> {
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<Response>,
) {
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,
});
}
}
Original file line number Diff line number Diff line change
@@ -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/<template>.ts
```

2. Add any required Environment-Variable to a `.env` file, see inside of the
respective `_shared/jwt/template.ts` file to find which variables is required.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Using 'default' Supabase auth middleware
// Change to a specific provider by importing like that:
// import { AuthMiddleware } from "../_shared/jwt/clerk.ts";
import { AuthMiddleware } from "../_shared/jwt/default.ts";

interface reqPayload {
name: string;
}

Deno.serve((r) =>
AuthMiddleware(r, async (req) => {
const { name }: reqPayload = await req.json();
const data = {
message: `Hello ${name} from foo!`,
};

return Response.json(data);
})
);
Loading