Skip to main content

How Supabase auth, RLS and real-time works

· 7 min read
Serhii Hrekov
software engineer, creator, artist, programmer, projects founder

Here are detailed answers and a guide on how Supabase's Real-time, Auth, and RLS policies work together to secure your application.


How Real-time Sync Integrates with RLS Policies

Supabase's real-time feature is seamlessly integrated with Row-Level Security (RLS) to ensure data privacy and security. The core principle is that real-time events are only broadcast to a client if that client would be able to read the data via a standard database query.

Under the hood, this works as follows:

  1. A change occurs in the PostgreSQL database (an INSERT, UPDATE, or DELETE).
  2. Supabase's realtime server, which is listening to the database's replication stream, captures this event.
  3. Before broadcasting the change, the realtime server performs a security check. It temporarily assumes the identity of each subscribed client and runs an internal query to see if that client's RLS policies would allow them to see the updated row.
  4. If the RLS policy for a given client evaluates to true, the real-time event is sent to that client via a WebSocket. If the policy evaluates to false, the event is filtered out and the client never receives it.

This means you don't have to write any special code to secure your real-time data. Your existing RLS policies automatically handle the filtering, ensuring that a user only gets real-time updates for data they are authorized to access. This applies to both changes from Postgres and to real-time features like Broadcast and Presence which have their own RLS policies on the realtime.messages table.


Restricting Database Access to Specific Roles

Yes, you can and should restrict database access to specific roles using RLS policies. This is the primary mechanism for fine-grained authorization in Supabase.

Supabase automatically assigns users to one of two core PostgreSQL roles:

  • anon: This role is used for unauthenticated, public requests. It's active when a user has not signed in.
  • authenticated: This role is for logged-in users. Once a user signs in, their session token is used by the Supabase client to switch to this role for all API requests.

You can create RLS policies that target these roles specifically.

Example 1: Public read, private write

If you have a posts table where everyone can read but only logged-in users can write:

-- Enable RLS on the posts table
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

-- Allow 'anon' and 'authenticated' users to read all posts
CREATE POLICY "Public posts are visible to everyone" ON posts
FOR SELECT
TO anon, authenticated
USING (true);

-- Allow only 'authenticated' users to insert new posts
CREATE POLICY "Authenticated users can create posts" ON posts
FOR INSERT
TO authenticated
WITH CHECK (true);

The TO clause specifies which PostgreSQL role the policy applies to. This powerful mechanism allows you to create different access rules for different user states.

Example 2: Restricting access based on custom roles

You can also implement Role-Based Access Control (RBAC) by storing user roles in a separate table or as a custom claim in the user's JWT. The JWT's claims are accessible inside your RLS policies, allowing you to create fine-grained access rules.

Let's assume you have a user_roles table. You can write a policy that checks if the user has the 'admin' role:

CREATE POLICY "Admins can update all posts" ON posts
FOR UPDATE
TO authenticated
USING (
auth.uid() IN (
SELECT user_id FROM user_roles WHERE role = 'admin'
)
);

This policy ensures that only authenticated users who are also listed as 'admin' in the user_roles table can update any post.


The Interaction Between Supabase Auth and RLS

Supabase Auth and RLS policies are designed to work together seamlessly to provide a secure authorization layer [1]. Here's how they interact:

  1. Authentication: When a user signs in (e.g., with email/password or OAuth), Supabase Auth verifies their identity and issues a JSON Web Token (JWT). This JWT contains a unique user ID (sub claim) and other metadata.
  2. Authorization: When the Supabase client makes a request to the database, it automatically includes the user's JWT in the request header.
  3. JWT Verification: The Supabase API gateway (PostgREST) intercepts the request. It verifies the JWT's signature and expiration.
  4. Role Assignment: Upon successful verification, PostgREST switches the PostgreSQL session from the anon role to the authenticated role.
  5. Policy Evaluation: PostgreSQL then evaluates the RLS policies for the requested table. The policies can access data from the user's JWT, most importantly the user's ID via auth.uid(). This allows policies to filter data based on who is making the request.

This process ensures that a user cannot access data they are not authorized to see, even if they tried to bypass the client-side code. RLS acts as a final, server-side security layer that is applied to every single database query, guaranteeing that your data remains secure [1, 2].

Sources

  1. Supabase. "Row Level Security | Supabase Docs". https://supabase.com/docs/guides/database/postgres/row-level-security
  2. Supabase. "Auth | Supabase Docs". https://supabase.com/docs/guides/auth