Module P-6·24 min read

Roles, privileges, Row-Level Security policies, and multi-tenant data isolation — the production access model.

JJS
Written by Jatin Jain Saraf · Senior Software Engineer

P-6 — Authentication, Row-Level Security, and Access Control

Most tutorials treat PostgreSQL authentication as a one-liner: create a superuser, give it a password, connect. That works in development. In production it is a security incident waiting to happen.

This module covers the PostgreSQL access model from first principles — roles, privileges, and the Row-Level Security system that lets the database enforce data boundaries between tenants without any application-layer code. By the end you will have the mental model to design a proper production access architecture, and a working multi-tenant RLS setup you can adapt for real projects.


Why the Default Setup Is Dangerous

When you install PostgreSQL locally, the postgres superuser is created automatically. When you deploy to Heroku, Railway, or Supabase, you get a user with broad privileges. Most tutorials say "connect as this user from your app" and move on.

Here is what that means in practice:

  • Your application can DROP TABLE its own data
  • A SQL injection vulnerability has DELETE access to every table
  • One compromised connection can exfiltrate the entire database
  • There is no way to give a read-only analytics tool limited access

The fix is a proper role hierarchy with the principle of least privilege. PostgreSQL has a first-class system for this.


Roles Are Everything

PostgreSQL does not have "users" and "groups" as separate concepts. It has roles. A role can:

  • Log in (it becomes a "user")
  • Own objects (tables, sequences, schemas)
  • Have other roles granted to it (role inheritance)
  • Grant its privileges to other roles

Everything is a role. CREATE USER is just syntax sugar for CREATE ROLE ... WITH LOGIN.

Creating Roles

sql

When app_user logs in, it has all privileges that readonly_role has. This is role inheritance.

Role Attributes

Key attributes when creating a role:

AttributeMeaning
LOGINCan authenticate and open a session
SUPERUSERBypasses all access checks — avoid in production
CREATEDBCan create new databases
CREATEROLECan create other roles
REPLICATIONUsed for streaming replication connections
BYPASSRLSBypasses Row-Level Security policies
PASSWORD 'x'Sets the login password
sql

Granting and Revoking Privileges

PostgreSQL privileges are granted on specific objects: tables, sequences, schemas, databases, functions.

The GRANT Syntax

sql

ALTER DEFAULT PRIVILEGES is the one most people miss. Without it, every new table you create needs a separate GRANT. With it, new tables automatically get the right privileges.

Revoking Privileges

sql

Checking What a Role Can Do

sql

A Production Role Architecture

Here is a pattern that works well for production applications:

sql

Your migration tool connects as myapp_owner. Your application connects as myapp_app. Your BI tool connects as myapp_reader. A SQL injection in the app cannot drop tables or read tables the app role has no access to.


Row-Level Security (RLS)

Role-based access control gets you table-level isolation. Row-Level Security gets you row-level isolation. This is the mechanism behind Supabase's per-user data access, multi-tenant SaaS isolation, and any system where different users should see different subsets of the same table.

How RLS Works

When RLS is enabled on a table, every query against that table automatically gets an invisible WHERE clause appended — the clause defined by the policy. No application code required.

sql

Sign in to keep reading

The rest of this module is free — sign in with Google to unlock it and track your progress.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.