-- Multiple provider identities may resolve to one canonical app user. -- Alias writes are owner-only: the runtime app role can read through RLS but -- receives no INSERT/UPDATE/DELETE policy. CREATE TABLE IF NOT EXISTS app.auth_identity_alias ( external_id TEXT PRIMARY KEY CHECK (external_id ~ '^[a-z0-9_-]+:.+$'), user_id UUID NOT NULL REFERENCES app.app_user(user_id) ON DELETE RESTRICT, source_user_id UUID REFERENCES app.app_user(user_id) ON DELETE SET NULL, linked_at TIMESTAMPTZ NOT NULL DEFAULT now(), linked_by TEXT NOT NULL, reason TEXT NOT NULL, CHECK (source_user_id IS NULL OR source_user_id <> user_id) ); CREATE INDEX IF NOT EXISTS idx_auth_identity_alias_user ON app.auth_identity_alias(user_id, linked_at DESC); ALTER TABLE app.auth_identity_alias ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS p_auth_identity_alias_select ON app.auth_identity_alias; CREATE POLICY p_auth_identity_alias_select ON app.auth_identity_alias FOR SELECT USING (true); ALTER TABLE app.auth_session ADD COLUMN IF NOT EXISTS login_email TEXT NOT NULL DEFAULT ''; UPDATE app.auth_session AS s SET login_email = lower(u.email) FROM app.app_user AS u WHERE u.user_id = s.user_id AND s.login_email = '' AND u.email IS NOT NULL;