No view in an exposed schema exposes auth.users data to anon or authenticated
Why it matters
The auth schema holds every user's email, phone, password hash, confirmation and recovery tokens, and raw metadata, and it is deliberately not exposed through the API. A developer who wants a 'users list' or to join profiles to emails writes create view public.users as select id, email, raw_user_meta_data from auth.users. If that view runs as its owner (the default) and anon or authenticated can read it, one API call returns every user's email address and metadata to anyone with the public key, and the view bypasses the lock on auth.users entirely. Even with security_invoker the pattern is wrong because the caller has no rights on auth.users and the view either errors or gets 'fixed' by loosening grants. AI tools reach for this shortcut constantly because the dashboard shows auth.users as the place users live. The advisor flags it as lint 0002.
Severity rationale
Critical because one unauthenticated request can return every registered user's email and metadata, turning the identity table into a public contact list for phishing and account takeover.
Remediation
Do not read auth.users from the API. Copy the few fields the app needs into your own table with a trigger and protect that table with RLS:
create table public.profiles (
id uuid primary key references auth.users on delete cascade,
display_name text
);
alter table public.profiles enable row level security;
create function private.handle_new_user() returns trigger
language plpgsql security definer set search_path = '' as $$
begin
insert into public.profiles (id, display_name)
values (new.id, new.raw_user_meta_data ->> 'display_name');
return new;
end $$;
If a view over auth already exists, drop it or move it to a non-exposed schema, and revoke all on <view> from anon, authenticated.
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Supabase RLS & Keys audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- cwe:4.14 · CWE-284 — Improper Access Control
- cwe:4.14 · CWE-285 — Improper Authorization
- cwe:4.14 · CWE-200 — Exposure of Sensitive Information to an Unauthorized Actor
- owasp:2021 · A01 — Broken Access Control
- external · 0002_auth_users_exposed — Supabase database advisor lint 0002 auth.users exposed to the API
Taxons
History
- 2026-10-03·v1.0.0·Initial supabase-rls-keys v1 authoring; views and materialized views over auth.* must not be readable by anon or authenticated. Plain views over other tables are covered by views-use-security-invoker.·by supabase-rls-keys-v1-build