# Authentik SSO Setup Single Sign-On (SSO) for homelab services using Authentik. ## Overview Authentik provides centralized authentication for all homelab services via OAuth2/OpenID Connect. - **URL**: https://sso.vish.gg - **Admin Interface**: https://sso.vish.gg/if/admin/ - **User Portal**: https://sso.vish.gg/if/user/ - **Host**: Calypso NAS (Synology DS723+) - **Stack**: Docker Compose via Portainer ## Admin Credentials - **Username**: `akadmin` - **Email**: `admin@example.com` - **Password**: REDACTED_PASSWORD in password manager ## Architecture ``` ┌──────────────────┐ │ Cloudflare │ │ (DNS + SSL) │ └────────┬─────────┘ │ ┌────────▼─────────┐ │ sso.vish.gg │ │ (Authentik) │ │ Calypso NAS │ └────────┬─────────┘ │ ┌────────────────────┼────────────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────┐ ┌─────────┐ ┌──────────┐ │ Grafana │ │ Gitea │ │Portainer │ │gf.vish.gg│ │git.vish.gg│ │ internal │ │homelab-vm│ │ Calypso │ │ Calypso │ └─────────┘ └─────────┘ └──────────┘ ``` ## OAuth2 Providers ### Grafana | Setting | Value | |---------|-------| | Client ID | `lEGw1UJ9Mhk6QVrNA61rAsr59Kel9gAvdPQ1FAJA` | | Client Secret | `ArP5XWdkwVyw9nvXZaqjE9sIjXdmIgpgI4ZR8oKvTUVLgmIGVvKU8T867diMGSQXgTcWQQPbdbEdXTU1v3y9RKMnAqu2k6V4xlmxwNYlCDuk5inxJSdoC0V8ICtZxk1X` | | Redirect URI | `https://gf.vish.gg` | | Scopes | `openid profile email` | **Configuration File**: `hosts/vms/homelab-vm/monitoring.yaml` ### Gitea | Setting | Value | |---------|-------| | Client ID | `7KamS51a0H7V8HyIsfMKNJ8COstZEFh4Z8Em6ZhO` | | Client Secret | `3IjyKCbHtgev6eMb1hYpQGHoGwPSRKda4ijRtbWfkhguNomxexxTiWtoWtyrXwGaF0ORj4D7D0kzB3Z1YN9DN5iz0HOKjAn5AdWJrSyxan02MjiwKmEriAbSGyh53uph` | | Redirect URI | `https://git.vish.gg/user/oauth2/authentik/callback` | | Discovery URL | `https://sso.vish.gg/application/o/gitea/.well-known/openid-configuration` | **Configuration File**: `hosts/synology/calypso/gitea-server.yaml` **Manual Setup Required**: Add OAuth2 source in Gitea admin UI: 1. Go to Site Administration → Authentication Sources 2. Add new OAuth2 source 3. Use Discovery URL for auto-configuration ### Portainer | Setting | Value | |---------|-------| | Client ID | `fLLnVh8iUyJYdw5HKdt1Q7LHKJLLB8tLZwxmVhNs` | | Client Secret | `xD9u47XbJd2g7vCeIyJC7MNvfEqytEnnHeVtJ7nU5Y1XGxYncXkejNAYkToUiRWcym3GpZIXgMpUnNNuUwud0Ff493ZwSHCiSKsk9n6RJLJ1iVvR20NdDnMe4YEGYXrt` | | Redirect URI | `http://vishinator.synology.me:10000` | | User Identifier | `email` | **Configuration**: Via Portainer API (`/api/settings`) ### Reactive Resume v5 | Setting | Value | |---------|-------| | Client ID | `QU5qA7jLP9ghxy7iGMJoyZsCja2vY2Y2oGaLGjxA` | | Client Secret | `wX1aFaby4aIABjLBBClYu4ukmIOjviL85GJBX8bAB3srQnt1BD31LcblRKyxzuv1yGwtsKLTFjwz12rUy6HknOqpIwk1QQ21jMjpWb1aa77iRG6lDkf4eNf8wWpE9Apo` | | Redirect URI | `https://rx.vish.gg/api/auth/callback/custom` | | Discovery URL | `https://sso.vish.gg/application/o/reactive-resume/.well-known/openid-configuration` | **Configuration File**: `hosts/synology/calypso/reactive_resume_v5/docker-compose.yml` (also live at `/volume1/docker/rxv5/docker-compose.yml` on Calypso) ### Homarr | Setting | Value | |---------|-------| | Client ID | `8oP0ha7gLjdz13MAPVsb7fe7TBkFBz7mt1eU8MEO` | | Client Secret | `SpJXIGDk3SJfiS9GJwzH0fKrePsrumvCOmvFd2h0hEfxXMO77aCtpPEs6FShLTaUW5YxqgEDFkQi7q9NIOQDJTPQHlSy3nIeyDQmS2tVIV1BpSdGpnLQedouOkXACwe2` | | Redirect URI | `https://dash.vish.gg/api/auth/callback/oidc` | | Admin Group | `Homarr Admins` (Authentik group, pk=`892da833-5283-4672-a906-7448ae3ba9b6`) | | Discovery URL | `https://sso.vish.gg/application/o/homarr/.well-known/openid-configuration` | **Configuration File**: `hosts/synology/atlantis/homarr.yaml` **Note**: `SECRET_ENCRYPTION_KEY` is required by Homarr — a 64-char hex key must be provided as an env var. The `AUTH_OIDC_ADMIN_GROUP` and `AUTH_OIDC_OWNER_GROUP` map to an Authentik group name. ### Immich | Setting | Value | |---------|-------| | Client ID | `XSHhp1Hys1ZyRpbpGUv4iqu1y1kJXX7WIIFETqcL` | | Client Secret | `mlbc4NbqiyRyUSqeUupaob7WsA3sURWExmoxYAcozClnmsdCPzGHlyO6zmErnS9YNyBsKOYoGUPvSTQPrE07UnYDLSMy286fycHoAJoc0cAN8BMc5cIif5kf88NSNCj2` | | Redirect URIs | `http://192.168.0.250:8212/auth/login`, `http://calypso.vish.local:8212/auth/login`, `app.immich:/` | | Issuer URL | `https://sso.vish.gg/application/o/immich/` | | Button Text | `Sign in with Authentik` | | Auto Register | true | **Configuration**: Via `immich-config.json` mounted at `/config/immich-config.json` inside the container. Config file lives at `/volume1/docker/immich/config/immich-config.json` on Calypso and is tracked at `/home/homelab/immich-config.json`. **Note**: Immich constructs the redirect URI dynamically from the hostname the browser used to access it — so every access hostname must be registered in Authentik. Currently registered: IP, `calypso.vish.local`, `app.immich:/`. `mobileRedirectUri` in the config file must be empty string — Immich's validator rejects custom URI schemes there. ### Headplane | Setting | Value | |---------|-------| | Provider PK | `16` | | Client ID | `1xLx9TkufvLGKgq8UmQV2RfTB6raSpEjZExBOhJ4` | | Client Secret | `4r4n96jBGc8MlonyHStiN09ow0txTwERLupt9hsoNswpicEnJZHgKwi38jYP5zlou5J525dVFUmXNSvnxwBJgKIIAfpC43zi8yUVtT0NYNdEBeYQOsh1YW5jK8nVPSdc` | | Redirect URI | `https://headscale.vish.gg:8443/admin/oidc/callback` | | Issuer URL | `https://sso.vish.gg/application/o/headplane/` | | Scopes | `openid profile email` | | Sub Mode | `hashed_user_id` | **Configuration File**: `hosts/synology/calypso/headplane-config.yaml` (reference, secrets redacted). Live config at `/volume1/docker/headscale/headplane/config.yaml` on Calypso. **Note**: Headplane is served at `https://headscale.vish.gg:8443/admin` — no separate domain. NPM proxy host 44 routes `/admin` to port 3002. First user to log in via OIDC is automatically assigned the Owner role. ### NetBox | Setting | Value | |---------|-------| | Provider PK | `23` | | Client ID | `BB7PiOu8xFOl58H2MUfl9IHISVLuJ4UwwMGvmJ9N` | | Client Secret | `CRdRVCM13JN9bSiT2aU74cFXSI9GpVBLBShOFGBpVHOQ4brnDWOzk8I02cEww8Gcrr6GnsU0XdBxHTEpfvX2u9rhmey7XDT3XUVVh9ADaSldww83hp4hAzH5eNx1zKvB` | | Redirect URI | `https://nb.vish.gg/oauth/complete/oidc/` | | Discovery URL | `https://sso.vish.gg/application/o/netbox/.well-known/openid-configuration` | | Scopes | `openid profile email` | **Configuration**: NetBox `configuration.py` on homelab-vm (`/home/homelab/docker/netbox/config/configuration.py`). Uses `python-social-auth` with `social_core.backends.open_id_connect.OpenIdConnectAuth` backend. `associate_by_email` pipeline maps Authentik users to existing NetBox accounts by email. ## Authentik Endpoints | Endpoint | URL | |----------|-----| | 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/` | | JWKS | `https://sso.vish.gg/application/o/{app-slug}/jwks/` | | OpenID Config | `https://sso.vish.gg/application/o/{app-slug}/.well-known/openid-configuration` | | End Session | `https://sso.vish.gg/application/o/{app-slug}/end-session/` | ## Docker Compose Configuration **Location**: `hosts/synology/calypso/authentik.yaml` Key environment variables: - `AUTHENTIK_SECRET_KEY`: Random secret for encryption - `AUTHENTIK_REDIS__HOST`: Redis container hostname - `AUTHENTIK_POSTGRESQL__*`: PostgreSQL connection settings ## SSL/TLS Configuration SSL is handled by Cloudflare Origin Certificate: - Certificate ID: `lONWNn` (Synology reverse proxy) - Covers: `*.vish.gg` - Origin: Cloudflare Full (Strict) mode ## DNS Configuration | Domain | Type | Target | Proxy | |--------|------|--------|-------| | sso.vish.gg | CNAME | calypso DDNS | Orange (proxied) | ## Adding New Services ### Method 1: OAuth2/OpenID (for apps that support it) 1. **Create Provider in Authentik** - Admin → Providers → Create → OAuth2/OpenID - Set name, redirect URIs, scopes 2. **Create Application** - Admin → Applications → Create - Link to provider - Set launch URL 3. **Configure Service** - Add OAuth2/OIDC settings to service config - Use Authentik endpoints - Test login flow ### Method 2: Proxy Provider (for apps without OAuth support) Use this for apps like Actual Budget, Paperless-NGX, etc. 1. **Create Proxy Provider in Authentik** - Admin → Providers → Create → Proxy Provider - Name: e.g., "actual-proxy" - Authorization flow: default-provider-authorization-implicit-consent - External host: `https://actual.vish.gg` - Mode: Forward auth (single application) 2. **Create Application** - Admin → Applications → Create - Name: e.g., "Actual Budget" - Slug: `actual` - Provider: Select the proxy provider - Launch URL: `https://actual.vish.gg` 3. **Create Outpost** (if not exists) - Admin → Applications → Outposts - Create embedded outpost or deploy standalone - Add the application to the outpost 4. **Configure Nginx/Reverse Proxy** Add forward auth to your reverse proxy config: ```nginx location / { # Forward auth to Authentik 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; proxy_set_header X-authentik-username $authentik_username; proxy_set_header X-authentik-groups $authentik_groups; proxy_set_header X-authentik-email $authentik_email; # Your existing proxy_pass proxy_pass http://localhost:PORT; } location /outpost.goauthentik.io { proxy_pass https://sso.vish.gg/outpost.goauthentik.io; proxy_set_header Host $host; proxy_set_header X-Original-URL $scheme://$http_host$request_uri; } location @goauthentik_proxy_signin { internal; add_header Set-Cookie $auth_cookie; return 302 /outpost.goauthentik.io/start?rd=$request_uri; } ``` ### Bypassing Auth for Share Links For services like Seafile that have share links: ```nginx # Allow share links without auth location /f/ { proxy_pass http://localhost:8611; } location /d/ { proxy_pass http://localhost:8611; } # Everything else requires auth location / { auth_request /outpost.goauthentik.io/auth/nginx; # ... rest of auth config proxy_pass http://localhost:8611; } ``` ## Services Protection Summary ### OAuth2/OpenID Connect (Login Button) Services with native OAuth support - users see a "Sign in with Authentik" button. | Domain | Service | Backend | Port | Status | |--------|---------|---------|------|--------| | gf.vish.gg | Grafana | 192.168.0.210 | 3300 | ✅ Working | | git.vish.gg | Gitea | 192.168.0.250 | 3052 | ✅ Working | | sf.vish.gg | Seafile | 192.168.0.250 | 8611 | ✅ Working | | vishinator.synology.me:10000 | Portainer | 192.168.0.250 | 9000 | ✅ Working | | rx.vish.gg | Reactive Resume v5 | 192.168.0.250 | 4550 | ✅ Working | | dash.vish.gg | Homarr | 192.168.0.200 | 7575 | ✅ Working | | immich.vish.gg | Immich | 192.168.0.250 | 8212 | ✅ Working | | headscale.vish.gg/admin | Headplane | 192.168.0.250 | 3002 | ✅ Working | | nb.vish.gg | NetBox | 192.168.0.210 | 8443 | ✅ Working | ### Proxy Provider (Forward Auth) Services without OAuth support - Authentik intercepts all requests and requires login first. | Domain | Service | Backend | Port | Status | |--------|---------|---------|------|--------| | paperless.vish.gg | Paperless-NGX | 192.168.0.250 | 8777 | ✅ Working | | docs.vish.gg | Paperless-NGX | 192.168.0.250 | 8777 | ✅ Working | | actual.vish.gg | Actual Budget | 192.168.0.250 | 8304 | ✅ Working | | npm.vish.gg | NPM Admin | 192.168.0.250 | 81 | ✅ Working | | kuma.vish.gg | Uptime Kuma | 192.168.0.66 | 3001 | ✅ Working — `/status/*` public, rest gated | | ollama.vish.gg | Ollama | 192.168.0.200 | 11434 | ✅ Working | | wizarr.vish.gg | Wizarr | 192.168.0.200 | 5690 | ❌ Removed — caused redirect loop; Wizarr uses own auth | ### Services Without SSO These services use their own authentication or are public. | Domain | Service | Backend | Notes | |--------|---------|---------|-------| | sso.vish.gg | Authentik | 192.168.0.250:9000 | SSO itself | | pw.vish.gg | Vaultwarden | 192.168.0.200:4080 | Own auth | | ntfy.vish.gg | Ntfy | 192.168.0.210:8081 | Own auth | | cal.vish.gg | Baikal | 192.168.0.200:12852 | CalDAV auth | | dav.vish.gg | Seafile WebDAV | 192.168.0.250:8612 | WebDAV auth | | mm.crista.love | Mattermost | 192.168.0.154:8065 | Own auth | | mastodon.vish.gg | Mastodon | 192.168.0.154:3000 | Own auth | | mx.vish.gg | Mail | 192.168.0.154:8082 | Own auth | | ollama.vish.gg | Ollama | 192.168.0.200:11434 | See Forward Auth table above | | retro.vish.gg | Retro Site | 192.168.0.250:8025 | Static site | | rackula.vish.gg | Rackula | 192.168.0.250:3891 | Own auth | | ost.vish.gg | OpenSpeedTest | 192.168.0.250:8004 | Public | ### Other Domains | Domain | Service | Backend | Notes | |--------|---------|---------|-------| | hoarder.thevish.io | Hoarder | 192.168.0.210:3000 | Own auth | | matrix.thevish.io | Matrix | 192.168.0.154:8081 | Own auth | | joplin.thevish.io | Joplin Server | 192.168.0.200:22300 | Own auth | | meet.thevish.io | Jitsi | 192.168.0.200:5443 | Public | | binterest.thevish.io | Binternet | 192.168.0.210:21544 | Own auth | | crista.love | Personal Site | 192.168.0.100:28888 | Static | | rxv4access.vish.gg | Reactive Resume v4 | 192.168.0.250:9751 | STALE - 525 SSL error, dead instance | ## Troubleshooting ### OAuth Login Fails with "Unauthorized" - Verify user has email set in Authentik - Check redirect URI matches exactly - Verify client secret is correct ### Certificate Errors - Ensure Cloudflare proxy is enabled (orange cloud) - Verify origin certificate is valid - Check Synology reverse proxy SSL settings ### User Auto-Creation Not Working - Enable "Auto Create Users" in service OAuth settings - Verify email scope is requested - Check user identifier matches (email/username) ## Recovery Access If locked out of Authentik admin, you can create a recovery token: ```bash # Via Portainer exec or SSH to Calypso docker exec -it Authentik-SERVER ak create_recovery_key 10 akadmin ``` This generates a one-time recovery URL valid for 10 minutes. ## Related Documentation - [Cloudflare Tunnels](./cloudflare-tunnels.md) - [Port Forwarding Configuration](./port-forwarding-configuration.md) - [Security](./security.md) - [Grafana OAuth](../services/individual/grafana-oauth.md) - [Gitea OAuth](../services/individual/gitea.md#-oauth2-single-sign-on-authentik) - [Seafile OAuth](../services/individual/seafile-oauth.md) ## Change Log - **2026-03-17**: Added NetBox OIDC provider (pk=23) — nb.vish.gg, associate_by_email pipeline - **2026-03-17**: Removed Wizarr forward auth from NPM (wizarr has own auth, forward auth caused redirect loop) - **2026-03-11**: Added Headplane OIDC provider (pk=16) — Headscale web UI at headscale.vish.gg/admin, port 3002 - **2026-03-08**: Added Forward Auth for Uptime Kuma (kuma.vish.gg), Ollama (ollama.vish.gg), Wizarr (wizarr.vish.gg) - **2026-03-08**: Kuma /status/* and Wizarr /i/* paths are public; all other paths gated - **2026-03-08**: Removed Forward Auth from dash.vish.gg NPM proxy (Homarr handles auth natively via OIDC) - **2026-03-08**: Disabled Uptime Kuma built-in auth (disableAuth=true in SQLite); Authentik is sole gate - **2026-03-08**: Calibre-Web started on port 8183 (8083 was occupied by Watchtower) - **2026-03-08**: Added OIDC for Reactive Resume v5 (rx.vish.gg), Homarr (dash.vish.gg), Immich (immich.vish.gg) — all working - **2026-03-08**: Fixed Homarr startup crash — SECRET_ENCRYPTION_KEY is mandatory (64-char hex) - **2026-03-08**: Immich OAuth configured via immich-config.json mount (not Admin UI); mobileRedirectUri must be empty - **2026-03-08**: Immich stack.env added to repo so stack is self-contained (no Portainer env injection needed) - **2026-03-08**: Flagged rxv4access.vish.gg as stale (dead RR v4 instance, 525 SSL error) - **2026-01-31**: Verified all OAuth2 and Forward Auth services working - **2026-01-31**: Fixed Grafana OAuth "InternalError" - added scope mappings to provider - **2026-01-31**: Removed Forward Auth from NPM for gf.vish.gg (conflicts with native OAuth) - **2026-01-31**: Added scope mappings to Gitea, Portainer, Seafile OAuth2 providers - **2026-01-31**: Updated comprehensive service protection summary