RLS context issue.

publishSession: {
code: '42501',
details: null,
hint: null,
message: 'new row violates row-level security policy for table "sessions"'
}
POST /api/sessions/publish 500 1203.073 ms - 37
publishSession: {
code: '42501',
details: null,
hint: null,
message: 'new row violates row-level security policy for table "sessions"'
}
POST /api/sessions/publish 500 1203.073 ms - 37
8 Replies
!اUltimecia
!اUltimeciaOP2w ago
const jwt = require('jsonwebtoken')

const auth = (req, res, next) => {
try {
const h = req.get('authorization') || req.get('Authorization')
if (!h || !h.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Token missing' })
}

const token = h.split(' ')[1]
const decoded = jwt.verify(token, process.env.SUPABASE_JWT_SECRET)

req.user = {
id: decoded.sub,
_id: decoded.sub,
email: decoded.email || null
}

next()
} catch (err) {
if (err.name === 'JsonWebTokenError') {
return res.status(401).json({ error: 'Invalid token' })
}
if (err.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Expired token' })
}

console.error('Auth error:', err)
res.status(500).json({ error: 'Authentication error' })
}
}

module.exports = auth
const jwt = require('jsonwebtoken')

const auth = (req, res, next) => {
try {
const h = req.get('authorization') || req.get('Authorization')
if (!h || !h.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Token missing' })
}

const token = h.split(' ')[1]
const decoded = jwt.verify(token, process.env.SUPABASE_JWT_SECRET)

req.user = {
id: decoded.sub,
_id: decoded.sub,
email: decoded.email || null
}

next()
} catch (err) {
if (err.name === 'JsonWebTokenError') {
return res.status(401).json({ error: 'Invalid token' })
}
if (err.name === 'TokenExpiredError') {
return res.status(401).json({ error: 'Expired token' })
}

console.error('Auth error:', err)
res.status(500).json({ error: 'Authentication error' })
}
}

module.exports = auth
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY, {
auth: { autoRefreshToken: false,
persistSession: false,
detectSessionInUrl: false }
})
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY, {
auth: { autoRefreshToken: false,
persistSession: false,
detectSessionInUrl: false }
})
exports.publishSession = async (req, res) => {
try {
const userId = req.user._id
const {
id,
title,
tags = [],
video_url = null,
description = '',
difficulty = 'Beginner'
} = req.body

if (!title || typeof title !== 'string') {
return res.status(400).json({ error: 'Title is required' })
}

const payload = {
user_id: userId,
title,
tags,
video_url,
description,
difficulty,
status: 'published'
}

let query
if (id) {
query = supabase
.from('sessions')
.update(payload)
.eq('id', id)
.eq('user_id', userId)
.select()
.single()
} else {
query = supabase
.from('sessions')
.insert(payload)
.select()
.single()
}

const { data: session, error } = await query
if (error) throw error
const profiles = await fetchProfilesForSessions([session])
res.status(id ? 200 : 201).json({ data: { session: mapRow(session, profiles) } })
} catch (err) {
console.error('publishSession:', err)
res.status(500).json({ error: 'Failed to publish session' })
}
}
exports.publishSession = async (req, res) => {
try {
const userId = req.user._id
const {
id,
title,
tags = [],
video_url = null,
description = '',
difficulty = 'Beginner'
} = req.body

if (!title || typeof title !== 'string') {
return res.status(400).json({ error: 'Title is required' })
}

const payload = {
user_id: userId,
title,
tags,
video_url,
description,
difficulty,
status: 'published'
}

let query
if (id) {
query = supabase
.from('sessions')
.update(payload)
.eq('id', id)
.eq('user_id', userId)
.select()
.single()
} else {
query = supabase
.from('sessions')
.insert(payload)
.select()
.single()
}

const { data: session, error } = await query
if (error) throw error
const profiles = await fetchProfilesForSessions([session])
res.status(id ? 200 : 201).json({ data: { session: mapRow(session, profiles) } })
} catch (err) {
console.error('publishSession:', err)
res.status(500).json({ error: 'Failed to publish session' })
}
}
idk what am i doing wrong
create table if not exists public.profiles (
id uuid primary key references auth.users(id) on delete cascade,
email text unique
);

-- Sessions table
create table if not exists public.sessions (
id uuid primary key default gen_random_uuid(),
user_id uuid not null references auth.users(id) on delete cascade,
title text not null,
tags text[] not null default '{}',
video_url text,
description text default '',
difficulty text not null default 'Beginner' check (difficulty in ('Beginner','Intermediate','Advanced')),
status text not null default 'draft' check (status in ('draft','published')),
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);

-- RLS
alter table public.sessions enable row level security;

-- Read published sessions by anyone
drop policy if exists "read published sessions" on public.sessions;
create policy "read published sessions" on public.sessions
for select using (status = 'published');

-- Owners can do everything on their own rows
drop policy if exists "owner full access" on public.sessions;
create policy "owner full access" on public.sessions
for all
using (auth.uid() = user_id)
with check (auth.uid() = user_id);
create table if not exists public.profiles (
id uuid primary key references auth.users(id) on delete cascade,
email text unique
);

-- Sessions table
create table if not exists public.sessions (
id uuid primary key default gen_random_uuid(),
user_id uuid not null references auth.users(id) on delete cascade,
title text not null,
tags text[] not null default '{}',
video_url text,
description text default '',
difficulty text not null default 'Beginner' check (difficulty in ('Beginner','Intermediate','Advanced')),
status text not null default 'draft' check (status in ('draft','published')),
created_at timestamptz not null default now(),
updated_at timestamptz not null default now()
);

-- RLS
alter table public.sessions enable row level security;

-- Read published sessions by anyone
drop policy if exists "read published sessions" on public.sessions;
create policy "read published sessions" on public.sessions
for select using (status = 'published');

-- Owners can do everything on their own rows
drop policy if exists "owner full access" on public.sessions;
create policy "owner full access" on public.sessions
for all
using (auth.uid() = user_id)
with check (auth.uid() = user_id);
the whole setup
Fieryduck82579
Are you signed-in when you query Supabase? I suspect the RLS policies apply to anon/public role (it is missing a to <role> clause), but the query might be performed using the authenticated role.
garyaustin
garyaustin2w ago
What supabase call is getting the RLS error? You do both an insert and an update. Check the API Gateway log for your failing call and see what role the user is making the call.
!اUltimecia
!اUltimeciaOP2w ago
code fall backs to insert in my case which is ok but even when the user is authenticated the insert sent is anon
!اUltimecia
!اUltimeciaOP2w ago
No description
No description
garyaustin
garyaustin2w ago
If the dashboard log shows anon then your code is not getting a user session or the authenticated header set at the time of your call. So not an RLS issue.
!اUltimecia
!اUltimeciaOP7d ago
am i stupid to do this in my node backend
No description
!اUltimecia
!اUltimeciaOP7d ago
im signing in frontend both different client

Did you find this page helpful?