I am having trouble getting realtime data from my Supabase database when using Row level security (RLS) and a custom JWT.
When getting data from the database normally things work as expected, but when I watch for changes using the Realtime functionality no events fire.
Here is my code (I'm using Astro):
import { createClient } from "@supabase/supabase-js";
const supabase = await createClient(import.meta.env.PUBLIC_SUPABASE_URL, import.meta.env.PUBLIC_SUPABASE_KEY, {
global: {
headers: {
Authorization: `Bearer ${jwt}`,
},
},
});
supabase.from('notes').select().then(({data, error}) => {
console.log(data, error) // This works, my notes data is in the console.
})
// I am unsure if these 2 lines are needed but they endless googling lead me to adding them.
supabase.realtime.setAuth(jwt)
supabase.realtime.accessToken = jwt
// HERE IS THE PROBLEM. When the notes table is updated, I don't see anything in the console.
supabase
.channel('notes')
.on('postgres_changes', { event: '*', schema: '*', table: 'notes' }, payload => {
console.log("notes db change detected:", payload) // This log never appears
})
.subscribe()
My RLS on the notes table currently looks like this. Obviously I want to check a bit more than just if the JWT contains an email, but if I could get this to work it would be a good start.
(((current_setting('request.jwt.claims'::text, true))::json #>> '{email}'::text[]) IS NOT NULL)
This failing suggests that the JWT doesn't contain an email, but then why does is work when fetching data normally (not watching for realtime changes)?
Here is my JWT setup (using Jose library).
const signingSecret = import.meta.env.SUPABASE_JWT_SECRET;
if (signingSecret) {
session.supabaseAccessToken = await new jose.SignJWT({ email: user.email, role: "authenticated" })
.setProtectedHeader({ alg: 'HS256' })
.setAudience("authenticated")
.setExpirationTime(Math.floor(new Date(session.expires).getTime() / 1000))
.setSubject(user.id)
.sign(new TextEncoder().encode(signingSecret))
}
I have tried testing this in the Supabase inspector tab (joining the notes channel) and I get the following results:
- When a
service_roleuser, I can see the realtime updates. - When an
anon_user, I cannot see the realtime updates.
I have also taken a look at my developer tools network tab and I can see that the websocket connection is working (I can see the heartbeat event and sync events), but no postgres events are being transmitted.
I am finding it very difficult to debug the RLS policy because I am not very familiar with databases and there doesn't appear to be any easy way to log what is going on when an RLS policy fails.
