-- 016: username-based first-party auth. -- Usernames are the login identity (external_id = local:); email is -- optional metadata for future password recovery. Existing email-keyed users -- get a username derived from their email local-part (or display name). ALTER TABLE users ADD COLUMN username text; UPDATE users SET username = COALESCE( NULLIF(regexp_replace(lower(split_part(coalesce(email, ''), '@', 1)), '[^a-z0-9_.-]', '', 'g'), ''), NULLIF(regexp_replace(lower(display_name), '[^a-z0-9_.-]', '', 'g'), ''), 'member' ); -- Deterministic dedupe: colliding candidates get a short id suffix. UPDATE users u SET username = u.username || '_' || substr(u.id::text, 1, 4) WHERE EXISTS (SELECT 1 FROM users v WHERE v.username = u.username AND v.id < u.id); ALTER TABLE users ALTER COLUMN username SET NOT NULL; CREATE UNIQUE INDEX users_username_key ON users (username); ALTER TABLE users ALTER COLUMN email DROP NOT NULL; GRANT SELECT, INSERT, UPDATE, DELETE ON users TO sp_app;