Discussion has been updated with solution chosen.
Realtime Authorization for Broadcast and Presence is now available in Public Beta.
See the official documentation.
This post explains how authorization works for Realtime Broadcast and Realtime Presence.
This allows you (the developer) to control access to Realtime Channels. We use Postgres Row Level Security to manage access. Developers create Policies which allow or deny access for your users.
Using Studio’s SQL editor you can set RLS rules against the table realtime.messages
which will define the rules for your users.
_10CREATE POLICY "presence sync and broadcast listen to authenticated users"
_10ON realtime.messages FOR SELECT
_10CREATE POLICY "presence track and broadcast send to authenticated users"
_10ON realtime.messages FOR INSERT
Since you are using RLS policies you can do more complex examples.
In a scenario where you have a schema with a table for rooms and one that creates an association between rooms and users.
We'll use this example schema to be showcase RLS policies limiting Realtime functionality
We can build more complex RLS rules using this information:
_26-- Set permission for authenticated users to only listen for Broadcast messages
_26CREATE POLICY "authenticated can listen to broadcast only on their topics"
_26ON realtime.messages FOR SELECT
_26 from public.rooms r join public.rooms_users ru on r.id = ru.room_id
_26 where ru.user_id = auth.uid()
_26 and r.name = realtime.topic()
_26 and realtime.messages.extension = 'broadcast'
_26-- Set permission for authenticated users to only write for Broadcast messages
_26CREATE POLICY "authenticated can write to broadcast only on their topics"
_26ON realtime.messages FOR INSERT
_26 from public.rooms r join public.rooms_users ru on r.id = ru.room_id
_26 where ru.user_id = auth.uid()
_26 and r.name = realtime.topic()
_26 and realtime.messages.extension = 'broadcast'
Now to test it we can use a quick deno script by creating a index.ts
_21// Run with deno run --allow-net --allow-env --allow-read --allow-ffi index.ts
_21import { createClient } from "npm:@supabase/supabase-js@2.38.5";
_21const url = "https://<project_ref>.supabase.com";
_21const apikey = "<api_key>";
_21const client = createClient(url, apikey);
_21const channel = client.channel("channel_1", {
_21 config: { broadcast: { self: true }, private: true},
_21 .on("broadcast", { event: "test" }, (payload) => console.log(payload))
_21 .on("presence", { event: "join" }, (payload) => console.log(payload))
_21 .on("presence", { event: "leave" }, (payload) => console.log(payload))
_21 .subscribe((status: string, err: any) => {
_21 if (status === "SUBSCRIBED") {
_21 console.log("Connected!");
This will return an error with the message You do not have permissions to read from this Topic
But if we change our code to pass along an authenticated user, then we will be able to connect and receive / send messages.
_28import { createClient } from "npm:@supabase/supabase-js@2.38.5";
_28const url = "https://<project_ref>.supabase.co";
_28const apikey = "<api_key>";
_28const client = createClient(url, apikey);
_28await client.auth.signInWithPassword({
_28 password: "<password>",
_28client.realtime.setAuth(
_28 (await client.auth.getSession()).data.session.access_token
_28const channel = client.channel("channel_1", {
_28 config: { broadcast: { self: true }, private: true },
_28 .on("broadcast", { event: "test" }, (payload) => console.log(payload))
_28 .on("presence", { event: "join" }, (payload) => console.log(payload))
_28 .on("presence", { event: "leave" }, (payload) => console.log(payload))
_28 .subscribe((status: string, err: any) => {
_28 if (status === "SUBSCRIBED") {
_28 console.log("Connected!");
Do not forget that RLS policies can use other tables in them so this will give you all the flexibility you need to better fit your use case but be aware of the performance impact of heavy RLS queries or non-indexed fields.
On connect, you need to send in the configuration that the channel will be private: true
We’re working on the next
version actively so we can provide a good developer experience.
Please check the latest next
version at https://www.npmjs.com/package/@supabase/realtime-js?activeTab=versions
This library as changed the configuration settings to add private: true
on channel connect to determine if the user will be connecting an RLS checked channel.
Connection context#
When you connect with Realtime we set a connection configuration with your JWT, Topic and Headers using the following query:
_10 set_config('role', $1, true),
_10 set_config('realtime.topic', $2, true),
_10 set_config('request.jwt', $4, true),
_10 set_config('request.jwt.claims', $6, true),
_10 set_config('request.headers', $7, true)
This query is only run when you connect to a topic.
We’re also providing a new function to easily fetch the realtime.topic
configuration with
_10SELECT realtime.topic();
_10CREATE POLICY "authenticated users can only write to topic named foo"
_10ON realtime.messages FOR INSERT
_10WITH CHECK ( realtime.topic() = 'foo' );
To achieve RLS checks on your Realtime connection we created a new table in the realtime
schema to which you will be able to write RLS rules against it to control your topics extensions.
You won’t see any entries recorded in this table as we rollback the changes made to test out RLS policies to avoid creating clutter in your database.