Auth

JSON Web Token (JWT)

Information on how best to use JSON Web Tokens with Supabase


A JSON Web Token is a type of data structure, represented as a string, that usually contains identity and authorization information about a user. It encodes information about its lifetime and is signed with a cryptographic key to make it tamper-resistant.

Supabase Auth continuously issues a new JWT for each user session, for as long as the user remains signed in. Check the comprehensive guide on Sessions to find out how you can tailor this process for your needs.

JWTs provide the foundation for Row Level Security. Each Supabase product is able to securely decode and verify the validity of a JWT it receives before using Postgres policies and roles to authorize access to the project's data.

Supabase provides a comprehensive system of managing JWT Signing Keys used to create and verify JSON Web Tokens.

Introduction

JWTs are strings that have the following structure:

1
<header>.<payload>.<signature>

Each part is a string of Base64-URL encoded JSON, or bytes for the signature.

Header

1
2
3
4
5
{ "typ": "JWT", "alg": "<HS256 | ES256 | RS256>", "kid": "<unique key identifier>"}

Gives some basic identifying information about the string, indicating its type typ, the cryptographic algorithm alg that can be used to verify the data, and optionally the unique key identifier that should be used when verifying it.

Payload

1
2
3
4
5
6
7
8
9
{ "iss": "https://project_id.supabase.co/auth/v1", "exp": 12345678, "sub": "<user ID>", "role": "authenticated", "email": "someone@example.com", "phone": "+15552368" // ...}

Provides identifying information (called "claims") about the user (or other entity) that is represented by the token. Usually a JWT conveys information about what the user can access (then called Access Token) or who the user is (then called ID Token). You can use a Custom Access Token Hook to add, remove or change claims present in the token. A few claims are important:

ClaimDescription
issIdentifies the server which issued the token. If you append /.well-known/jwks.json to this URL you'll get access to the public keys with which you can verify the token.
expSets a time limit after which the token should not be trusted and is considered expired, even if it is properly signed.
subMeans subject, is the unique ID of the user represented by the token.
roleThe Postgres role to use when applying Row Level Security policies.
...All other claims are useful for quick access to profile information without having to query the database or send a request to the Auth server.

Signature

A digital signature using a shared secret or public-key cryptography. The purpose of the signature is to verify the authenticity of the <header>.<payload> string without relying on database access, liveness or performance of the Auth server. To verify the signature avoid implementing the algorithms yourself and instead rely on supabase.auth.getClaims(), or other high-quality JWT verification libraries for your language.

Supabase and JWTs

Supabase creates JWTs in these cases for you:

  1. When using Supabase Auth, an access token (JWT) is created for each user while they remain signed in. These are short lived, so they are continuously issued as your user interacts with Supabase APIs.
  2. As the legacy JWT-based API keys anon and service_role. These have a 10 year expiry and are signed with a shared secret, making them hard to rotate or expire. These JWTs express public access via the anon key, or elevated access via the service_role key. We strongly recommend switching to publishable and secret API keys.
  3. On-the-fly when using publishable or secret API keys. Each API key is transformed into a short-lived JWT that is then used to authorize access to your data. Accessing these short-lived tokens is generally not possible.

In addition to creating JWTs, Supabase can also accept JWTs from other Auth servers via the Third-Party Auth feature or ones you've made yourself using the legacy JWT secret or if you've imported in JWT Signing Key.

Using custom or third-party JWTs

Your Supabase project accepts a JWT in the Authorization: Bearer <jwt> header. If you're using the Supabase client library, it does this for you.

If you are already using Supabase Auth, when a user is signed in, their access token JWT is automatically managed and sent for you with every API call.

If you wish to send a JWT from a Third-Party Auth provider, or one you made yourself by using the legacy JWT secret or a JWT signing key you imported, you can pass it to the client library using the accessToken option.

1
2
3
4
5
6
7
import { } from '@supabase/supabase-js'const = ('https://<supabase-project>.supabase.co', 'SUPABASE_ANON_KEY', { : async () => { return '<your JWT here>' },})

In the past there was a recommendation to set custom headers on the Supabase client with the Authorization header including your custom JWT. This is no longer recommended as it's less flexible and causes confusion when combined with a user session from Supabase Auth.

Verifying a JWT from Supabase

If you're not able to use the Supabase client libraries, the following can be used to help you securely verify JWTs issued by Supabase.

Supabase Auth exposes a JSON Web Key Set URL for each Supabase project:

1
GET https://project-id.supabase.co/auth/v1/.well-known/jwks.json

Which responds with JWKS object containing one or more asymmetric JWT signing keys (only their public keys).

1
2
3
4
5
6
7
8
9
10
11
{ "keys": [ { "kid": "<match with kid from JWT header>", "alg": "<match with alg from JWT header>", "kty": "<RSA|EC|OKP>", "key_ops": ["verify"] // public key fields } ]}

This endpoint is served directly from the Auth server, but is also additionally cached by the Supabase Edge for 10 minutes, significantly speeding up access to this data regardless of where you're performing the verification. It's important to be aware of the cache expiry time to prevent unintentionally rejecting valid user access tokens. We recommend waiting at least 20 minutes when creating a standby signing key, or revoking a previously used key.

Make sure that you do not cache this data for longer in your application, as it might make revocation difficult. If you do, make sure to provide a way to purge this cache when rotating signing keys to avoid unintentionally rejecting valid user access tokens.

Below is an example of how to use the jose TypeScript JWT verification library with Supabase JWTs:

1
2
3
4
5
6
7
8
9
10
11
12
import { jwtVerify, createRemoteJWKSet } from 'jose'const PROJECT_JWKS = createRemoteJWKSet( new URL('https://project-id.supabase.co/auth/v1/.well-known/jwks.json'))/** * Verifies the provided JWT against the project's JSON Web Key Set. */async function verifyProjectJWT(jwt: string) { return jwtVerify(jwt, PROJECT_JWKS)}

Verifying with the legacy JWT secret or a shared secret signing key

If your project is still using the legacy JWT secret, or you're using a shared secret (HS256) signing key, we recommend always verifying a user access token directly with the Auth server by sending a request like so:

1
2
3
GET https://project-id.supabase.co/auth/v1/userapikey: publishable or anon legacy API keyAuthorization: Bearer <JWT>

If the server responds with HTTP 200 OK, the JWT is valid, otherwise it is not.

Because the Auth server runs only in your project's specified region and is not globally distributed, doing this check can be quite slow depending on where you're performing the check. Avoid doing checks like this from servers or functions running on the edge, and prefer routing to a server within the same geographical region as your project.

If you are using the legacy JWT secret, or you've imported your own shared secret (HS256) signing key, you may wish to verify using the shared secret. We strongly recommend against this approach.

Check the JWT verification libraries for your language on how to securely verify JWTs signed with the legacy JWT secret or a shared secret (HS256) signing key. We strongly recommend relying on the Auth server as described above, or switching to a different signing key based on public key cryptography (RSA, Elliptic Curves) instead.

Resources