# VS-01: Identity Access Control (IAC) & Role-Based Access

**Vertical Slice:** VS-01 (Identity & Authorization)  
**Version:** 1.0 DRAFT  
**Date:** 2026-08-07  
**Owner:** Security & Identity Architecture  
**Status:** 📋 DRAFT (Specification Ready for Contract Review)

---

## 📋 User Story

**As a** platform security architect  
**I want to** establish identity, MFA, RBAC role hierarchy, and maker-checker approval boundaries  
**So that** all downstream slices (VS-02 through VS-08) can enforce consistent access control and segregation of duties

**Acceptance Criteria:**
- 📋 Identity contract defined (user/role/permission schema)
- 📋 MFA policy specified (2FA/TOTP/WebAuthn tiers)
- 📋 RBAC role hierarchy formalized (Guest/User/Operator/Admin/SuperAdmin + domain-specific roles)
- 📋 Maker-checker approval boundaries documented (for critical operations like model promotion, dataset freeze)
- 📋 Permission matrix mapped (read/write/delete/audit per role)

---

## 🎯 Non-Goals

- ❌ Implement UI/API endpoints (belongs to BE/FE slices)
- ❌ Integrate with external identity provider (OIDC/Kerberos setup deferred)
- ❌ Build MFA enforcement engine (belongs to separate AUTH_ENFORCEMENT slice)
- ❌ Execute permission checks (belongs to handler/middleware slices)
- ❌ Seed production user data (deferred to operations)

---

## 🔄 State Transitions

### Identity Lifecycle

```
[UNDEFINED]
    ↓ (user registered)
[ACTIVE]
    ↓ (MFA required but not set)
[REQUIRES_MFA_SETUP]
    ↓ (MFA device registered)
[MFA_CONFIGURED]
    ↓ (temporary disable during password reset)
[MFA_SUSPENDED]
    ↓ (re-enable)
[MFA_CONFIGURED]
    ↓ (admin deactivation)
[INACTIVE]
    ↓ (security breach)
[REVOKED]
```

### Role Assignment Workflow (Maker-Checker)

```
User requests elevated role (e.g., OPERATOR → ADMIN)
    ↓
[PENDING_APPROVAL] ← Role request created (requester_id, requested_role, reason)
    ↓
Admin receives notification (role.required_approver_count = 2)
    ↓
Approver-1 reviews & approves/rejects
    ↓
[APPROVED_BY_1] or [REJECTED]
    ↓ (if approved by 1, awaits Approver-2)
[APPROVED_BY_2]
    ↓
[ACTIVE] (role_assignment.effective_at set, correlation_id = approval_request.id)
    ↓
[EXPIRED] (optional: time-bound roles like "Quarterly Reviewer")
```

---

## 🔐 RBAC Constraints

### Core Role Hierarchy

| Role | Description | Can Access | Can Modify | Can Approve | Maker-Checker Approval Required |
|------|-------------|-----------|-----------|-------------|--------|
| **GUEST** | Anonymous/public | Public resources (GDP compliant) | ❌ | ❌ | N/A |
| **USER** | Authenticated individual | Own data + shared workspace | Own data | ❌ | N/A |
| **OPERATOR** | Operations team (data ops, risk team) | All non-sensitive data | Configurations | MODEL_ACTIVATION (1 more) | MODEL_ACTIVATION, DATASET_FREEZE |
| **ADMIN** | Platform administrator | All data (except audit logs) | All (soft delete) | All (except critical) | CRITICAL_CONFIG, USER_REVOCATION |
| **SUPER_ADMIN** | Super administrator | All (including audit logs) | All (hard delete) | All | N/A (can self-approve in emergency) |

### Domain-Specific Roles (Optional, for Future Slices)

- **QUANT_ENGINEER** — Can read market data, backtest code; cannot modify live models
- **RISK_MANAGER** — Can read risk dashboards, flag models; cannot freeze or promote
- **COMPLIANCE_OFFICER** — Can audit all; cannot modify data
- **MODEL_REVIEWER** — Can read model cards, evidence; approves promotion via maker-checker

### MFA Tiers

| Tier | Requirement | Impact | Users |
|------|-------------|--------|-------|
| **NO_MFA** | None (legacy) | Guest/public read | Public API consumers |
| **TOTP_OPTIONAL** | Google Authenticator / Authy (optional) | USER tier | General staff |
| **TOTP_REQUIRED** | TOTP mandatory | OPERATOR+ tier | Operations, Risk, Compliance |
| **HARDWARE_KEY** | YubiKey / FIDO2 (required) | SUPER_ADMIN tier | Executives, DBAs |

---

## 📊 Data Contract (v1.0)

### Point-in-Time (PIT) Envelope (Inherited from VS-00)

All identity tables MUST include:

```sql
-- Core identity tables
CREATE TABLE identity.users (
    id UUID PRIMARY KEY,
    email VARCHAR(255) NOT NULL UNIQUE,
    display_name VARCHAR(255),
    mfa_status VARCHAR(50) NOT NULL DEFAULT 'REQUIRES_MFA_SETUP',  -- ACTIVE, REQUIRES_MFA_SETUP, MFA_CONFIGURED, INACTIVE, REVOKED
    mfa_method VARCHAR(50),  -- TOTP, HARDWARE_KEY, none
    created_at TIMESTAMPTZ NOT NULL,
    updated_at TIMESTAMPTZ NOT NULL,
    published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    revision INT NOT NULL DEFAULT 1,
    correlation_id UUID NOT NULL
);

CREATE TABLE identity.roles (
    id UUID PRIMARY KEY,
    name VARCHAR(100) NOT NULL UNIQUE,  -- GUEST, USER, OPERATOR, ADMIN, SUPER_ADMIN
    description TEXT,
    required_approver_count INT DEFAULT 1,  -- How many approvers needed for elevation to this role
    published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    revision INT NOT NULL DEFAULT 1,
    correlation_id UUID NOT NULL
);

CREATE TABLE identity.user_roles (
    id UUID PRIMARY KEY,
    user_id UUID NOT NULL REFERENCES identity.users(id),
    role_id UUID NOT NULL REFERENCES identity.roles(id),
    assigned_by_user_id UUID,  -- Who assigned this role
    effective_at TIMESTAMPTZ NOT NULL,
    expires_at TIMESTAMPTZ,  -- Optional: time-bound roles
    is_active BOOLEAN DEFAULT TRUE,
    published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    revision INT NOT NULL DEFAULT 1,
    correlation_id UUID NOT NULL
);

CREATE TABLE identity.role_approval_requests (
    id UUID PRIMARY KEY,
    user_id UUID NOT NULL REFERENCES identity.users(id),
    requested_role_id UUID NOT NULL REFERENCES identity.roles(id),
    reason TEXT,
    status VARCHAR(50) NOT NULL DEFAULT 'PENDING_APPROVAL',  -- PENDING_APPROVAL, APPROVED_BY_1, APPROVED_BY_2, REJECTED, WITHDRAWN
    approver_count_required INT NOT NULL,
    approvers JSONB NOT NULL DEFAULT '[]'::JSONB,  -- [{ "approver_id": UUID, "approved_at": TIMESTAMPTZ, "reason": "" }]
    created_at TIMESTAMPTZ NOT NULL,
    published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    revision INT NOT NULL DEFAULT 1,
    correlation_id UUID NOT NULL
);

CREATE TABLE identity.mfa_devices (
    id UUID PRIMARY KEY,
    user_id UUID NOT NULL REFERENCES identity.users(id),
    device_type VARCHAR(50) NOT NULL,  -- TOTP, HARDWARE_KEY
    secret_hash VARCHAR(255),  -- Hashed TOTP secret (never store plaintext)
    device_name VARCHAR(255),  -- User-friendly name ("My YubiKey", "Work Phone")
    registered_at TIMESTAMPTZ NOT NULL,
    last_used_at TIMESTAMPTZ,
    is_backup_device BOOLEAN DEFAULT FALSE,
    published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    revision INT NOT NULL DEFAULT 1,
    correlation_id UUID NOT NULL
);

CREATE TABLE identity.permissions (
    id UUID PRIMARY KEY,
    role_id UUID NOT NULL REFERENCES identity.roles(id),
    resource VARCHAR(255) NOT NULL,  -- "model_activation", "dataset_freeze", "user_management"
    action VARCHAR(50) NOT NULL,  -- READ, WRITE, DELETE, AUDIT
    constraints JSONB,  -- Optional: { "requires_approval_count": 2, "requires_evidence": ["model_card"] }
    published_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    revision INT NOT NULL DEFAULT 1,
    correlation_id UUID NOT NULL,
    UNIQUE(role_id, resource, action)
);
```

### Data Quality Rules

- ✅ No direct password storage (use bcrypt + salt)
- ✅ MFA secrets never logged or exposed in HTTP responses
- ✅ All role changes tracked in `user_roles` append-only (no soft deletes)
- ✅ Approval requests immutable once APPROVED_BY_1 or REJECTED
- ✅ PIT envelope strictly enforced: `published_at <= cutoff` for all reads
- ✅ `correlation_id` links all related tables for audit trail

---

## 🛡️ Governance Gates

### Pre-Merge Gates

- [ ] **RBAC Matrix Approved:** Security team signs off on role hierarchy and permission matrix
- [ ] **MFA Tier Mapping:** Confirm mapping between role tiers and MFA requirements
- [ ] **Maker-Checker Thresholds:** Define approval_count per critical operation (e.g., model promotion = 2 approvers)
- [ ] **Audit Log Design:** Confirm all authorization decisions (grant/deny/revoke) are logged with `correlation_id`
- [ ] **Identity Provider Integration Plan:** Document OIDC/Kerberos provider (if applicable)

### Post-Merge Validation

- [ ] **Schema Tests:** User/role/MFA creation tests pass (40+ scenarios)
- [ ] **RBAC Policy Tests:** Permission matrix matches code (cross-checked vs ADR-SEC-001)
- [ ] **PIT Query Tests:** All reads include `WHERE published_at <= @cutoff`

---

## 📋 Source / Assumptions / Unknown

### Source

- **ADR-SEC-001:** OIDC/JWT/DevelopmentHeader authentication tiers (approved 2026-08-04)
- **Existing RBAC:** VS-00-SLICE_SPEC (base governance, roles table exists)
- **Maker-Checker Pattern:** Standard 2-approver workflow from compliance requirements

### Assumptions

- ✅ OIDC identity provider will be integrated later (separate slice); VS-01 is schema + policy only
- ✅ MFA enforcement (checking device before operation) happens in middleware/handler layer (not here)
- ✅ Audit logging of permission checks is already handled by OutboxPollerJob + SerilogCorrelation
- ✅ All users are human; no service-account roles yet (may expand in future)

### Unknown

- ❓ **OIDC Provider Identity:** Which OIDC provider (Keycloak, Auth0, Azure AD)? Deferred to separate architecture decision.
- ❓ **Hardware Key Vendor:** YubiKey vs other FIDO2 vendors? Deferred to procurement.
- ❓ **Approval SLA:** How long can role requests stay in PENDING_APPROVAL before escalation alert? (Assumed 5 business days; confirm with ops)
- ❓ **Audit Retention:** How long to retain `role_approval_requests` history? (Assumed 7 years for compliance; confirm with legal)
- ❓ **Domain-Specific Roles:** Should QUANT_ENGINEER/RISK_MANAGER/COMPLIANCE roles be predefined, or dynamically created per organization? (Deferred to VS-03+)

---

## ✅ Compliance & Traceability

**Governance:** AGENTS.md v16.0 Maturity gate (contract-first, no placeholder code)  
**Related ADRs:**
- ADR-SEC-001: Authentication strategy (OIDC tiers)
- ADR-GOV-001: Role-based access control (assumed; link when available)

**WBS Dependencies:**
- ✅ AEG-X-001 (Version Coverage Matrix): Prerequisite for schema versioning
- ✅ AEG-VS-00-02 (Data Contract): PIT envelope inherited

**Next Slices (Depend on VS-01):**
- VS-02: Financial Security Master (source approval RBAC)
- VS-03: Model Operations (model promotion maker-checker)
- VS-04+: All domain slices (inherit identity & approval boundaries)

---

## Status

**📋 DRAFT:** Specification complete, ready for:
1. Security team approval (RBAC matrix + MFA tiers)
2. Compliance team approval (maker-checker SLA + audit retention)
3. Architecture review (schema + PIT readiness)
4. Next: Implementation (separate PR for schema migration + tests)

