Authorization and Roles Contract
Status: Canonical (Draft) Last Updated: 2026-02-06 Owner: Engineering
Purpose
This document defines the authorization (AuthZ) model and role system for CAIRL.
It answers one question only:
Given an authenticated user, what actions are they allowed to perform?
This document is authoritative and defines system-wide invariants. All features, specs, flows, and implementations MUST comply.
Scope
This contract applies to:
- All application features
- All Next.js server code (Route Handlers, Server Actions)
- All Postgres tables protected by Supabase Row Level Security (RLS)
- All admin and moderation functionality
No feature may override this contract.
Definitions
- AuthN (Authentication): Verifying who the user is. Handled by NextAuth.
- AuthZ (Authorization): Determining what the user can do. Defined here.
- Role: A coarse-grained classification that affects authorization.
- RLS: Row Level Security enforced at the Postgres layer via Supabase.
- User-owned data: Data belonging to a specific user.
- System-owned data: Data controlled by the platform, not an individual user.
Authentication Source of Truth
- Authentication is handled by NextAuth.
- A valid NextAuth session is required for all authenticated actions.
- The application user identifier is a UUID stored in Postgres.
NextAuth is responsible only for identity. NextAuth does NOT determine authorization.
Authorization Enforcement Model
Authorization is enforced using a defense-in-depth approach:
Postgres RLS (Primary Authority)
- RLS policies MUST restrict access to user-owned rows.
- RLS is the final enforcement layer and MUST NOT be bypassed.
Server-Side Authorization Checks
- Next.js Route Handlers and Server Actions MUST perform role checks.
- These checks supplement RLS and provide clearer error handling.
Client-Side Gating (Non-authoritative)
- UI may hide or disable actions.
- UI gating MUST NOT be relied upon for security.
Role Model
Defined Roles
The system currently defines the following roles:
user
- Default role for all authenticated users
- May only access their own data
- Subject to subscription and feature gating
admin
- Elevated role for internal operators
- May access system-owned data
- May perform moderation and enforcement actions
No other roles are currently supported.
Role Storage (Current State)
Roles are currently represented in the application database with known issues:
- Role information exists on the user record.
- There is a known duplicate / inconsistent admin role representation.
- This inconsistency is acknowledged and permitted temporarily.
This document intentionally describes behavior, not ideal structure.
Role Resolution Rules
When determining a user’s role:
- The system MUST resolve the effective role server-side.
- If multiple role indicators exist:
- admin takes precedence over user
- Absence of an admin indicator implies user role.
If role resolution is ambiguous, the system MUST default to the least-privileged role.
Admin Authorization Invariants
The following actions are ADMIN-ONLY and MUST be enforced server-side:
- Viewing or modifying system allowlists
- Approving or denying user requests requiring moderation
- Accessing data across multiple users
- Viewing enforcement, abuse, or suppression data
- Performing irreversible system actions
UI gating alone is insufficient.
User Authorization Invariants
All users (including admins acting as users):
- May only access user-owned rows via RLS
- May not bypass quotas, limits, or safety controls
- May not impersonate another user without explicit tooling
Admin privileges do not bypass safety invariants unless explicitly stated.
Supabase Row Level Security Requirements
The following rules are mandatory:
- All tables containing user-owned data MUST have RLS enabled.
- RLS policies MUST reference the authenticated user identifier.
- No client-side Supabase key may bypass RLS.
- Service role keys MUST be restricted to server-only contexts.
Any table without RLS MUST be explicitly documented and justified.
Server-Side Privileged Access
Use of elevated database access (e.g., service role keys) is permitted ONLY:
- In server-side code
- For clearly documented administrative operations
- With explicit scope and justification
Privileged access MUST NOT be used for normal user flows.
Subscription and Feature Gating
Roles are orthogonal to subscription tiers.
- Role determines who you are
- Tier determines what features you can use
Admin role does NOT imply unlimited feature access unless specified.
Error Handling and Enforcement
Authorization failures MUST result in:
- A clear server-side rejection
- A consistent error code
- No partial side effects
Authorization failures MUST be logged for auditing.
Migration and Cleanup Notes
The current role system is known to be imperfect.
Future improvements MAY include:
- Normalizing roles into a separate table
- Removing duplicate admin indicators
- Introducing scoped admin permissions
Such changes MUST be introduced via:
- A new or updated contract
- A migration plan
- Updated RLS policies
Non-Negotiable Rules
- Authorization MUST be enforced server-side.
- RLS MUST be the final enforcement layer.
- Ambiguity defaults to least privilege.
- UI checks are advisory only.
- No feature may weaken these rules.
References
- NextAuth documentation
- Supabase Row Level Security documentation
docs/governance/doc-authority.new.md
End of Document