Custom Claims & Role-based Access Control (RBAC)
Custom Claims are special attributes attached to a user that you can use to control access to portions of your application. For example:
_10{_10 "user_role": "admin",_10 "plan": "TRIAL",_10 "user_level": 100,_10 "group_name": "Super Guild!",_10 "joined_on": "2022-05-20T14:28:18.217Z",_10 "group_manager": false,_10 "items": ["toothpick", "string", "ring"]_10}
To implement Role-Based Access Control (RBAC) with custom claims
, use a Custom Access Token Auth Hook. This hook runs before a token is issued. You can use it to add additional claims to the user's JWT.
This guide uses the Slack Clone example to demonstrate how to add a user_role
claim and use it in your Row Level Security (RLS) policies.
Create a table to track user roles and permissions
In this example, you will implement two user roles with specific permissions:
moderator
: A moderator can delete all messages but not channels.admin
: An admin can delete all messages and channels.
For the full schema, see the example application on GitHub.
You can now manage your roles and permissions in SQL. For example, to add the mentioned roles and permissions from above, run:
Create Auth Hook to apply user role
The Custom Access Token Auth Hook runs before a token is issued. You can use it to edit the JWT.
Enable the hook
In the dashboard, navigate to Authentication > Hooks (Beta)
and select the appropriate PostgreSQL function from the dropdown menu.
When developing locally, follow the local development instructions.
To learn more about Auth Hooks, see the Auth Hooks docs.
Accessing custom claims in RLS policies
To utilize Role-Based Access Control (RBAC) in Row Level Security (RLS) policies, create an authorize
method that reads the user's role from their JWT and checks the role's permissions:
You can read more about using functions in RLS policies in the RLS guide.
You can then use the authorize
method within your RLS policies. For example, to enable the desired delete access, you would add the following policies:
_10create policy "Allow authorized delete access" on public.channels for delete to authenticated using ( (SELECT authorize('channels.delete')) );_10create policy "Allow authorized delete access" on public.messages for delete to authenticated using ( (SELECT authorize('messages.delete')) );
Accessing custom claims in your application
The auth hook will only modify the access token JWT but not the auth response. Therefore, to access the custom claims in your application, e.g. your browser client, or server-side middleware, you will need to decode the access_token
JWT on the auth session.
In a JavaScript client application you can for example use the jwt-decode
package:
_10import { jwtDecode } from 'jwt-decode'_10_10const { subscription: authListener } = supabase.auth.onAuthStateChange(async (event, session) => {_10 if (session) {_10 const jwt = jwtDecode(session.access_token)_10 const userRole = jwt.user_role_10 }_10})
For server-side logic you can use packages like express-jwt, koa-jwt, PyJWT, dart_jsonwebtoken, Microsoft.AspNetCore.Authentication.JwtBearer, etc.
Conclusion
You now have a robust system in place to manage user roles and permissions within your database that automatically propagates to Supabase Auth.