Integrating Hasura RBAC with Supabase JWT Authentication
November 2, 2025
November 2, 2025
Welcome to this comprehensive guide on integrating Hasura Role-Based Access Control (RBAC) with Supabase JWT Authentication! If you're looking to build a secure, scalable application with fine-grained access control, this tutorial is for you. By the end, you'll have a seamless setup where Supabase handles user authentication and Hasura enforces role-based permissions, all connected through JSON Web Tokens (JWTs). Let's dive in!
Combining Hasura and Supabase offers the best of both worlds: Supabase provides a robust PostgreSQL database with built-in authentication, while Hasura delivers instant GraphQL APIs with powerful RBAC. This integration allows you to:
Whether you're building a simple web application or a complex SaaS platform, this setup ensures security and scalability. Ready to get started?
Before we begin, ensure you have:
If you haven't set these up yet, visit the Supabase Dashboard and Hasura Cloud to create your projects.
Supabase allows you to embed custom claims in JWTs using data from the raw_app_meta_data field in user profiles. We'll create a database function and trigger to automatically inject Hasura-specific claims (like user ID and roles) into the token whenever a user logs in or updates their profile.
Why is this important? Hasura uses these claims to identify users and enforce role-based permissions, ensuring that each user only accesses data they're authorized to see.
In your Supabase Dashboard, navigate to the SQL Editor and create the following function:
-- Function to add Hasura claims to user metadata
CREATE OR REPLACE FUNCTION public.custom_access_token_hook(event jsonb)
RETURNS jsonb
LANGUAGE plpgsql
AS $$
DECLARE
claims jsonb;
user_role text;
BEGIN
-- Get user role from raw_user_meta_data or default to 'user'
user_role := COALESCE(
event->'claims'->'app_metadata'->>'user_role',
'user'
);
-- Build Hasura claims
claims := jsonb_build_object(
'https://hasura.io/jwt/claims', jsonb_build_object(
'x-hasura-user-id', (event->'claims'->>'sub'),
'x-hasura-default-role', user_role,
'x-hasura-allowed-roles', jsonb_build_array(user_role, 'anonymous')
)
);
-- Merge with existing claims
event := jsonb_set(event, '{claims}', (event->'claims') || claims);
RETURN event;
END;
$$;
-- Grant necessary permissions
GRANT USAGE ON SCHEMA public TO supabase_auth_admin;
GRANT EXECUTE ON FUNCTION public.custom_access_token_hook TO supabase_auth_admin;
REVOKE EXECUTE ON FUNCTION public.custom_access_token_hook FROM authenticated, anon, public;
Note: Ensure the function name custom_access_token_hook matches exactly as shown, as Supabase looks for this specific name when enabling hooks.
Now that the function is created, you need to activate it as a hook in Supabase to modify JWTs during user authentication.

public.custom_access_token_hook from the dropdown.


This step ensures that every time a user authenticates, the hook runs and embeds the Hasura claims into their JWT.
Hasura needs the Supabase JWT secret to validate the tokens issued by Supabase. This secret is used to sign the JWTs and must be shared with Hasura for secure communication.

Warning: If this secret is compromised, unauthorized parties could forge tokens. Treat it like a password.
With the JWT secret in hand, configure Hasura to recognize and validate Supabase-issued tokens. The setup differs slightly depending on whether you're using Hasura Cloud or a self-hosted instance.
HASURA_GRAPHQL_JWT_SECRET.{"type":"HS256","key":"<YOUR_SUPABASE_JWT_SECRET>","claims_namespace":"https://hasura.io/jwt/claims"}. Replace <YOUR_SUPABASE_JWT_SECRET> with the secret you copied earlier.

If you're running Hasura locally or on your own server, update your docker-compose.yml file:
hasura:
environment:
# other settings...
HASURA_GRAPHQL_JWT_SECRET: '{"type":"HS256","key":"<YOUR_SUPABASE_JWT_SECRET>","claims_namespace":"https://hasura.io/jwt/claims"}'Replace <YOUR_SUPABASE_JWT_SECRET> with your actual Supabase JWT secret, then restart the Hasura container to apply the changes.
Let's confirm that the Hasura claims are correctly embedded in the Supabase JWTs.
access_token from the session object.https://hasura.io/jwt/claims:{
...,
"https://hasura.io/jwt/claims": {
"x-hasura-allowed-roles": [
"user",
"anonymous"
],
"x-hasura-default-role": "user",
"x-hasura-user-id": "<user_uuid>"
}
}
If you see these claims, congratulations! Your tokens are now carrying the necessary information for Hasura to enforce RBAC.
With the claims in place, it's time to define permissions in Hasura based on the roles (user and anonymous) embedded in the JWT.
anonymous: Allow only read access to public data.user: Allow read and write access, restricted to data linked to x-hasura-user-id.
Tip: Use session variables like x-hasura-user-id in permission rules to create row-level security, ensuring users only access their own data.
Beyond decoding the JWT, test the integration end-to-end to ensure everything works as expected:
access_token as the Authorization: Bearer <token> header.anonymous user should be denied write access if configured as such.If the query returns the expected data or an appropriate error based on permissions, your setup is working!
If you encounter problems, here are some common issues and solutions:
custom_access_token_hook. Check for errors in the Supabase SQL Editor logs.HASURA_GRAPHQL_JWT_SECRET is correctly set with the right Supabase secret and that the claims_namespace matches https://hasura.io/jwt/claims.user, anonymous). Also, ensure permission rules are saved and applied to the correct tables.If issues persist, consult the Hasura Documentation or Supabase Support.
These examples illustrate how flexible and powerful this setup can be for modern applications.
Congratulations on successfully integrating Hasura RBAC with Supabase JWT Authentication! You've built a secure foundation for your application, leveraging the strengths of both platforms. Now, take it further:
Thank you for following along! If you found this guide helpful, consider sharing it with others embarking on a similar journey. Happy coding!
You can find the complete blog post in my hasura-supabase-jwt-integration github repository.