Edge Functions

Securing Edge Functions

Best practices on securing Edge Functions


Before continuing, read the JWT Signing Keys guide for details about the main differences compared to Legacy JWTs.

Overview

When an HTTP request is sent to Edge Functions, you can use Supabase Auth to secure endpoints. In the past, this verification was controlled by the verify_jwt flag.

But, this method is incompatible with the new JWT Signing Keys and also caused trouble when attempting third-party integration.

For this reason we decided to no longer implicitly force JWT verification, but instead suggest patterns and templates to handle this task. This allows users to own and control the auth code, instead of hiding it internally under Edge Runtime infrastructure.

Integrating with Supabase Auth

Important notes to consider:

  • This is done inside the Deno.serve() callback argument, so that the Authorization header is set for each request.
  • Use Deno.env.get('SUPABASE_URL') to get the URL associated with your project. Using a value such as http://localhost:54321 for local development will fail due to Docker containerization.

Get API details

Now that you've created some database tables, you are ready to insert data using the auto-generated API.

To do this, you need to get the Project URL and key from the project Connect dialog.

Read the API keys docs for a full explanation of all key types and their uses.

1
import 'jsr:@supabase/functions-js/edge-runtime.d.ts'
2
import { createClient } from 'npm:@supabase/supabase-js@2'
3
4
const supabase = createClient(Deno.env.get('SUPABASE_URL')!, Deno.env.get('SB_PUBLISHABLE_KEY')!)
5
6
Deno.serve(async (req) => {
7
const authHeader = req.headers.get('Authorization')!
8
const token = authHeader.replace('Bearer ', '')
9
10
const { data, error } = await supabase.auth.getClaims(token)
11
const userEmail = data?.claims?.email
12
if (!userEmail || error) {
13
return Response.json(
14
{ msg: 'Invalid JWT' },
15
{
16
status: 401,
17
}
18
)
19
}
20
21
return Response.json({ message: `hello ${userEmail}` })
22
})

Verifying JWT

Using Supabase template

You can see a custom JWT verification example on GitHub and a variety of auth function templates also on GitHub.

To verify incoming requests, you can copy/download the specified template and start using it:

_shared/jwt/default.ts
1
// ...
2
3
import * as jose from "jsr:@panva/jose@6";
4
5
const SUPABASE_JWT_ISSUER = Deno.env.get("SB_JWT_ISSUER") ??
6
Deno.env.get("SUPABASE_URL") + "/auth/v1";
7
8
const SUPABASE_JWT_KEYS = jose.createRemoteJWKSet(
9
new URL(Deno.env.get("SUPABASE_URL")! + "/auth/v1/.well-known/jwks.json"),
10
);
11
12
function getAuthToken(req: Request) {
13
const authHeader = req.headers.get("authorization");
14
if (!authHeader) {
15
throw new Error("Missing authorization header");
16
}
17
const [bearer, token] = authHeader.split(" ");
18
if (bearer !== "Bearer") {
19
throw new Error(`Auth header is not 'Bearer {token}'`);
20
}
21
22
return token;
23
}
24
25
function verifySupabaseJWT(jwt: string) {
26
return jose.jwtVerify(jwt, SUPABASE_JWT_KEYS, {
27
issuer: SUPABASE_JWT_ISSUER,
28
});
29
}
30
31
// Validates authorization header
32
export async function AuthMiddleware(
33
req: Request,
34
next: (req: Request) => Promise<Response>,
35
) {
36
if (req.method === "OPTIONS") return await next(req);
37
38
try {
39
const token = getAuthToken(req);
40
const isValidJWT = await verifySupabaseJWT(token);
41
42
if (isValidJWT) return await next(req);
43
44
return Response.json({ msg: "Invalid JWT" }, {
45
status: 401,
46
});
47
} catch (e) {
48
return Response.json({ msg: e?.toString() }, {
49
status: 401,
50
});
51
}
52
}
View source
hello/index.ts
1
// ...
2
3
import { AuthMiddleware } from "../_shared/jwt/default.ts";
4
5
interface reqPayload {
6
name: string;
7
}
8
9
Deno.serve((r) =>
10
AuthMiddleware(r, async (req) => {
11
const { name }: reqPayload = await req.json();
12
const data = {
13
message: `Hello ${name} from foo!`,
14
};
15
16
return Response.json(data);
17
})
18
);
View source