S
Supabase•3mo ago
eric44

Dealing with user roles

I create a table called user_roles. I made two roles. But im wondering if roles is already built in and im suppose to do it differently? also in the middleware can I get the roles easily to decide where to redirect? cause I didnt think querying the DB in the middleware was a good idea.
26 Replies
garyaustin
garyaustin•3mo ago
Nothing built in. You can use app_metadata to carry a role key or Supabase has a custom claims auth hook where you can set the value from your roles table into the claim. Those allow accessing the JWT claims so can be used in your client. RLS can work with the JWT claims or the role_table directly. https://supabase.com/docs/guides/database/postgres/custom-claims-and-role-based-access-control-rbac Auth hook
eric44
eric44OP•3mo ago
ill try and decipher what u said. lol ill read the article chat gpt is saying i should store the role on user metadata so its avaiable when i get the user id in middl3eware. as opposed to querying the user_roles table
garyaustin
garyaustin•3mo ago
Do not user user metadata. That is not secure. The user could change their role whenever they want. You can use app_metadata which can only be updated with and admin updateUser call or from SQL.
eric44
eric44OP•3mo ago
super confused. how can a user update their metadata?
garyaustin
garyaustin•3mo ago
auth.updateUser The only security on that is it only works for that user to update. But if they are signed in they have their JWT and can make that call with Postman or other tools if desired. Or hack code in the browser dev console.
Chatox
Chatox•3mo ago
@eric44 first time using RBAC as proposed here: https://supabase.com/docs/guides/database/postgres/custom-claims-and-role-based-access-control-rbac I'll say go this way first, I had implemented the app_metadata prop as well but as gary mentioned you will need to use the service client instead. I also updated the example function to work with multiple roles in case you need it (once decoded the jwt you'll see an array instead of a string) create or replace function public.custom_access_token_hook(event jsonb) returns jsonb language plpgsql stable as $$ declare claims jsonb; user_roles jsonb; begin -- Fetch all user roles in the user_roles table select jsonb_agg(role) into user_roles from public.user_roles where user_id = (event->>'user_id')::uuid;
claims := event->'claims'; if user_roles is not null then -- Set the claims with all roles claims := jsonb_set(claims, '{user_role}', user_roles); else claims := jsonb_set(claims, '{user_role}', 'null'); end if; -- Update the 'claims' object in the original event event := jsonb_set(event, '{claims}', claims); -- Return the modified or original event return event; end; $$;
Custom Claims & Role-based Access Control (RBAC) | Supabase Docs
Use Auth Hooks to add custom claims for managing role-based access control.
eric44
eric44OP•3mo ago
im trying to follow that page but having a hard time... i have roles and user roles tables. when i triedto make a claimsfunction it said permission denied ok i created a function to add the roles to the jwt token... i think. a user can have multiple roles in my setup. so now i go to authentication hooks and find that function i made?
garyaustin
garyaustin•3mo ago
Multiple roles would mean your role claim would need to be an array most likely. Then your RLS also has to deal with arrays of roles (probably best done by functions to return the roles a user has for that activity).
No description
garyaustin
garyaustin•3mo ago
Sorry the image was in regards to your first question on permissions. SB made this guide pretty tough the way they implemented grants.
eric44
eric44OP•3mo ago
No description
eric44
eric44OP•3mo ago
this is what i made for the hook then i added it via dashboard authentication hook area as postgres not https... and it populated the grants i got it all working jsut so i can redirect in middleware by user role without making a call to the DB in middleware cause i figured that was bad. Problem is i dont give a role until they fill out a form. they have to choose between two forms which will give them 1 of 2 roles. after they submit form i give them a role on server side. But the jwt token isnt refreshed... so middlewaer doesnt have ypdated role so they can still go back to the choice page and what not :/ the example of nextjs with supabase DOEs have middleware calling the user role from the DB. is that bad? like await supabase.from('user_roles')...
garyaustin
garyaustin•3mo ago
You would need to force a refresh, or do getUser to reload if that works with your flow.
eric44
eric44OP•3mo ago
well i set it after submit profile form. and i upsert on server side. so where would i refresh it? when it comes back successful? i show a toast... so thats where i would refresh it
garyaustin
garyaustin•3mo ago
Not something I've done. If you are changing server side it is difficult as there won't be a change until the client/app refreshes the JWT. Also note if you are using the auth hook custom claim getUser actually won't work as the claim is in the JWT itself and NOT the getUser returned user object.
eric44
eric44OP•3mo ago
i think i got it working yes i figured out its not on the getuser its jsut in the jwt and needs decoding
garyaustin
garyaustin•3mo ago
Yep. Next month that should be easier when asymmetric keys come out.
eric44
eric44OP•3mo ago
cool! i have no idea what that is or what that means for me but cool 😛 @garyaustin last question. i have a profiles table. and i had a column "verified" that gives profiles some extra access. should ihave made a user role for that? cause i cant currently get that on the jwt. only their role. so i now have to do a separate query to get ifthey are verified. i also have RLS setup so they cant access things nayway of they arent but i feel safer putting some conditional statements in also
garyaustin
garyaustin•3mo ago
If you use RLS to enforce the verified you could have a postgres function check that to allow access as part of RLS. But if you need it on the server then have a claim in the JWT or app_metadata becomes more useful.
eric44
eric44OP•3mo ago
currently that table simply comes back null if they dont have access. but for ui and for me to do things conditionally i currently have to query their profile row. i do it server side in nextjs before laoding the page. i put their row in react context.
garyaustin
garyaustin•3mo ago
Yes. You either have to have it in the auth.users table (app_metadata) or a custom JWT claim to have it from auth calls automatically for your server side. Or read the the user role/profile table like you are doing. The auth custom JWT claim hook can read your profile table column and add it to a JWT claim
eric44
eric44OP•3mo ago
should i out the row from thw table in to the claim? ive never done the app data. i folloe the link abobe and added it to jwt claim hook thing the user role only so far..
garyaustin
garyaustin•3mo ago
I would do this method and not app_metadata if you have a table already and need it server side https://supabase.com/docs/guides/database/postgres/custom-claims-and-role-based-access-control-rbac I don't do server side code myself so rely on the tables in RLS for security and limiting user operations.
eric44
eric44OP•3mo ago
your sauing i should put their profile row on the jwt?
garyaustin
garyaustin•3mo ago
No. If you need a specific role or claim like verified you need on the server that is something to consider adding to the JWT. But it does not matter if you add it as a hook claim or to app_metadata both add to the length of the JWT.
eric44
eric44OP•3mo ago
got it. yeah i want sure if verified should be a role or just a column
garyaustin
garyaustin•3mo ago
That is semantics. If you have multiple roles for other things in a role claim adding verified to that seems odd. If you only need a single new role "verified" then that can be in a claim called role or a claim called verified, no real difference. More what makes sense to you.

Did you find this page helpful?