# ADR-0014 — Identity Provider: Keycloak (SSO via OIDC + forward-auth) - Status: Accepted — Keycloak, native OIDC, groups and MFA stand (forward-auth enforcement point and app list superseded by [ADR-0022](adr-0022-forward-auth-per-app-oauth2-proxy-sidecars.md)) - Date: 2026-08-15 - Serves: R-IDP-01, R-IDP-02, R-IDP-03, R-IDP-04, R-IDP-05, R-IDP-06 - Supersedes: — ## Context The platform needs one identity and single sign-on: a central user directory, group-based authorization, MFA, and a way to protect both OIDC-capable apps and bare management UIs — without per-app passwords scattered across services. This also resolves the previously-open C2 auth question ([12](../docs/12-command-center-ui.md)). ## Decision Use **Keycloak** as the platform IdP, integrated two ways behind one identity: - **Native OIDC** for apps that support it (Gitea, Nextcloud, Grafana, Kasm, Semaphore, Headlamp). - **Forward-auth proxy (oauth2-proxy → Keycloak)** at the Gateway for apps without usable OIDC (PiHole, *arr UIs, qBittorrent, Homepage, Prometheus, Alertmanager, Hubble UI). Supporting decisions: - **Config as code**: realms/clients/groups are declarative and GitOps-reconciled (Keycloak Operator/ Helm for the instance; `keycloak-config-cli` or realm-import for config). Secrets SOPS-encrypted. - **Authorization by groups**; **MFA** required for privileged groups. - **PostgreSQL on Longhorn**, DR-critical, backed up off-cluster; Keycloak rebuilds from DB + git. - **`public` exposure** (hardened) so login flows work for internet-exposed services. - **Exceptions**: Plex/Seerr use Plex auth; Minecraft uses Mojang/Microsoft; UniFi keeps native auth. ## Alternatives considered - **Authelia / Authentik** — lighter, and Authelia pairs cleanly with forward-auth; but Keycloak is the operator's explicit choice and offers the broadest OIDC/SAML, realm, and federation feature set for heterogeneous apps. (Chosen.) - **Per-app local auth only** — rejected: no SSO, password sprawl, weak central policy/MFA (fails R-IDP-01/06). - **Cloud IdP (e.g. hosted OIDC)** — rejected: this is a self-hosted, offline-capable home platform; identity should not depend on an external SaaS. ## Consequences - One login, one directory, central MFA and group policy; management UIs are never unauthenticated on the network (R-IDP-02). - **Keycloak becomes a critical dependency**: an IdP outage impairs SSO logins → DR-critical storage + a documented **break-glass** local-admin path (e.g. on Gitea) so recovery tools stay reachable. - New platform components to run and secure: Keycloak, PostgreSQL, oauth2-proxy — all under default-deny with explicit allows and trusted TLS. - Onboarded **early** (with PiHole/Gitea) so later services integrate SSO as they come up. - Adds an `auth` block to the ServiceDefinition ([13](../docs/13-service-catalog.md)) so SSO mode + allowed groups are declarative per service.