Bug report
Hello Supabase Team,
I am encountering a persistent issue with Row Level Security (RLS) policies on my ideas table, resulting in a 403 Forbidden error (PostgREST error code 42501: new row violates row-level security policy for table "ideas") when attempting to UPDATE a row (specifically, performing a logical delete by setting deleted_at), even though all checks seem to pass.
Context:
- Table:
public.ideas
- Operation:
PATCH /rest/v1/ideas?uid=eq.<idea_uid> (from Supabase JS client update method) to set deleted_at = NOW().
- Target Row:
uid = '1b046deb-76ec-8168-afa8-f5364f00e612', user_uid = 'd1dc7a43-6a95-426f-9c3c-144742edef9f'
- Authenticated User:
auth.uid() = 'd1dc7a43-6a95-426f-9c3c-144742edef9f'
Troubleshooting Steps Taken:
- Client-Side Authentication Verified: Used
supabaseClient.auth.getUser() right before the update call, confirming the client is authenticated with the correct user ID (d1dc7a43-...).
- Request Headers Verified: Inspected the
PATCH request headers in browser dev tools. The Authorization: Bearer <token> header is present, and the JWT payload correctly contains "sub": "d1dc7a43-...". The token is valid.
- Data Integrity Verified: Confirmed that the target row (
uid = '1b046deb-...') in the ideas table indeed has user_uid = 'd1dc7a43-...'.
- Table Permissions Verified: Confirmed using
information_schema.role_table_grants that the authenticated role has UPDATE privileges on the public.ideas table.
- RLS Policy Simplified (UPDATE):
- Initially, the
UPDATE policy had both USING (auth.uid() = user_uid) and WITH CHECK (auth.uid() = user_uid). The 403 error occurred.
- Crucially, I modified the
UPDATE policy to remove the WITH CHECK clause, leaving only USING (auth.uid() = user_uid). The 403 Forbidden error still persists even with this simplified policy.
- Other RLS Policies: Checked
pg_policies - INSERT has WITH CHECK (auth.uid() = user_uid), SELECT has USING (((auth.uid() = user_uid) OR (shared = true)) AND (deleted_at IS NULL)). No other potentially conflicting policies found.
- Triggers: Only have a standard trigger
BEFORE UPDATE to set updated_at = NOW(). This trigger does not modify user_uid.
- Database Webhooks/Functions: No webhooks or functions are triggered by updates on the
ideas table.
Current RLS Policies on public.ideas:
[
{
"schemaname": "public",
"tablename": "ideas",
"policyname": "Users can only create their own ideas",
"permissive": "PERMISSIVE",
"roles": "{public}",
"cmd": "INSERT",
"qual": null,
"with_check": "(auth.uid() = user_uid)"
},
{
"schemaname": "public",
"tablename": "ideas",
"policyname": "Users can only update their own ideas (no check)", // Note: WITH CHECK removed
"permissive": "PERMISSIVE",
"roles": "{public}",
"cmd": "UPDATE",
"qual": "(auth.uid() = user_uid)", // USING clause
"with_check": null // No WITH CHECK
},
{
"schemaname": "public",
"tablename": "ideas",
"policyname": "Users can view their own non-deleted ideas and shared non-delet",
"permissive": "PERMISSIVE",
"roles": "{public}",
"cmd": "SELECT",
"qual": "(((auth.uid() = user_uid) OR (shared = true)) AND (deleted_at IS NULL))",
"with_check": null
}
]
Problem Summary:
Despite verifying client authentication, token validity, data integrity, table permissions, and simplifying the RLS UPDATE policy to only USING (auth.uid() = user_uid), the operation is still blocked by RLS (403 Forbidden, code 42501). This suggests a potential issue within the Supabase backend regarding how auth.uid() is evaluated or compared in the USING clause for this specific UPDATE operation.
Could you please investigate why the RLS policy might be failing even when all apparent conditions are met?
Thank you for your assistance.
System information
- OS: macOS 15.3.2(24D81)
- package.json
- "@supabase/supabase-js": "^2.47.10",
- @supabase/auth-helpers-nextjs@0.10.0
- "next": "^14.2.8",
- Node.js Version18.x
- Browser: Arn
- Version1.91.0 (61725)
- Chromium Engine Version 135.0.7049.96
Screenshots
If applicable, add screenshots to help explain your problem.
Additional context
Add any other context about the problem here.
Bug report
Hello Supabase Team,
I am encountering a persistent issue with Row Level Security (RLS) policies on my
ideastable, resulting in a403 Forbiddenerror (PostgREST error code42501:new row violates row-level security policy for table "ideas") when attempting toUPDATEa row (specifically, performing a logical delete by settingdeleted_at), even though all checks seem to pass.Context:
public.ideasPATCH /rest/v1/ideas?uid=eq.<idea_uid>(from Supabase JS clientupdatemethod) to setdeleted_at = NOW().uid = '1b046deb-76ec-8168-afa8-f5364f00e612',user_uid = 'd1dc7a43-6a95-426f-9c3c-144742edef9f'auth.uid() = 'd1dc7a43-6a95-426f-9c3c-144742edef9f'Troubleshooting Steps Taken:
supabaseClient.auth.getUser()right before theupdatecall, confirming the client is authenticated with the correct user ID (d1dc7a43-...).PATCHrequest headers in browser dev tools. TheAuthorization: Bearer <token>header is present, and the JWT payload correctly contains"sub": "d1dc7a43-...". The token is valid.uid = '1b046deb-...') in theideastable indeed hasuser_uid = 'd1dc7a43-...'.information_schema.role_table_grantsthat theauthenticatedrole hasUPDATEprivileges on thepublic.ideastable.UPDATEpolicy had bothUSING (auth.uid() = user_uid)andWITH CHECK (auth.uid() = user_uid). The403error occurred.UPDATEpolicy to remove theWITH CHECKclause, leaving onlyUSING (auth.uid() = user_uid). The403 Forbiddenerror still persists even with this simplified policy.pg_policies-INSERThasWITH CHECK (auth.uid() = user_uid),SELECThasUSING (((auth.uid() = user_uid) OR (shared = true)) AND (deleted_at IS NULL)). No other potentially conflicting policies found.BEFORE UPDATEto setupdated_at = NOW(). This trigger does not modifyuser_uid.ideastable.Current RLS Policies on
public.ideas:[ { "schemaname": "public", "tablename": "ideas", "policyname": "Users can only create their own ideas", "permissive": "PERMISSIVE", "roles": "{public}", "cmd": "INSERT", "qual": null, "with_check": "(auth.uid() = user_uid)" }, { "schemaname": "public", "tablename": "ideas", "policyname": "Users can only update their own ideas (no check)", // Note: WITH CHECK removed "permissive": "PERMISSIVE", "roles": "{public}", "cmd": "UPDATE", "qual": "(auth.uid() = user_uid)", // USING clause "with_check": null // No WITH CHECK }, { "schemaname": "public", "tablename": "ideas", "policyname": "Users can view their own non-deleted ideas and shared non-delet", "permissive": "PERMISSIVE", "roles": "{public}", "cmd": "SELECT", "qual": "(((auth.uid() = user_uid) OR (shared = true)) AND (deleted_at IS NULL))", "with_check": null } ]Problem Summary:
Despite verifying client authentication, token validity, data integrity, table permissions, and simplifying the RLS
UPDATEpolicy to onlyUSING (auth.uid() = user_uid), the operation is still blocked by RLS (403 Forbidden, code42501). This suggests a potential issue within the Supabase backend regarding howauth.uid()is evaluated or compared in theUSINGclause for this specificUPDATEoperation.Could you please investigate why the RLS policy might be failing even when all apparent conditions are met?
Thank you for your assistance.
System information
Screenshots
If applicable, add screenshots to help explain your problem.
Additional context
Add any other context about the problem here.