Edge Functions & JWT Claims Auth Hook timeouts
Hello, I'm currently implementing an edge functions which is being called as an auth hook to get custom claims.
The code is working fine currently, but I would like to also update the user/app metadata in the supabase db for the users and when I try to call the supabase auth admin to update the user by id, it always timeouts, the logs shows that the hook took more than 5 seconds every single time I try to update and it's impossible to have great error handling, as the edge function is being called, logs are showing up to the supabase auth admin call, then timeouts so I have no idea why it can timeout.
In the Auth log in supabase dashboard, I always get :
422: Failed to reach hook within maximum time of 5.000000 seconds
And in the Postgres logs I get:
process 65551 acquired ShareLock on transaction 1346 after 2707.894 ms
process 65551 still waiting for ShareLock on transaction 1346 after 1000.072 ms
connection authorized: user=supabase_auth_admin database=postgres
And they corresponds to the UPDATE query made my the updateUserById call.
Here is the code snippet:
Thank you.41 Replies
Just to let you know, executing the EXACT same query using the PostgREST api, it works totally fine...
Here is the query extracted from the ShareLock logs:
and here is my new code to update the user app metadata:
So I assume something is wrong in the function called
You can't update app metadata with .update.
Your .from with auth.users will not run at all. Your code seems to be updating a public users table.... Do you have one?
The auth schema is not available to the REST API. And the way you access other schemas is .schema() but that won't help for auth.
Your edge function seems to be attempting the correct way.
Do you have trigger functions on auth.users?
Well I'm going to check again but I always get hook timeout when running the getUserById, can the hook timeout and the query still go through?
My main issue here is that auth hook if timed out, the login is failed, and I really need to update the app metadata in the db
You'll need to provide more info on what you are up to. It is possible there is a conflict with the auth hook calling your edge function and you trying to update auth.users with the admin call.
What hook are you using?
JWT Claims
I would not attempt to call an edge function in that.
That is run for every JWT refresh.
The main logic is login via discord, and process additional check (roles and servers condition) before allowing the login
What are you trying to update into app metadate?
That is still not a good thing to do in the claim hook. It runs every JWT refresh which could be minutes if you have a faster expire time.
What would you recommend then? Executing a server action in my nextjs app during exchange code process and signout if not allowed?
Is this when a new user is created or each signin?
The server is a paid community, and I need to gather the specific roles upon login to implement a RBAC webapp
I would check in your serverside signin code. Your other option might be an update trigger on auth.users as that logs signins (updates a last signed column).
A user can be signed in for months though by default.
Yes but when that signin occurs and the auth.users is updated, will the JWT already be generated?
You can kill the process at that point. I don't think you can modify the jwt other than to modify auth.users NEW data in the trigger, which would modify the jwt.
Okay, I have to check.
Because the whole point of it, is that the discord oauth would be successful but to allow the login to my app, I need to check the roles AND update the claims before the jwt is generated
or signing out
So you could do your check, then change raw_app_meta_data in the NEW object on the update trigger (checking the sign time column is changing) and then your JWT back to the user will reflect that.
You can't add custom claims though there.
Okay, I'll implemented this and get back to you.
Even using the community package? for custom claims
I've been using this for past projects
You could also have the custom claims hook get data from a table and have the update trigger modify the table the hook uses.
The community package would work in the update trigger.
I would first add an update trigger and
raise log 'new=%',NEW;
to see if that fires when you need.I see, the edge functions were using mainly to make requests and code easier than just run everything within psql function
Don't do that on auth calls.
Especially jwt claim one.
okay but trigger's fine?
Auth times out in a couple of seconds if not finished.
So there is risk if the edge function is slow or then signin will error out.
That's why I created that thread :pain:
but yeah I suppose I have to do everything within a trigger like I used to in other projects
Your first one is interesting if somehow calling auth.users while auth is in process caused a deadlock.
It happens everytime with that updateUserById inside the edge function
more than 3s is not normal
You might not be able to update auth.users if Auth has a transaction going on. I don't know the details of their process.
for a simple query like this one
That is why I think it is a deadlock.
That is probably the sharelock message, but not something I've dealt with.
I could also use a trigger before insert in auth.sessions right?
but I think if session is created, jwt is already generated no?
The JWT gets generated constantly. I don't know the order auth does all its things, except that app metadata can be changed on the auth.users TRIGGER by changing NEW.
Ok I'll test and get back to you if I need anything else
supabase/auth github has the source and I've been able to get around enough to see what is up for somethings.
You just have to be careful on what you do as it can change at any point. But they document using auth.users trigger.
Ok but when jwt is refreshed, auth.users isn't triggered?
Sessions is not triggered for a refresh I don't believe. I think that is signin/out... but I've not looked at it.
You would need to verify the times on the sessions.
Yes, I'll check all good.
Thank you
@garyaustin Another question, if I move the discord api check within triggers, and maybe some cron psql function to regularly update the subscription value, could I still use the auth hook to just add the subscription claim to the jwt (like in the example in the docs, https://supabase.com/docs/guides/auth/auth-hooks/custom-access-token-hook?queryGroups=language&language=http) ?
So everything updating the metadata for the subscription value will be handled within the db itself, and only the auth hook will be used to add to the jwt
Custom Access Token Hook | Supabase Docs
Customize the access token issued by Supabase Auth
Yes. The hook is setup mainly to do a quick database operation (read a user privilege table) and set a custom claim in the jwt. It runs on every refresh of the token so needs to be as simple as possible. You could then do your update with cron or the auth.users trigger to update the table and on the next jwt generation it would be reflected. (I would assume the jwt hook would be called AFTER the auth.users trigger function.
Hello @garyaustin,
Unfortunately I tested both ways, using the community claims package and doing everything within triggers (both before update 'last_sign_in_at' in auth.users and before insert auth.sessions) and using the auth hook to just gather the app_metadata and apply there, but they all have the previous metadata value saved during the login.
From what I can see in the supabase/auth source code, User is fetched always before everything during the auth process so it would be :
1. Fetch User data
2. Create Session, and followed by Refresh Token and then finally Access Token
And because of both implementation I tried happen in step 2., the actual changes are not reflected when claims are assigned during JWT creation.
Basically this logic for login process isn't working, it works fine on signup tho but I really would like to have it working for the login too.
(https://github.com/supabase/auth/blob/master/internal/api/token.go#L287)
GitHub
auth/internal/api/token.go at master · supabase/auth
A JWT based API for managing users and issuing JWT tokens - supabase/auth
So creating session would be too late to modify data.
I would think though on login it changes the last signed in column with an update, so an update trigger on auth.users could check and change the metadata.
I'm out for awhile so I can't continue to be less than helpful for a bit.
Yes but that's what I already tested, an update trigger on auth.users but even in this case, user data is already pre-fetched. I didn't find other "event' being triggered just before fetching the user data in the auth process yet.
Because the general flow is:
1. User data Fetched
<-- Tried trigger here (before insert auth.sessions)
2. Session created
3. Refresh Token created (with FK to the newly session)
<-- Tried also trigger here (before update 'last_sign_in_at')
5. Update the 'last_sign_in_at' in auth.users
4. Access token created (based on newly refresh token)
One workaround I found working, just tested now, might not be the best but at least it works.
It is basically forcing refresh session within the auth/callback when code is successfully exchanged for a session. In that case it would force it and re-trigger the jwt generation with the update sub value.
Yeah if they update sign_in_at after they set the refresh token then no way to trigger.