create schema if not exists minegeo_private; revoke all on schema minegeo_private from public, anon, authenticated; grant usage on schema minegeo_private to service_role; create table minegeo_private.legacy_password_claims ( user_id uuid primary key references auth.users(id) on delete cascade, claimed_at timestamptz not null default now() ); alter table minegeo_private.legacy_password_claims enable row level security; revoke all on minegeo_private.legacy_password_claims from public, anon, authenticated; grant select, insert on minegeo_private.legacy_password_claims to service_role; create policy "Server reads password claims" on minegeo_private.legacy_password_claims for select to service_role using (true); create policy "Server creates password claims" on minegeo_private.legacy_password_claims for insert to service_role with check (true); -- This private helper reads only password eligibility, never returns a hash, -- and never stores a plaintext password. Only the trusted server can call it. create function minegeo_private.legacy_password(p_user uuid, p_claim boolean) returns boolean language plpgsql security definer set search_path = '' as $$ declare eligible boolean; claimed uuid; begin if auth.uid() is not null and auth.uid() <> p_user then return false; end if; select coalesce(encrypted_password, '') = '' and email is not null and email_confirmed_at is not null and not coalesce(is_anonymous, false) into eligible from auth.users where id = p_user for update; if not coalesce(eligible, false) then return false; end if; if not p_claim then return not exists(select 1 from minegeo_private.legacy_password_claims where user_id = p_user); end if; insert into minegeo_private.legacy_password_claims(user_id) values(p_user) on conflict(user_id) do nothing returning user_id into claimed; return claimed is not null; end; $$; revoke all on function minegeo_private.legacy_password(uuid,boolean) from public, anon, authenticated; grant execute on function minegeo_private.legacy_password(uuid,boolean) to service_role; create function public.legacy_password_needed(p_user uuid) returns boolean language sql security invoker set search_path = '' as $$ select minegeo_private.legacy_password(p_user, false); $$; create function public.claim_legacy_password(p_user uuid) returns boolean language sql security invoker set search_path = '' as $$ select minegeo_private.legacy_password(p_user, true); $$; revoke all on function public.legacy_password_needed(uuid) from public, anon, authenticated; revoke all on function public.claim_legacy_password(uuid) from public, anon, authenticated; grant execute on function public.legacy_password_needed(uuid) to service_role; grant execute on function public.claim_legacy_password(uuid) to service_role;