- 0025: Observability stack (Prometheus + ClickStack + OTEL) - 0026: Tiered storage strategy (Longhorn + NFS) - 0027: Database strategy (CloudNativePG for PostgreSQL) - 0028: Authentik SSO strategy (OIDC/SAML identity provider)
14 KiB
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
- Authentik as OIDC/SAML provider
- Keycloak
- Authelia + LDAP
- 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
# 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
# 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)
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:
# 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
# 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_secondsauthentik_login_attempt_totalauthentik_outpost_connectedauthentik_provider_authorization_total
Grafana Dashboard
- Login success/failure rates
- Active sessions
- Provider usage
- MFA adoption rates
Alerts
- 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
- PostgreSQL database (via CNPG)
- Media files (if custom branding)
- Blueprint exports (configuration as code)
Blueprints
Export configuration as YAML for GitOps:
# 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
- Passkey/FIDO2 - Passwordless authentication
- External IdP federation - Google/GitHub as upstream IdP
- Conditional access - Device trust, network location policies
- Session revocation - Force logout from all apps