# Authentik Single Sign-On Strategy * Status: accepted * Date: 2026-02-04 * Deciders: Billy * Technical Story: Centralize authentication across all homelab applications ## Context and Problem Statement A growing homelab with many self-hosted applications creates authentication sprawl - each app has its own user database, passwords, and session management. This creates poor user experience and security risks. How do we centralize authentication while maintaining flexibility for different application requirements? ## Decision Drivers * Single sign-on (SSO) for all applications * Centralized user management and lifecycle * MFA enforcement across all applications * Open-source and self-hosted * Low resource requirements for homelab scale ## Considered Options 1. **Authentik as OIDC/SAML provider** 2. **Keycloak** 3. **Authelia + LDAP** 4. **Per-application local auth** ## Decision Outcome Chosen option: **Option 1 - Authentik as OIDC/SAML provider** Authentik provides modern identity management with OIDC, SAML 2.0, LDAP, and SCIM support. Its flow-based authentication engine allows customizable login experiences. ### Positive Consequences * Clean, modern UI for users and admins * Flexible flow-based authentication * Built-in MFA (TOTP, WebAuthn, SMS, Duo) * Proxy provider for legacy apps * SCIM for user provisioning * Active development and community ### Negative Consequences * Python-based (higher memory than Go alternatives) * PostgreSQL dependency * Some enterprise features require outpost pods ## Architecture ``` ┌─────────────────────┐ │ User │ └──────────┬──────────┘ │ ▼ ┌─────────────────────┐ │ Ingress/Traefik │ └──────────┬──────────┘ │ ┌──────────────────────────┼──────────────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │ auth.lab.io │ │ app.lab.io │ │ app2.lab.io │ │ (Authentik) │ │ (OIDC-enabled) │ │ (Proxy-auth) │ └─────────────────┘ └────────┬────────┘ └────────┬────────┘ │ │ │ │ ┌──────────────────┘ │ │ │ OIDC/OAuth2 │ │ │ │ ▼ ▼ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ Authentik │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ Server │ │ Worker │ │ Outpost │◄───────────┤ │ │ (API) │ │ (Tasks) │ │ (Proxy) │ Forward │ │ └──────┬──────┘ └──────┬──────┘ └─────────────┘ Auth │ │ │ │ │ │ └────────┬───────┘ │ │ │ │ │ ┌──────▼──────┐ │ │ │ Redis │ │ │ │ (Cache) │ │ │ └─────────────┘ │ │ │ └─────────────────────────────┬──────────────────────────────────┘ │ ┌──────▼──────┐ │ PostgreSQL │ │ (CNPG) │ └─────────────┘ ``` ## Provider Configuration ### OIDC Applications | Application | Provider Type | Claims Override | Notes | |-------------|---------------|-----------------|-------| | Gitea | OIDC | None | Admin mapping via group | | Affine | OIDC | `email_verified: true` | See ADR-0016 | | Companions | OIDC | None | Custom provider | | Grafana | OIDC | `role` claim | Admin role mapping | | ArgoCD | OIDC | `groups` claim | RBAC integration | | MLflow | Proxy | N/A | Forward auth | | Open WebUI | OIDC | None | LLM interface | ### Provider Template ```yaml # Example OAuth2/OIDC Provider apiVersion: authentik.io/v1 kind: OAuth2Provider metadata: name: gitea spec: name: Gitea authorizationFlow: default-authorization-flow clientId: ${GITEA_CLIENT_ID} clientSecret: ${GITEA_CLIENT_SECRET} redirectUris: - https://git.lab.daviestechlabs.io/user/oauth2/authentik/callback signingKey: authentik-self-signed propertyMappings: - authentik-default-openid - authentik-default-email - authentik-default-profile ``` ## Authentication Flows ### Default Login Flow ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Login │────▶│ Username │────▶│ Password │────▶│ MFA │ │ Stage │ │ Stage │ │ Stage │ │ Stage │ └─────────────┘ └─────────────┘ └─────────────┘ └──────┬──────┘ │ ▼ ┌─────────────┐ │ Session │ │ Created │ └─────────────┘ ``` ### Flow Customization - **Admin users:** Require hardware key (WebAuthn) - **API access:** Service account tokens - **External users:** Email verification + MFA enrollment ## Group-Based Authorization ### Group Structure ``` authentik-admins → Authentik admin access ├── cluster-admins → Full cluster access ├── gitea-admins → Git admin ├── monitoring-admins → Grafana admin └── ai-platform-admins → AI/ML admin authentik-users → Standard user access ├── developers → Git write, monitoring read ├── ml-engineers → AI/ML services access └── guests → Read-only access ``` ### Application Group Mapping ```yaml # Grafana OIDC config grafana: auth.generic_oauth: role_attribute_path: | contains(groups[*], 'monitoring-admins') && 'Admin' || contains(groups[*], 'developers') && 'Editor' || 'Viewer' ``` ## Outpost Deployment ### Embedded Outpost (Default) - Runs within Authentik server - Handles LDAP and Radius - Suitable for small deployments ### Standalone Outpost (Proxy) ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: authentik-outpost-proxy spec: replicas: 2 template: spec: containers: - name: outpost image: ghcr.io/goauthentik/proxy ports: - containerPort: 9000 name: http - containerPort: 9443 name: https env: - name: AUTHENTIK_HOST value: "https://auth.lab.daviestechlabs.io/" - name: AUTHENTIK_TOKEN valueFrom: secretKeyRef: name: authentik-outpost-token key: token ``` ### Forward Auth Integration For applications without OIDC support: ```yaml # Traefik ForwardAuth middleware apiVersion: traefik.io/v1alpha1 kind: Middleware metadata: name: authentik-forward-auth spec: forwardAuth: address: http://authentik-outpost-proxy.authentik.svc:9000/outpost.goauthentik.io/auth/traefik trustForwardHeader: true authResponseHeaders: - X-authentik-username - X-authentik-groups - X-authentik-email ``` ## MFA Enforcement ### Policies | User Group | MFA Requirement | |------------|-----------------| | Admins | WebAuthn (hardware key) required | | Developers | TOTP or WebAuthn required | | Guests | MFA optional | ### Device Registration - Self-service MFA enrollment - Recovery codes generated at setup - Admin can reset user MFA ## SCIM User Provisioning ### When to Use - Automatic user creation in downstream apps - Group membership sync - User deprovisioning on termination ### Supported Apps Currently using SCIM provisioning for: - None (manual user creation in apps) Future consideration for: - Gitea organization sync - Grafana team sync ## Secrets Management Integration ### Vault Integration ```yaml # External Secret for Authentik DB credentials apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: authentik-db-credentials namespace: authentik spec: secretStoreRef: kind: ClusterSecretStore name: vault target: name: authentik-db-credentials data: - secretKey: password remoteRef: key: kv/data/authentik property: db_password - secretKey: secret_key remoteRef: key: kv/data/authentik property: secret_key ``` ## Monitoring ### Prometheus Metrics Authentik exposes metrics at `/metrics`: - `authentik_login_duration_seconds` - `authentik_login_attempt_total` - `authentik_outpost_connected` - `authentik_provider_authorization_total` ### Grafana Dashboard - Login success/failure rates - Active sessions - Provider usage - MFA adoption rates ### Alerts ```yaml - alert: AuthentikHighLoginFailures expr: rate(authentik_login_attempt_total{result="failure"}[5m]) > 10 for: 5m labels: severity: warning annotations: summary: High login failure rate detected - alert: AuthentikOutpostDisconnected expr: authentik_outpost_connected == 0 for: 5m labels: severity: critical ``` ## Backup and Recovery ### What to Backup 1. PostgreSQL database (via CNPG) 2. Media files (if custom branding) 3. Blueprint exports (configuration as code) ### Blueprints Export configuration as YAML for GitOps: ```yaml # authentik-blueprints/providers/gitea.yaml version: 1 metadata: name: Gitea OIDC Provider entries: - model: authentik_providers_oauth2.oauth2provider identifiers: name: gitea attrs: authorization_flow: !Find [authentik_flows.flow, [slug, default-authorization-flow]] # ... provider config ``` ## Integration Patterns ### Pattern 1: Native OIDC Best for: Modern applications with OIDC support ``` App ──OIDC──▶ Authentik ──▶ App (with user info) ``` ### Pattern 2: Proxy Forward Auth Best for: Legacy apps without SSO support ``` Request ──▶ Traefik ──ForwardAuth──▶ Authentik Outpost │ │ │◀──────Header injection─────┘ │ ▼ App (reads X-authentik-* headers) ``` ### Pattern 3: LDAP Compatibility Best for: Apps requiring LDAP ``` App ──LDAP──▶ Authentik Outpost (LDAP) ──▶ Authentik ``` ## Resource Requirements | Component | CPU Request | Memory Request | |-----------|-------------|----------------| | Server | 100m | 500Mi | | Worker | 100m | 500Mi | | Redis | 50m | 128Mi | | Outpost (each) | 50m | 128Mi | ## Future Enhancements 1. **Passkey/FIDO2** - Passwordless authentication 2. **External IdP federation** - Google/GitHub as upstream IdP 3. **Conditional access** - Device trust, network location policies 4. **Session revocation** - Force logout from all apps ## References * [Authentik Documentation](https://goauthentik.io/docs/) * [Authentik GitHub](https://github.com/goauthentik/authentik) * [OIDC Specification](https://openid.net/specs/openid-connect-core-1_0.html)