-- Provider identities allow passwordless platform users while legacy credentials remain valid. ALTER TABLE users ALTER COLUMN username DROP NOT NULL, ALTER COLUMN password_hashed DROP NOT NULL; DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM pg_constraint WHERE conname = 'users_credentials_pair_check' ) THEN ALTER TABLE users ADD CONSTRAINT users_credentials_pair_check CHECK ( (username IS NULL AND password_hashed IS NULL) OR (username IS NOT NULL AND password_hashed IS NOT NULL) ); END IF; END $$; CREATE TABLE IF NOT EXISTS provider_identities ( id UUID PRIMARY KEY, user_uuid UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, provider VARCHAR(50) NOT NULL, provider_user_id VARCHAR(255) NOT NULL, display_name VARCHAR(255), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), CONSTRAINT provider_identities_provider_user_unique UNIQUE (provider, provider_user_id), CONSTRAINT provider_identities_user_provider_unique UNIQUE (user_uuid, provider) ); CREATE INDEX IF NOT EXISTS provider_identities_user_idx ON provider_identities (user_uuid); CREATE TABLE IF NOT EXISTS api_keys ( id UUID PRIMARY KEY, name VARCHAR(255) NOT NULL, key_type VARCHAR(20) NOT NULL, user_uuid UUID REFERENCES users(id) ON DELETE CASCADE, service_name VARCHAR(255), key_prefix VARCHAR(20) NOT NULL, key_hash CHAR(64) NOT NULL UNIQUE, scopes JSONB NOT NULL DEFAULT '[]'::JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), expires_at TIMESTAMPTZ, revoked_at TIMESTAMPTZ, last_used_at TIMESTAMPTZ, CONSTRAINT api_keys_type_check CHECK (key_type IN ('user', 'service')), CONSTRAINT api_keys_scopes_check CHECK (jsonb_typeof(scopes) = 'array'), CONSTRAINT api_keys_owner_check CHECK ( (key_type = 'user' AND user_uuid IS NOT NULL AND service_name IS NULL AND scopes = '[]'::JSONB) OR (key_type = 'service' AND user_uuid IS NULL AND service_name IS NOT NULL) ) ); CREATE INDEX IF NOT EXISTS api_keys_prefix_idx ON api_keys (key_prefix); CREATE INDEX IF NOT EXISTS api_keys_user_idx ON api_keys (user_uuid, created_at DESC) WHERE key_type = 'user'; CREATE INDEX IF NOT EXISTS api_keys_service_idx ON api_keys (service_name, created_at DESC) WHERE key_type = 'service';