# Authentik SSO Disaster Recovery & Rebuild Guide **Last Updated**: 2026-01-31 **Tested On**: Authentik 2024.12.x on Calypso (Synology DS723+) This guide documents the complete process to rebuild Authentik SSO and reconfigure OAuth2 for all homelab services from scratch. --- ## Table of Contents 1. [Prerequisites](#prerequisites) 2. [Deploy Authentik](#deploy-authentik) 3. [Initial Configuration](#initial-configuration) 4. [Configure OAuth2 Providers](#configure-oauth2-providers) 5. [Configure Forward Auth Providers](#configure-forward-auth-providers) 6. [Service-Specific Configuration](#service-specific-configuration) 7. [NPM Integration](#npm-integration) 8. [Troubleshooting](#troubleshooting) 9. [Recovery Procedures](#recovery-procedures) --- ## Prerequisites ### Infrastructure Required - Docker host (Calypso NAS or equivalent) - PostgreSQL database - Redis - Nginx Proxy Manager (NPM) for reverse proxy - Domain with SSL (e.g., sso.vish.gg via Cloudflare) ### Network Configuration | Service | Host | Port | |---------|------|------| | Authentik Server | 192.168.0.250 | 9000 | | Authentik Worker | 192.168.0.250 | (internal) | | PostgreSQL | 192.168.0.250 | 5432 | | Redis | 192.168.0.250 | 6379 | ### Credentials to Have Ready - Admin email (e.g., admin@example.com) - Strong admin password - SMTP settings (optional, for email notifications) --- ## Deploy Authentik ### Docker Compose File Location: `hosts/synology/calypso/authentik/docker-compose.yaml` ```yaml version: '3.9' services: postgresql: image: postgres:16-alpine container_name: Authentik-DB restart: unless-stopped healthcheck: test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"] start_period: 20s interval: 30s retries: 5 timeout: 5s volumes: - database:/var/lib/postgresql/data environment: POSTGRES_PASSWORD: "REDACTED_PASSWORD" password required} POSTGRES_USER: ${PG_USER:-authentik} POSTGRES_DB: ${PG_DB:-authentik} networks: - authentik redis: image: redis:alpine container_name: Authentik-REDIS command: --save 60 1 --loglevel warning restart: unless-stopped healthcheck: test: ["CMD-SHELL", "redis-cli ping | grep PONG"] start_period: 20s interval: 30s retries: 5 timeout: 3s volumes: - redis:/data networks: - authentik server: image: ghcr.io/goauthentik/server:2024.12 container_name: Authentik-SERVER restart: unless-stopped command: server environment: AUTHENTIK_REDIS__HOST: redis AUTHENTIK_POSTGRESQL__HOST: postgresql AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} AUTHENTIK_POSTGRESQL__PASSWORD: "REDACTED_PASSWORD" AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} volumes: - ./media:/media - ./custom-templates:/templates ports: - "9000:9000" - "9443:9443" depends_on: postgresql: condition: service_healthy redis: condition: service_healthy networks: - authentik worker: image: ghcr.io/goauthentik/server:2024.12 container_name: Authentik-WORKER restart: unless-stopped command: worker environment: AUTHENTIK_REDIS__HOST: redis AUTHENTIK_POSTGRESQL__HOST: postgresql AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} AUTHENTIK_POSTGRESQL__PASSWORD: "REDACTED_PASSWORD" AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} volumes: - ./media:/media - ./custom-templates:/templates depends_on: postgresql: condition: service_healthy redis: condition: service_healthy networks: - authentik volumes: database: redis: networks: authentik: driver: bridge ``` ### Environment File (.env) ```bash PG_PASS="REDACTED_PASSWORD" PG_USER=authentik PG_DB=authentik AUTHENTIK_SECRET_KEY= ``` ### Generate Secret Key ```bash openssl rand -base64 60 ``` ### Deploy ```bash cd /volume1/docker/authentik docker-compose up -d ``` ### Verify Deployment ```bash docker ps | grep -i authentik # Should show: Authentik-SERVER, Authentik-WORKER, Authentik-DB, Authentik-REDIS ``` --- ## Initial Configuration ### First-Time Setup 1. Navigate to `https://sso.vish.gg/if/flow/initial-setup/` 2. Create admin account: - **Username**: `akadmin` - **Email**: `admin@example.com` - **Password**: (use password manager) ### Post-Setup Configuration 1. **Admin Interface**: `https://sso.vish.gg/if/admin/` 2. **User Portal**: `https://sso.vish.gg/if/user/` ### Create User Groups (Optional but Recommended) Navigate to: Admin → Directory → Groups | Group Name | Purpose | |------------|---------| | `Grafana Admins` | Admin access to Grafana | | `Grafana Editors` | Editor access to Grafana | | `Homelab Users` | General homelab access | --- ## Configure OAuth2 Providers ### Critical: Scope Mappings **EVERY OAuth2 provider MUST have these scope mappings configured, or logins will fail with "InternalError":** 1. Go to: Admin → Customization → Property Mappings 2. Note these default mappings exist: - `authentik default OAuth Mapping: OpenID 'openid'` - `authentik default OAuth Mapping: OpenID 'email'` - `authentik default OAuth Mapping: OpenID 'profile'` When creating providers, you MUST add these to the "Scopes" field. ### Provider 1: Grafana OAuth2 **Admin → Providers → Create → OAuth2/OpenID Provider** | Setting | Value | |---------|-------| | Name | `Grafana OAuth2` | | Authentication flow | default-authentication-flow | | Authorization flow | default-provider-authorization-implicit-consent | | Client type | Confidential | | Client ID | (auto-generated, save this) | | Client Secret | (auto-generated, save this) | | Redirect URIs | `https://gf.vish.gg/login/generic_oauth` | | Signing Key | authentik Self-signed Certificate | | **Scopes** | Select: `openid`, `email`, `profile` ⚠️ CRITICAL | **Create Application:** - Admin → Applications → Create - Name: `Grafana` - Slug: `grafana` - Provider: `Grafana OAuth2` - Launch URL: `https://gf.vish.gg` ### Provider 2: Gitea OAuth2 **Admin → Providers → Create → OAuth2/OpenID Provider** | Setting | Value | |---------|-------| | Name | `Gitea OAuth2` | | Authorization flow | default-provider-authorization-implicit-consent | | Client type | Confidential | | Redirect URIs | `https://git.vish.gg/user/oauth2/authentik/callback` | | **Scopes** | Select: `openid`, `email`, `profile` ⚠️ CRITICAL | **Create Application:** - Name: `Gitea` - Slug: `gitea` - Provider: `Gitea OAuth2` - Launch URL: `https://git.vish.gg` ### Provider 3: Portainer OAuth2 **Admin → Providers → Create → OAuth2/OpenID Provider** | Setting | Value | |---------|-------| | Name | `Portainer OAuth2` | | Authorization flow | default-provider-authorization-implicit-consent | | Client type | Confidential | | Redirect URIs | `http://vishinator.synology.me:10000` | | **Scopes** | Select: `openid`, `email`, `profile` ⚠️ CRITICAL | **Create Application:** - Name: `Portainer` - Slug: `portainer` - Provider: `Portainer OAuth2` - Launch URL: `http://vishinator.synology.me:10000` ### Provider 4: Seafile OAuth2 **Admin → Providers → Create → OAuth2/OpenID Provider** | Setting | Value | |---------|-------| | Name | `Seafile OAuth2` | | Authorization flow | default-provider-authorization-implicit-consent | | Client type | Confidential | | Redirect URIs | `https://sf.vish.gg/oauth/callback/` | | **Scopes** | Select: `openid`, `email`, `profile` ⚠️ CRITICAL | **Create Application:** - Name: `Seafile` - Slug: `seafile` - Launch URL: `https://sf.vish.gg` --- ## Configure Forward Auth Providers Forward Auth is used for services that don't have native OAuth support. Authentik intercepts all requests and requires login first. ### Provider: vish.gg Domain Forward Auth **Admin → Providers → Create → Proxy Provider** | Setting | Value | |---------|-------| | Name | `vish.gg Domain Forward Auth` | | Authorization flow | default-provider-authorization-implicit-consent | | Mode | Forward auth (single application) | | External host | `https://sso.vish.gg` | **Create Application:** - Name: `vish.gg Domain Auth` - Slug: `vishgg-domain-auth` - Provider: `vish.gg Domain Forward Auth` ### Create/Update Outpost **Admin → Applications → Outposts** 1. Edit the embedded outpost (or create one) 2. Add all Forward Auth applications to it 3. The outpost will listen on port 9000 --- ## Service-Specific Configuration ### Grafana Configuration **Environment variables** (in docker-compose or Portainer): ```yaml environment: # OAuth2 SSO - GF_AUTH_GENERIC_OAUTH_ENABLED=true - GF_AUTH_GENERIC_OAUTH_NAME=Authentik - GF_AUTH_GENERIC_OAUTH_CLIENT_ID= - GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET= - GF_AUTH_GENERIC_OAUTH_SCOPES=openid profile email - GF_AUTH_GENERIC_OAUTH_AUTH_URL=https://sso.vish.gg/application/o/authorize/ - GF_AUTH_GENERIC_OAUTH_TOKEN_URL=https://sso.vish.gg/application/o/token/ - GF_AUTH_GENERIC_OAUTH_API_URL=https://sso.vish.gg/application/o/userinfo/ - GF_AUTH_SIGNOUT_REDIRECT_URL=https://sso.vish.gg/application/o/grafana/end-session/ # CRITICAL: Attribute paths - GF_AUTH_GENERIC_OAUTH_EMAIL_ATTRIBUTE_PATH=email - GF_AUTH_GENERIC_OAUTH_LOGIN_ATTRIBUTE_PATH=preferred_username - GF_AUTH_GENERIC_OAUTH_NAME_ATTRIBUTE_PATH=name # Role mapping - GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH=contains(groups[*], 'Grafana Admins') && 'Admin' || contains(groups[*], 'Grafana Editors') && 'Editor' || 'Viewer' # Additional settings - GF_AUTH_GENERIC_OAUTH_USE_PKCE=true - GF_AUTH_GENERIC_OAUTH_ALLOW_ASSIGN_GRAFANA_ADMIN=true - GF_SERVER_ROOT_URL=https://gf.vish.gg ``` ### Gitea Configuration Configure via **Site Administration → Authentication Sources → Add OAuth2**: | Setting | Value | |---------|-------| | Authentication Name | `authentik` | | OAuth2 Provider | OpenID Connect | | Client ID | (from Authentik) | | Client Secret | (from Authentik) | | OpenID Connect Auto Discovery URL | `https://sso.vish.gg/application/o/gitea/.well-known/openid-configuration` | ### Portainer Configuration Configure via **Settings → Authentication → OAuth**: | Setting | Value | |---------|-------| | Client ID | (from Authentik) | | Client Secret | (from Authentik) | | Authorization URL | `https://sso.vish.gg/application/o/authorize/` | | Access Token URL | `https://sso.vish.gg/application/o/token/` | | Resource URL | `https://sso.vish.gg/application/o/userinfo/` | | Redirect URL | `http://vishinator.synology.me:10000` | | User Identifier | `email` | | Scopes | `openid profile email` | ### Seafile Configuration Add to `/volume1/docker/seafile/data/seafile/conf/seahub_settings.py`: ```python ENABLE_OAUTH = True OAUTH_ENABLE_INSECURE_TRANSPORT = False OAUTH_CLIENT_ID = "" OAUTH_CLIENT_SECRET = "" OAUTH_REDIRECT_URL = "https://sf.vish.gg/oauth/callback/" OAUTH_PROVIDER_DOMAIN = "sso.vish.gg" OAUTH_AUTHORIZATION_URL = "https://sso.vish.gg/application/o/authorize/" OAUTH_TOKEN_URL = "https://sso.vish.gg/application/o/token/" OAUTH_USER_INFO_URL = "https://sso.vish.gg/application/o/userinfo/" OAUTH_SCOPE = ["openid", "profile", "email"] OAUTH_ATTRIBUTE_MAP = { "email": (True, "email"), "name": (False, "name"), } ``` Then restart Seafile: `docker restart Seafile` --- ## NPM Integration ### For OAuth2 Services (Grafana, Gitea, etc.) **DO NOT add Forward Auth config!** These services handle OAuth themselves. NPM proxy host should be simple: - Forward host: service IP - Forward port: service port - SSL: enabled - Advanced config: **EMPTY** ### For Forward Auth Services (Paperless, Actual, etc.) Add this to NPM Advanced Config: ```nginx # Authentik Forward Auth Configuration proxy_buffers 8 16k; proxy_buffer_size 32k; auth_request /outpost.goauthentik.io/auth/nginx; error_page 401 = @goauthentik_proxy_signin; auth_request_set $auth_cookie $upstream_http_set_cookie; add_header Set-Cookie $auth_cookie; auth_request_set $authentik_username $upstream_http_x_authentik_username; auth_request_set $authentik_groups $upstream_http_x_authentik_groups; auth_request_set $authentik_email $upstream_http_x_authentik_email; auth_request_set $authentik_name $upstream_http_x_authentik_name; auth_request_set $authentik_uid $upstream_http_x_authentik_uid; proxy_set_header X-authentik-username $authentik_username; proxy_set_header X-authentik-groups $authentik_groups; proxy_set_header X-authentik-email $authentik_email; proxy_set_header X-authentik-name $authentik_name; proxy_set_header X-authentik-uid $authentik_uid; location /outpost.goauthentik.io { proxy_pass http://192.168.0.250:9000/outpost.goauthentik.io; proxy_set_header Host $host; proxy_set_header X-Original-URL $scheme://$http_host$request_uri; add_header Set-Cookie $auth_cookie; auth_request_set $auth_cookie $upstream_http_set_cookie; proxy_pass_request_body off; proxy_set_header Content-Length ""; } location @goauthentik_proxy_signin { internal; add_header Set-Cookie $auth_cookie; return 302 https://sso.vish.gg/outpost.goauthentik.io/start?rd=$scheme://$http_host$request_uri; } ``` ### Services with Forward Auth Configured | Domain | Backend | Port | |--------|---------|------| | paperless.vish.gg | 192.168.0.250 | 8777 | | docs.vish.gg | 192.168.0.250 | 8777 | | actual.vish.gg | 192.168.0.250 | 8304 | | npm.vish.gg | 192.168.0.250 | 81 | --- ## Troubleshooting ### "InternalError" After OAuth Login **Root Cause**: Missing scope mappings in Authentik provider. **Fix**: 1. Admin → Providers → Edit the OAuth2 provider 2. Scroll to "Scopes" section 3. Add: `openid`, `email`, `profile` 4. Save **Verify**: ```bash curl https://sso.vish.gg/application/o//.well-known/openid-configuration | jq '.scopes_supported' ``` ### Redirect Loop Between Service and Authentik **Root Cause**: Forward Auth configured in NPM for a service that uses native OAuth. **Fix**: 1. NPM → Proxy Hosts → Edit the affected host 2. Go to Advanced tab 3. **Clear all content** from the Advanced Config box 4. Save ### "User not found" or "No email" Errors **Root Cause**: Missing attribute paths in service config. **Fix for Grafana**: ``` GF_AUTH_GENERIC_OAUTH_EMAIL_ATTRIBUTE_PATH=email GF_AUTH_GENERIC_OAUTH_LOGIN_ATTRIBUTE_PATH=preferred_username ``` ### OAuth Works But User Gets Wrong Permissions **Root Cause**: Missing group claim or incorrect role mapping. **Fix**: 1. Ensure user is in correct Authentik group 2. Verify `groups` scope is included 3. Check role mapping expression in service config ### Can't Access Authentik Admin **Create recovery token via Portainer or SSH**: ```bash docker exec -it Authentik-SERVER ak create_recovery_key 10 akadmin ``` This generates a one-time URL valid for 10 minutes. --- ## Recovery Procedures ### Scenario: Complete Authentik Loss 1. **Restore from backup** (if available): ```bash # Restore PostgreSQL database docker exec -i Authentik-DB psql -U authentik authentik < backup.sql # Restore media files rsync -av backup/media/ /volume1/docker/authentik/media/ ``` 2. **Or redeploy from scratch**: - Follow this entire guide from [Deploy Authentik](#deploy-authentik) - You'll need to reconfigure all OAuth providers - Services will need their OAuth credentials updated ### Scenario: Locked Out of Admin ```bash # Via SSH to Calypso or Portainer exec docker exec -it Authentik-SERVER ak create_recovery_key 10 akadmin ``` Navigate to the URL it outputs. ### Scenario: Service OAuth Broken After Authentik Rebuild 1. Create new OAuth2 provider in Authentik (same settings) 2. Note new Client ID and Secret 3. Update service configuration with new credentials 4. Restart service 5. Test login ### Scenario: Forward Auth Not Working 1. Verify Authentik outpost is running: ```bash docker logs Authentik-SERVER | grep -i outpost ``` 2. Verify outpost includes the application: - Admin → Outposts → Edit → Check application is selected 3. Test outpost endpoint: ```bash curl -I http://192.168.0.250:9000/outpost.goauthentik.io/ping ``` 4. Check NPM Advanced Config has correct Authentik IP --- ## Quick Reference ### Authentik Endpoints | Endpoint | URL | |----------|-----| | Admin UI | `https://sso.vish.gg/if/admin/` | | User Portal | `https://sso.vish.gg/if/user/` | | Authorization | `https://sso.vish.gg/application/o/authorize/` | | Token | `https://sso.vish.gg/application/o/token/` | | User Info | `https://sso.vish.gg/application/o/userinfo/` | | OpenID Config | `https://sso.vish.gg/application/o//.well-known/openid-configuration` | | End Session | `https://sso.vish.gg/application/o//end-session/` | ### Service Status Checklist After rebuilding, verify each service: ```bash # OAuth2 Services curl -sI https://gf.vish.gg | head -1 # Should be 302 curl -sI https://git.vish.gg | head -1 # Should be 200 curl -sI https://sf.vish.gg | head -1 # Should be 302 # Forward Auth Services curl -sI https://paperless.vish.gg | head -1 # Should be 302 to SSO curl -sI https://actual.vish.gg | head -1 # Should be 302 to SSO # Authentik itself curl -sI https://sso.vish.gg | head -1 # Should be 302 ``` --- ## Change Log - **2026-01-31**: Initial creation based on live rebuild/verification session - **2026-01-31**: Documented scope mappings fix (critical for OAuth2) - **2026-01-31**: Added NPM Forward Auth vs OAuth2 distinction - **2026-01-31**: Added all service-specific configurations