---
number: 22484
slug: 22484-realtime-broadcast-and-presence-authorization
published: 2024-04-04
discussion: https://github.com/orgs/supabase/discussions/22484
labels:
  - realtime
page: https://supabase.com/changelog/22484-realtime-broadcast-and-presence-authorization
---

# Realtime Broadcast and Presence Authorization

## Update

Discussion has been updated with solution chosen. 

Realtime Authorization for Broadcast and Presence is now available in Public Beta.

See the [official documentation](https://supabase.com/docs/guides/realtime/authorization).

---

## Overview

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.

## Usage

### Creating Realtime Policies

Using Studio’s SQL editor you can set RLS rules against the table `realtime.messages` which will define the rules for your users.

```sql
CREATE POLICY "presence sync and broadcast listen to authenticated users"
ON realtime.messages FOR SELECT
TO authenticated
USING ( true );

CREATE POLICY "presence track and broadcast send to authenticated users"
ON realtime.messages FOR INSERT
TO authenticated
WITH CHECK ( true );
```

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.
<img width="797" alt="Example schema to be used in RLS policies" src="https://github.com/supabase/supabase/assets/1697301/93cac144-a35e-42cd-8f92-c890a8c3f8e4" />
We'll use this example schema to be showcase RLS policies limiting Realtime functionality

We can build more complex RLS rules using this information:

```sql
-- Set permission for authenticated users to only listen for Broadcast messages
CREATE POLICY "authenticated can listen to broadcast only on their topics"
ON realtime.messages FOR SELECT
TO authenticated
USING ( 
	exists(
      select 1
      from public.rooms r join public.rooms_users ru on r.id = ru.room_id
      where ru.user_id = auth.uid()
        and r.name = realtime.topic()
        and realtime.messages.extension = 'broadcast'
  )
);
-- Set permission for authenticated users to only write for Broadcast messages
CREATE POLICY "authenticated can write to broadcast only on their topics"
ON realtime.messages FOR INSERT
TO authenticated
WITH CHECK ( 
	exists(
	  select 1
    from public.rooms r join public.rooms_users ru on r.id = ru.room_id
    where ru.user_id = auth.uid()
      and r.name = realtime.topic()
      and realtime.messages.extension = 'broadcast'
  )
)
```

### Testing Authorization

Now to test it we can use a quick deno script by creating a `index.ts`

```tsx
// Run with deno run --allow-net --allow-env --allow-read --allow-ffi index.ts
import { createClient } from "npm:@supabase/supabase-js@2.38.5";
const url = "https://<project_ref>.supabase.com";
const apikey = "<api_key>";

const client = createClient(url, apikey);

const channel = client.channel("channel_1", {
  config: { broadcast: { self: true }, private: true},
});
channel
  .on("broadcast", { event: "test" }, (payload) => console.log(payload))
  .on("presence", { event: "join" }, (payload) => console.log(payload))
  .on("presence", { event: "leave" }, (payload) => console.log(payload))
  .subscribe((status: string, err: any) => {
    if (status === "SUBSCRIBED") {
      console.log("Connected!");
    } else {
      console.error(err);
    }
  });

```

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. 

```tsx
import { createClient } from "npm:@supabase/supabase-js@2.38.5";
const url = "https://<project_ref>.supabase.co";
const apikey = "<api_key>";

const client = createClient(url, apikey);

await client.auth.signInWithPassword({
  email: "<email>",
  password: "<password>",
});

client.realtime.setAuth(
  (await client.auth.getSession()).data.session.access_token
);
const channel = client.channel("channel_1", {
  config: { broadcast: { self: true }, private: true },
});
channel
  .on("broadcast", { event: "test" }, (payload) => console.log(payload))
  .on("presence", { event: "join" }, (payload) => console.log(payload))
  .on("presence", { event: "leave" }, (payload) => console.log(payload))
  .subscribe((status: string, err: any) => {
    if (status === "SUBSCRIBED") {
      console.log("Connected!");
    } else {
      console.error(err);
    }
  });
```

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.

### Migrating from Public Channels

On connect, you need to send in the configuration that the channel will be `private: true`

### Client library

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](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.

## How it works

### Connection context

When you connect with Realtime we set a connection configuration with your JWT, Topic and Headers using the following query:

```sql
SELECT
	set_config('role', $1, true),
	set_config('realtime.topic', $2, true),
	set_config('request.jwt', $4, true),
	set_config('request.jwt.claims', $6, true),
	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 

```sql
SELECT realtime.topic();

-- Usage example
CREATE POLICY "authenticated users can only write to topic named foo"
ON realtime.messages FOR INSERT
TO authenticated
WITH CHECK ( realtime.topic() = 'foo' );
```

### Applying RLS Policies

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.

![87d33215-313f-42c6-b775-f2e8671206e5](https://github.com/supabase/supabase/assets/1697301/0c3e3033-6bfb-4053-a947-fc48526af578)

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.
