{
  "id": 9912657,
  "title": "RLS says yes and Postgres still says permission denied: the 403 family I only understood on the second one",
  "url": "https://urgent.news/2026/09/26/rls-says-yes-and-postgres-still-says-permission-denied-the-403-family",
  "topic": "tech",
  "section": "Tech",
  "published": "2026-09-26T05:05:10.000Z",
  "source": {
    "name": "Dev.to",
    "slug": "dev-to",
    "url": "https://dev.to/dexterlung/rls-says-yes-and-postgres-still-says-permission-denied-the-403-family-i-only-understood-on-the-1f3p"
  },
  "original_language": "en",
  "account": "The article explains a complex issue surrounding PostgreSQL security and row-level security (RLS). The core problem is that even when RLS grants permission, users may still receive a \"permission denied\" error when trying to modify data.\n\nThe author explains that RLS and GRANT permissions are two separate mechanisms. While RLS can allow access to rows, GRANT determines which columns a role can write to. The author had implemented an admin RLS policy that allowed admins to edit customer balances, but the underlying table \"users\" had column-level GRANT permissions that restricted writes to the columns \"balance\" and others.\n\nThe key revelation was that the admin role was being treated as an \"authenticated\" role when invoking a PostgreSQL RPC (remote procedure call). This meant the privileged columns were not actually granted to the admin role as they should have been. The solution was to use a SECURITY DEFINER function, which executes as the function owner (postgres) and bypasses the table-level GRANT restrictions. The function first checks the admin identity, then calls the original RPC.\n\nThe author fixed two functions (top-up and deduction) using this approach. After deployment, they discovered the same bug family existed in two other cron maintenance functions (expireBonuses and reconcileBalances). These functions were not intended to be called from the frontend and should have their privileges restricted. The author decided to revoke access for authenticated users and leave the service_role as the only entity with permission.\n\nThe main takeaways are:\n1. When RLS allows access but a permission error occurs, examine column-level GRANT permissions in addition to the row-level policy.\n2. For cases where admins need to write data that is otherwise restricted, wrap the function with SECURITY DEFINER and place the admin check as the first statement inside the function body.\n3. Carefully audit and tailor privileges to each case - not all functions requiring admin actions need the same level of protection.",
  "summary": "RLS says yes and Postgres still says permission denied : the 403 family I only understood on the second one Read on: the layer of privileges RLS cannot reach at all · 繁體中文版 An admin tops up a customer's balance in the back office and the screen returns permission denied for table users . My RLS policy explicitly allows admins. The door is open and I can't get through — because the thing blocking…",
  "key_points": [
    "RLS grants permission but admin still gets \"permission denied\" error",
    "Admin role treated as authenticated when invoking PostgreSQL RPC",
    "Wrap admin functions with SECURITY DEFINER to bypass GRANT restrictions"
  ],
  "editors_take": "This development highlights the need to examine column-level GRANT permissions when row-level security allows access but permission errors occur, revealing a nuanced interplay between security mechanisms.",
  "illustration": null,
  "coverage": {
    "outlets": 1,
    "also_reported_by": []
  },
  "ai_generated": true,
  "disclaimer": "Summaries, key points and the editor’s take are written by software from other outlets’ reporting and may contain errors — always check the linked original."
}