@@ -1,54 +1,29 @@
|
|||||||
-- Add SAML authentication support
|
|
||||||
-- This migration adds SAML SSO functionality including:
|
|
||||||
-- - SAML configurations per organization
|
|
||||||
-- - SAML request/assertion tracking for security
|
|
||||||
-- - Domain verification
|
|
||||||
-- - User SAML subject tracking
|
|
||||||
|
|
||||||
-- Create ENUM for SAML enforcement policies
|
|
||||||
CREATE TYPE saml_enforcement_policy AS ENUM (
|
CREATE TYPE saml_enforcement_policy AS ENUM (
|
||||||
'OFF', -- SAML disabled, must use password
|
'OFF',
|
||||||
'OPTIONAL', -- SAML available but not required (default)
|
'OPTIONAL',
|
||||||
'REQUIRED' -- Everyone must use SAML
|
'REQUIRED'
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Create auth_saml_configurations table
|
|
||||||
CREATE TABLE auth_saml_configurations (
|
CREATE TABLE auth_saml_configurations (
|
||||||
id TEXT PRIMARY KEY,
|
id TEXT PRIMARY KEY,
|
||||||
tenant_id TEXT NOT NULL,
|
tenant_id TEXT NOT NULL,
|
||||||
organization_id TEXT NOT NULL,
|
organization_id TEXT NOT NULL,
|
||||||
email_domain TEXT NOT NULL,
|
email_domain TEXT NOT NULL,
|
||||||
|
|
||||||
-- SAML enabled flag
|
|
||||||
enabled BOOLEAN NOT NULL DEFAULT false,
|
enabled BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
|
||||||
-- Enforcement policy for this SAML configuration
|
|
||||||
enforcement_policy saml_enforcement_policy NOT NULL,
|
enforcement_policy saml_enforcement_policy NOT NULL,
|
||||||
|
|
||||||
-- Identity Provider (IdP) configuration
|
|
||||||
idp_entity_id TEXT NOT NULL,
|
idp_entity_id TEXT NOT NULL,
|
||||||
idp_sso_url TEXT NOT NULL,
|
idp_sso_url TEXT NOT NULL,
|
||||||
idp_certificate TEXT NOT NULL, -- X.509 certificate (PEM format)
|
idp_certificate TEXT NOT NULL,
|
||||||
idp_metadata_url TEXT, -- Optional: for auto-refresh
|
idp_metadata_url TEXT,
|
||||||
|
|
||||||
-- Attribute mapping configuration (using WS-Federation Claims)
|
|
||||||
attribute_email TEXT NOT NULL DEFAULT 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
|
attribute_email TEXT NOT NULL DEFAULT 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress',
|
||||||
attribute_firstname TEXT NOT NULL DEFAULT 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
|
attribute_firstname TEXT NOT NULL DEFAULT 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname',
|
||||||
attribute_lastname TEXT NOT NULL DEFAULT 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',
|
attribute_lastname TEXT NOT NULL DEFAULT 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname',
|
||||||
attribute_role TEXT NOT NULL DEFAULT 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role',
|
attribute_role TEXT NOT NULL DEFAULT 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/role',
|
||||||
|
|
||||||
-- Default role if mapping fails or attribute missing
|
|
||||||
default_role TEXT NOT NULL DEFAULT 'MEMBER',
|
default_role TEXT NOT NULL DEFAULT 'MEMBER',
|
||||||
|
|
||||||
-- Auto-signup settings
|
|
||||||
auto_signup_enabled BOOLEAN NOT NULL DEFAULT false,
|
auto_signup_enabled BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
|
||||||
-- Domain verification fields
|
|
||||||
domain_verified BOOLEAN NOT NULL DEFAULT false,
|
domain_verified BOOLEAN NOT NULL DEFAULT false,
|
||||||
domain_verification_token TEXT,
|
domain_verification_token TEXT,
|
||||||
domain_verified_at TIMESTAMP,
|
domain_verified_at TIMESTAMP,
|
||||||
|
|
||||||
-- Timestamps
|
|
||||||
created_at TIMESTAMP NOT NULL,
|
created_at TIMESTAMP NOT NULL,
|
||||||
updated_at TIMESTAMP NOT NULL,
|
updated_at TIMESTAMP NOT NULL,
|
||||||
|
|
||||||
@@ -56,63 +31,47 @@ CREATE TABLE auth_saml_configurations (
|
|||||||
REFERENCES organizations(id) ON DELETE CASCADE
|
REFERENCES organizations(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Index for fast organization lookup
|
|
||||||
CREATE INDEX idx_auth_saml_configurations_organization_id
|
CREATE INDEX idx_auth_saml_configurations_organization_id
|
||||||
ON auth_saml_configurations(organization_id);
|
ON auth_saml_configurations(organization_id);
|
||||||
|
|
||||||
-- Index for tenant scoping
|
|
||||||
CREATE INDEX idx_auth_saml_configurations_tenant_id
|
CREATE INDEX idx_auth_saml_configurations_tenant_id
|
||||||
ON auth_saml_configurations(tenant_id);
|
ON auth_saml_configurations(tenant_id);
|
||||||
|
|
||||||
-- Unique constraint scoped to organization
|
|
||||||
-- This allows the same domain in different organizations
|
|
||||||
-- while preventing duplicates within the same organization
|
|
||||||
CREATE UNIQUE INDEX idx_saml_config_domain_org_unique
|
CREATE UNIQUE INDEX idx_saml_config_domain_org_unique
|
||||||
ON auth_saml_configurations(organization_id, email_domain)
|
ON auth_saml_configurations(organization_id, email_domain)
|
||||||
WHERE enabled = true AND domain_verified = true;
|
WHERE enabled = true AND domain_verified = true;
|
||||||
|
|
||||||
-- Index for fast domain lookup (for email-based SAML discovery)
|
|
||||||
CREATE INDEX idx_saml_config_email_domain
|
CREATE INDEX idx_saml_config_email_domain
|
||||||
ON auth_saml_configurations(email_domain)
|
ON auth_saml_configurations(email_domain)
|
||||||
WHERE enabled = true AND domain_verified = true;
|
WHERE enabled = true AND domain_verified = true;
|
||||||
|
|
||||||
-- Add SAML subject to users table for tracking SAML NameID
|
|
||||||
ALTER TABLE users ADD COLUMN saml_subject TEXT;
|
ALTER TABLE users ADD COLUMN saml_subject TEXT;
|
||||||
|
|
||||||
-- Unique constraint: one SAML subject globally
|
|
||||||
CREATE UNIQUE INDEX idx_users_saml_subject
|
CREATE UNIQUE INDEX idx_users_saml_subject
|
||||||
ON users(saml_subject)
|
ON users(saml_subject)
|
||||||
WHERE saml_subject IS NOT NULL;
|
WHERE saml_subject IS NOT NULL;
|
||||||
|
|
||||||
-- Make hashed_password nullable for SAML users
|
|
||||||
-- SAML users authenticate via IdP and don't have passwords
|
|
||||||
ALTER TABLE users ALTER COLUMN hashed_password DROP NOT NULL;
|
ALTER TABLE users ALTER COLUMN hashed_password DROP NOT NULL;
|
||||||
|
|
||||||
-- Create auth_saml_assertions table for replay attack prevention
|
|
||||||
CREATE TABLE auth_saml_assertions (
|
CREATE TABLE auth_saml_assertions (
|
||||||
id TEXT PRIMARY KEY, -- SAML Assertion ID
|
id TEXT PRIMARY KEY,
|
||||||
tenant_id TEXT NOT NULL,
|
tenant_id TEXT NOT NULL,
|
||||||
organization_id TEXT NOT NULL,
|
organization_id TEXT NOT NULL,
|
||||||
used_at TIMESTAMP NOT NULL,
|
used_at TIMESTAMP NOT NULL,
|
||||||
expires_at TIMESTAMP NOT NULL
|
expires_at TIMESTAMP NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Index for cleanup of expired assertions
|
|
||||||
CREATE INDEX idx_auth_saml_assertions_expires_at
|
CREATE INDEX idx_auth_saml_assertions_expires_at
|
||||||
ON auth_saml_assertions(expires_at);
|
ON auth_saml_assertions(expires_at);
|
||||||
|
|
||||||
-- Index for organization lookup
|
|
||||||
CREATE INDEX idx_auth_saml_assertions_organization_id
|
CREATE INDEX idx_auth_saml_assertions_organization_id
|
||||||
ON auth_saml_assertions(organization_id);
|
ON auth_saml_assertions(organization_id);
|
||||||
|
|
||||||
-- Index for tenant scoping
|
|
||||||
CREATE INDEX idx_auth_saml_assertions_tenant_id
|
CREATE INDEX idx_auth_saml_assertions_tenant_id
|
||||||
ON auth_saml_assertions(tenant_id);
|
ON auth_saml_assertions(tenant_id);
|
||||||
|
|
||||||
-- Create auth_saml_requests table for proper InResponseTo validation
|
|
||||||
-- This prevents replay attacks and validates the SAML authentication flow
|
|
||||||
CREATE TABLE auth_saml_requests (
|
CREATE TABLE auth_saml_requests (
|
||||||
id TEXT PRIMARY KEY, -- SAML Request ID generated by SP
|
id TEXT PRIMARY KEY,
|
||||||
organization_id TEXT NOT NULL,
|
organization_id TEXT NOT NULL,
|
||||||
tenant_id TEXT NOT NULL,
|
tenant_id TEXT NOT NULL,
|
||||||
created_at TIMESTAMP NOT NULL,
|
created_at TIMESTAMP NOT NULL,
|
||||||
@@ -122,20 +81,16 @@ CREATE TABLE auth_saml_requests (
|
|||||||
REFERENCES organizations(id) ON DELETE CASCADE
|
REFERENCES organizations(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Index for fast request ID lookup during SAML callback
|
|
||||||
CREATE INDEX idx_auth_saml_requests_id_org ON auth_saml_requests(id, organization_id);
|
CREATE INDEX idx_auth_saml_requests_id_org ON auth_saml_requests(id, organization_id);
|
||||||
|
|
||||||
-- Index for cleanup of expired requests
|
|
||||||
CREATE INDEX idx_auth_saml_requests_expires_at ON auth_saml_requests(expires_at);
|
CREATE INDEX idx_auth_saml_requests_expires_at ON auth_saml_requests(expires_at);
|
||||||
|
|
||||||
-- Create auth_saml_relay_states table for secure RelayState management
|
|
||||||
-- This prevents organization hijacking attacks
|
|
||||||
CREATE TABLE auth_saml_relay_states (
|
CREATE TABLE auth_saml_relay_states (
|
||||||
token TEXT PRIMARY KEY, -- Cryptographically secure random token
|
token TEXT PRIMARY KEY,
|
||||||
tenant_id TEXT NOT NULL,
|
tenant_id TEXT NOT NULL,
|
||||||
organization_id TEXT NOT NULL,
|
organization_id TEXT NOT NULL,
|
||||||
request_id TEXT NOT NULL, -- Links to auth_saml_requests.id
|
request_id TEXT NOT NULL,
|
||||||
saml_config_id TEXT NOT NULL, -- Links to auth_saml_configurations.id
|
saml_config_id TEXT NOT NULL,
|
||||||
created_at TIMESTAMP NOT NULL,
|
created_at TIMESTAMP NOT NULL,
|
||||||
expires_at TIMESTAMP NOT NULL,
|
expires_at TIMESTAMP NOT NULL,
|
||||||
|
|
||||||
@@ -147,11 +102,8 @@ CREATE TABLE auth_saml_relay_states (
|
|||||||
REFERENCES auth_saml_requests(id) ON DELETE CASCADE
|
REFERENCES auth_saml_requests(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
|
|
||||||
-- Index for fast token lookup during callback
|
|
||||||
CREATE INDEX idx_auth_saml_relay_states_token ON auth_saml_relay_states(token);
|
CREATE INDEX idx_auth_saml_relay_states_token ON auth_saml_relay_states(token);
|
||||||
|
|
||||||
-- Index for cleanup of expired relay states
|
|
||||||
CREATE INDEX idx_auth_saml_relay_states_expires_at ON auth_saml_relay_states(expires_at);
|
CREATE INDEX idx_auth_saml_relay_states_expires_at ON auth_saml_relay_states(expires_at);
|
||||||
|
|
||||||
-- Index for tenant scoping
|
|
||||||
CREATE INDEX idx_auth_saml_relay_states_tenant_id ON auth_saml_relay_states(tenant_id);
|
CREATE INDEX idx_auth_saml_relay_states_tenant_id ON auth_saml_relay_states(tenant_id);
|
||||||
|
|||||||
Reference in New Issue
Block a user