| Local password auth (PBKDF2-SHA256) | Shipped (v0.10) | auth.cpp:69 pbkdf2_sha256() (OpenSSL PKCS5_PBKDF2_HMAC + BCrypt path) |
Persistent auth store (auth.db, SQLite) | Shipped (v0.12) | auth_db.cpp:222-236 chmod 0600 + L402 MigrationRunner::run; agent-doc .claude/agents/authdb.md |
| Session-cookie auth (HTMX dashboard) | Shipped | auth_routes.cpp:43,386 (extract_session_cookie, Set-Cookie: yuzu_session=…) |
API tokens — Bearer + X-Yuzu-Token | Shipped | api_token_store.cpp (store); both header forms parsed at auth_routes.cpp:108-119 |
| Owner-scoped token revocation (#222) | Shipped | rest_api_v1.cpp:1058-1082 (owner-vs-admin check at L1060) |
| Granular RBAC — 6 roles × 19 securable types × 6 ops | Shipped (Phase 3) | rbac_store.cpp:129-193 — types: Infrastructure, UserManagement, InstructionDefinition, InstructionSet, Execution, Schedule, Approval, Tag, AuditLog, Response, ManagementGroup, ApiToken, Security, Policy, DeviceToken, SoftwareDeployment, License, FileRetrieval, GuaranteedState; ops: Read/Write/Execute/Delete/Approve/Push |
| Self-target principal-destruction guard (#397/#403) | Shipped | settings_routes.cpp:434,1830,2488-2504 (3 call sites); design in docs/auth-architecture.md §self-target |
| OIDC SSO — full PKCE flow, Entra discovery, JWT validation | Shipped | oidc_provider.cpp:189 generate_code_verifier(), L194 compute_code_challenge(), L385 code_verifier post, L766 /.well-known/openid-configuration discovery, L542/L623 JWKS fetch + JWT signature verify |
| Directory Sync — AD/Entra users + groups + role mapping via Microsoft Graph v1.0 | Shipped | directory_sync.cpp:336,509,556,608 calls https://graph.microsoft.com/v1.0/users, /groups, /groups/{id}/members; persisted directory_group_role_mappings + directory_sync_status tables (directory_sync.cpp:147). NOTE: oidc_provider.cpp:248 only parses the JWT groups claim — Graph integration is the separate Directory Sync subsystem. |
| mTLS for agent ↔ server | Shipped | main.cpp:111 --ca-cert flag; peer-cert identity match in agent_service_impl.cpp:47,354 |
| Windows certificate-store mTLS (CryptoAPI/CNG) — agent-side only | Shipped | agents/core/src/cert_store.cpp:78,84,199-201 (CertOpenStore, NCrypt CNG export) |
| HTTPS-by-default, secure bind default (127.0.0.1) | Shipped (hard invariant) | main.cpp:100 127.0.0.1 default, L216 --no-https opt-out; design in docs/auth-architecture.md |
| HTTP security headers — six (CSP, HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy) | Shipped (SOC2-C1) | security_headers.cpp:187-195 (HSTS conditional on HTTPS responses) |
| Cert hot-reload (HTTPS) with audit + metrics | Shipped | cert_reloader.cpp:31 audit cert.reload, L80 watcher loop, L114-191 atomic SSL_CTX swap |
| Agent enrollment — pre-shared / platform-trust (auto-approve via attestation_provider) / admin-approval queue (3 tiers) | Shipped | auth.cpp:717-948 + agent_service_impl.cpp:67-189 (pre-shared L70, attestation auto-approve L101-136, pending-admin queue L138-189) |
| MCP token issuance + tier-before-RBAC ordering | Shipped | mcp_server.cpp:556-557,591,599 (tier check at L591 precedes RBAC at L599); design in docs/mcp-server.md |
auth.admin_required denied audit on every 403 | Shipped (gate) | auth_routes.cpp:150 inside require_admin |
| Private-key permission validation | Shipped | cert_reloader.cpp:120 validate_key_file_permissions() (helper in file_utils.hpp); called at startup from server.cpp and on hot-reload |
| Metrics endpoint localhost-only-no-auth | Shipped | server.cpp:1621 (loopback always unauthenticated; remote behavior toggled by cfg.metrics_require_auth) |
| Account lockout after N failed local-password logins | Shipped (SOC 2 CC6.3) | auth.db v3 columns + AuthDB::lockout_status/record_failed_login/clear_failed_logins (auth_db.cpp); POST /login pre-check + record/clear (auth_routes.cpp); admin unlock POST /api/v1/users/<name>/unlock (rest_api_v1.cpp); --auth-lockout-threshold/--auth-lockout-window-secs (main.cpp). Generic-401 (no enum/oracle), auto-expiring window, audit auth.lockout.applied/.cleared. Ref: docs/auth-architecture.md "Account lockout". |
MFA / TOTP — full ladder (enrollment + login challenge + recovery codes; step-up on 11 high-risk surfaces; enforcement modes + OIDC amr short-circuit + login-time enrollment bootstrap) | Shipped (v0.12–v0.13, SOC 2 CC6.6) | server/core/src/totp.{hpp,cpp} (RFC 6238 + base32); AuthDB::mfa_* accessors; POST /login 202-branches + POST /login/mfa, /login/mfa/stepup, /login/mfa/enroll at auth_routes.cpp; require_mfa_step_up + amr_asserts_mfa at mfa_step_up.{hpp,cpp}; --mfa-enforcement at main.cpp; Settings panel + self-target disable guard at settings_routes.cpp. Remaining: at-rest TOTP-secret encryption — mechanism decided by ADR-0010 (SecretCodec envelope encryption at the auth store's Postgres migration; the auth_kv scaffolding will NOT be used). Full reference: docs/auth-mfa-design.md. |
SCIM v2 provisioning — auto-create/deactivate/reactivate operators from an IdP (/scim/v2/*, Users-only) | Shipped (SOC 2 CC6.2/CC6.8) | server/core/include/yuzu/server/scim_store.hpp (storage: scim_resources/scim_tokens inside auth.db, own "scim" migration component) + scim_json.hpp (JSON codec/discovery) + scim_routes.{hpp,cpp} (routes); provenance guard via AuthDB::set_provisioning_source/get_provisioning_source (users.provisioning_source, auth.db migration v7), now also re-checking role == "user". See docs/auth-architecture.md "SCIM v2 provisioning". |