LongCut logo

We made Supabase Auth way faster!

By Supabase

Summary

## Key takeaways - **JWT Signing Keys Boost Supabase Auth Performance**: Supabase Auth performance can be significantly improved by using JWT Signing Keys, which allow JWT validation directly within your application instead of relying on network requests to the Supabase Auth Server. [00:10], [12:42] - **Refactor to `getClaims` for Future-Proofing**: Refactoring your Supabase authentication checks from `getUser` to `getClaims` prepares your application for JWT Signing Keys, enabling local JWT validation and reducing network latency. [01:09], [03:07] - **Asymmetric vs. Symmetric JWTs: A Key Distinction**: Symmetric JWTs require verification by the Supabase server, while asymmetric JWTs, enabled by signing keys, can be verified by your application, eliminating a network request. [02:27], [03:02] - **Migrating API Keys is Crucial for JWT Key Rotation**: Before rotating JWT signing keys, migrate from legacy JWT-based API keys (anon/service_role) to the new publishable/secret key format to ensure seamless integration. [07:45], [08:20] - **Performance Gains: Milliseconds vs. Seconds**: Implementing asymmetric JWTs via signing keys reduced JWT verification time from over a second to a few milliseconds, demonstrating a substantial performance improvement. [04:35], [12:25] - **Bootstrap New Projects with a Single Command**: A single command using `npx create-next-app --example with-supabase` can bootstrap a new Next.js application pre-configured with Supabase authentication using JWT signing keys. [12:44], [13:05]

Topics Covered

  • Asymmetric JWTs: Unlock faster local authentication.
  • Symmetric JWTs: The hidden performance bottleneck?
  • Avoid downtime: The multi-step JWT key migration.
  • Instant Auth: Bootstrap new apps with 5ms verification.

Full Transcript

Superbase or makes it really easy to add

authentication and authorization to your

apps. In this video, we're going to look

at how JWT signing keys can be used to

greatly improve the performance of

Superbase or we're going to refactor an

existing Nex.js app to use JWT signing

keys. And then at the end of the video

I'll show you a single command that you

can use to get up and running with a

brand new Nex.js app completely wired up

with Superbase or JWT signing keys

without really needing to do anything.

we're about to cover. So, let's get into

it.

Here I have an Nex.js app wired up to

Superbase to handle authentication. So

if I sign in as John at superbase.com

and my super seccure password, we'll see

this protected page that we can only see

if we're authenticated. And we have a

little bit of information about the

currently signed in user. If we have a

look at the code for this page, we're

creating a Superbase client and then

calling the get user method to work out

whether or not the user is signed in. If

they're not, then we redirect them to

the orth page. And if we have a user

then we render out this page we're

seeing in the browser. So the first

change we're going to make is rather

than calling this get user method, we're

instead going to call get claims. And

then rather than getting back a user, we

get back a claims object, which is very

similar to what we get back from get

user. And so that's what we want to

render out on the page here. Rather than

our user, we want our claims. And now if

we go back to the browser, we see a very

similar object that contains some

information about our user. So our app

is still functioning in the same way.

We've just refactored our orth check to

use get claims rather than get user. So

get claims is how we recommend doing

this kind of orth check going forward.

This doesn't give us huge performance or

security gains yet. it will later in

this video, but right now this just sets

us up to be able to use JWT signing

keys, which we highly recommend you

start using. So, similarly to the get

user method, get claims is currently

making a network request to the

Superbase OR server. It's passing across

that JWT and it's saying is this user

authenticated? Is this JWT valid? And

then once it gets back that response, it

can determine whether it redirects the

user to the login page or renders the

page they're actually requesting. Now

this is probably a good time to mention

that this video gets pretty technical.

If you start to feel a little bit lost

that's totally understandable, but it

really boils down to this one simple

rule. Symmetric JWTs or how JWTs have

worked with Superbase since the

beginning need to be verified by the

Superbase or server. So, that's the only

thing that can say this user is valid

and this JWT can be trusted. But with

JWT signing keys which we're about to

configure, you get asymmetric JWTs which

means they can be verified by the

Superbase or server but they can also be

verified by your application. So again

symmetric keys need to go off to another

server to be verified whereas asymmetric

keys can be verified by your

application. So one less network request

that needs to happen and this get claims

method can handle either of those cases.

So it tries to verify the user locally

if it's an asymmetric key. But if we're

using the older symmetric keys, it will

automatically handle sending this off to

the superbase or server to see whether

it's valid. So that's why we recommend

using get claims cuz it can do either.

It's handling that complexity for you.

And when we call get claims, we get back

these claims, but we also get back a

header, which we can use to see some

information about that JWT. So if we

save this and head back over to the

browser, we can see the signing

algorithm that was used is HS256, which

is a symmetric signing algorithm

meaning we can't validate this JWT

within our application. So every time we

refresh, this is going off to the

Superbase OR server to validate that

JWT. And so up in our middleware file

which runs on every single page load

we're also calling get user, which will

always make that network request to the

Superbase OR server. So let's test how

long this actually takes. So let's take

a timestamp for our start time which we

can get with date.now and then after

we've called get user let's console log

finished JWT verification in and then

take another timestamp and minus off

that start time and then that's how many

milliseconds this request has taken. And

so since this happens server side let's

open up our server console and then go

back to the browser and refresh this

page. And if we have a look at how long

this took, it was 1.2 seconds. Now

there is some cing and stuff that

happens behind the scenes. So, if we

refresh this and have a look, it's not

quite as dramatic at 850 milliseconds.

And if we refresh again, we see 313

milliseconds, but it's going to hover

around that mark every time we refresh.

And so, the reason this is taking so

long is firstly, I'm here in Australia

and my superbase region is set to

Europe. So I'm very far away

geographically from where my superbase

or server is. So anytime I have to do

that round trip, I need to wait for it.

So this is the perfect example of where

I would benefit from using asymmetric

JWTs or JWT signing keys because then my

application could verify the state of my

user without needing to send that

network request to Superbase. And we can

try with get claims. So rather than get

user, we'll do get claims. That gives us

back our claims. We'll also update our

comments because we're such good

citizens. So, get claims here and get

claims here. And then that should be it

because our middleware is just taking

that user and checking if we don't have

a user and we're trying to navigate

anywhere but the homepage or the login

page or anything or related then we want

to again redirect the user to that login

page. So, we're checking that on every

single request. So, now that this is

refactored to use get claims, let's

check the performance. So again, we'll

refresh our page and have a look at that

first request again about 1.2 seconds.

And then we'll do a few more subsequent

requests. And we're seeing it's hovering

around that same mark. So about 300

milliseconds. And there's not really

anything we can do to improve the

performance of this without implementing

JWT signing keys. So let's do that now.

So over in the Superbase dashboard, we

want to go to project settings and then

over to JWT keys. And you can see we're

currently using our legacy JWT secret to

sign all of our JWT keys. And here it's

nudging us towards using JWT signing

keys for these reasons. So let's go over

to the JWT signing keys tab and migrate

our JWT signing secret across to JWT

signing keys. This is telling us that

we're currently using symmetric JWTs.

This will allow us to use asymmetric

JWTs and it won't actually cause any

downtime to our project. So let's

migrate the JWT secret. And you can see

this has created a new standby key which

is our asymmetric key. But it has also

kept our current key which is our legacy

HS256 key which we know is a symmetric

JWT signing algorithm. So nothing's

actually changed in our Superbase

project at this point. If any new JWTs

need to be created, they will still use

that legacy symmetric JWT to create

them. And we can confirm this by going

back to our application and refreshing.

And we can see we're still using that

HS256 key. And if we have a look in our

server console, it's still taking a long

time to verify because it's still going

off to the Superbase or server. So the

way we move across to using this new

asymmetric key is by rotating our keys.

But we can't do that just yet. If we

have a look over at the legacy JWT

secret tab, we'll see we've now got this

caution sign. It says we've now migrated

to these new signing keys. But our

add-on and service role keys which we're

using as API keys to connect to

Superbase, they themselves are JWTs

which used the old legacy signing key.

So if we have a look at our API keys

both our anon and service ro key are

JWTs. And if we go over to our nextjs

app and look at ourv.local file, this is

what we're using here to connect to

superbase. And we'll see these are also

the legacy API keys because we recommend

migrating across to the new API keys

which uses a publishable and secret key

rather than add-on and service role. So

let's create some new API keys. This is

telling us it will create a publishable

and secret key and that's what we should

use to connect our application to

Superbase. So let's create those keys.

And now we can copy our publishable key

and head over to our next.js app and

replace our add-on key with this

publishable key. And you'll see in this

project our environment variable is

actually named next_pub

superbase publishable or anon key

because these can now be used

interchangeably. And so we can have a

look at how we're using this environment

variable in our application. So it's

used to check we actually have those

environment variables but then to create

our superbase client in the browser as

well as in middleware and on the server.

But anyway point is they can be used

interchangeably. So anywhere you're

using your anon key, you can replace it

with the value of your publishable key.

And anywhere you're using the service ro

key, you can replace it with this secret

key. So now that we've migrated across

to these new API keys, we can have a

look at our legacy keys. We can confirm

they're no longer being used. So I'm

confident my Nex.js application is the

only thing using these and we've now

refactored those to use our new API

keys. And so I can disable these

JWTbased API keys. So this is warning us

that we want to make sure that no

applications are still using these. So

let's proceed to disabling these API

keys. We'll need to type disable to

confirm. But you'll see that even if you

realize later, oh, there was this other

mobile app that was using this, you can

reenable these legacy API keys. So

you're not locked out permanently. But

we've now refactored our Superbase

project to use these new API keys. And

since they're not JWTs and they're not

signed with that old signing secret, we

can now head back over to our JWT

section and see that we're still using

that legacy JWT signing secret. But now

that we've migrated across to those new

API keys, we can fully migrate across to

our JWT signing keys. And so let's

rotate our keys. So this is telling us

that the standby key will now become the

current key, meaning that's what will be

used anytime we're minting a new JWT.

And the current key will become the

previous key. And to proceed, we need to

confirm all of our applications

components have picked up the standby

key and that any non-expired JWTs will

still be valid until we explicitly

revoke the currently used key. So, we'll

have a look at that in a second. It's

also a good time to call out that if

you're using the old signing key to mint

your own JWTs and manually verifying

that somewhere else, you'll need to make

sure that you refactor that manually

across to use this new signing key

before you rotate it. But that's not the

case for me. So, I'm going to rotate

this signing key. And so, now we're

using our new JWT signing key to create

new JWTs, but our previous key can still

be used to verify JWTs. So, this will be

used for creating new JWTs, but either

of these are valid until these tokens

expire. But once you're sure you're no

longer using this legacy symmetric JWT

signing secret, you can revoke it. which

means any JWTs that used this signing

secret will no longer be trusted. They

will not be valid JWTs. So by revoking

the signing key, all applications

trusting it will no longer do so if

there are JWTs or access tokens that are

valid at the time of revocation. They

will no longer be trusted, causing users

with these JWTs to be signed out. when

they sign back in, it will use the new

JWT signing secret and they'll get all

of the benefits of the asymmetric JWT

but they will be forced to sign out. So

if you're sure that is all good, you can

copy this UU ID into this field and

revoke the signing key, moving it to the

revoked key section. Again, this can be

moved back to being a standby key if you

realize that was a mistake. But now that

we've revoked that key, our Nex.js app

if we refresh, ah, it's kicked us out.

This is the exact thing your users will

experience if they're using one of the

old symmetric JWTs. But if we

authenticate, we can see that signing

algorithm has been updated to ES. And if

we take a look at our servers console

our verification finished in 5

milliseconds. And if we refresh again

and have a look at the console, 2

milliseconds. And if we refresh another

few times, we can see it's hovering

around that two or three milliseconds to

verify that JWT because it's now

happening in our application rather than

going off to the Superbase OR server. So

if you just want to create a brand new

Nex.js application with all of this

already wired up for you, then check out

this single command. Let's use MPX to

run the create-ext-

app package. And we're going to make

sure this is running the latest version.

Now we can specify the example with -ash

superbase and then the name of our

project. So in my case JWT signing keys.

Now if we run this one, it's going to

use that superbase template to create

our next.js app. So now we can change

into that directory and open it up in VS

Code. And this is the exact same project

that I've been using for this example.

So if we have a look at that protected

page, we can see it's already been set

up to use that get claims method. And

also in the middleware file where we're

updating the user session, this has also

been configured to use get claims. So

the only thing we need to do to wire

this up to our Superbase project is

update our environment variables. So

take this.example

file and rename itemv.local.

We can then head over to our superbase

project and click this connect button.

change this tab to app frameworks and we

can see our URL and our publishable key

have already been populated. So let's

copy these and go back over to our

next.js project and the naming for this

one is just slightly different. So let's

paste this underneath our superbase URL

is fine but let's copy the name of our

publishable or anon key environment

variable and replace this one that is

publishable default key. Now we can get

rid of these ones and save this file and

then run mpm rundev. open this one up in

a browser and we have exactly the same

application. It's even reusing my JWT

because I was already authenticated. But

if we log out, we can log back in as one

of our users and see that same protected

page using our get claims method and

therefore our asymmetric JWT signing

key. So now that you've got orth sorted

let's learn about branching. But for

that, you need to check out this video

right here. We look at managing branches

directly from the Superbase dashboard

which is an awesome way to quickly

prototype new ideas.

Loading...

Loading video analysis...