# Authentik SSO Configuration ## Location - **Server:** Contabo (5.189.158.149 / 100.64.0.1) - **URL:** https://auth.echo6.co - **Internal Port:** 9000 ## API Access API token stored in `/home/zvx/projects/.ref/credentials` as `AUTHENTIK_API_TOKEN` ```bash # Test API access curl -s "https://auth.echo6.co/api/v3/core/applications/" \ -H "Authorization: Bearer $AUTHENTIK_API_TOKEN" | python3 -m json.tool ``` ## Flow UUIDs Required for OAuth2 provider creation: | Flow | UUID | |------|------| | Authorization (implicit) | `86051292-389f-4bd9-b0f9-53cd32f197fd` | | Authorization (explicit) | `6f9f5c89-9f98-4776-9e0d-a72a8ad17963` | | Invalidation | `ed861c0d-2c81-4c3d-819b-946a21c4296a` | | Provider Invalidation | `1eb91626-19a3-4f45-b384-d699c6189197` | ## Signing Key | Key | UUID | |-----|------| | authentik Self-signed Certificate | `09f508f0-6b8e-4031-8563-0d3cebf86868` | ## Property Mappings (Standard OIDC Set) Include these three in every provider: | Mapping | UUID | |---------|------| | OpenID Connect scope: openid | `c6426dad-0d85-4daa-89fe-cb850ad4bfd7` | | Echo6 OAuth: email (verified=true) | `02c22323-da89-457a-bc12-7f4dd6a3d8ab` | | OpenID Connect scope: profile | `113ab791-fa04-4e8c-b103-09d706bc21b4` | **Note:** The default email scope (`096b0d6f`) was changed in 2025.10 to return `email_verified: false`. Our custom scope (`02c22323`) overrides this to always return `true`. All 14 OAuth2 providers were migrated to use the custom scope on 2026-02-16. ## Current OAuth2 Providers | PK | Name | Client ID | Application | Redirect URI | |----|------|-----------|-------------|--------------| | 1 | Mailcow | mailcow | Mailcow | `https://mail.echo6.co/sso/oidc` | | 2 | Forgejo | forgejo | Forgejo | `https://forge.echo6.co/user/oauth2/Authentik/callback` | | 3 | Vaultwarden | vaultwarden | Vaultwarden | `https://vault.echo6.co/identity/connect/callback` | | 4 | Proxmox | proxmox | Proxmox VE | `https://proxmox.echo6.co` (regex) | | 5 | Headscale | headscale | Headscale VPN | `https://vpn.echo6.co/oidc/callback` | | 6 | Headplane | headplane | Headplane | `https://vpn.echo6.co/admin/oidc/callback` | | 8 | Nextcloud | nextcloud | Nextcloud | `https://nextcloud.echo6.co/apps/oidc_login/oidc` | | 9 | Immich | immich | Immich | `https://immich.echo6.co/auth/login`, `/api/oauth/mobile-redirect` | | 10 | jellyfin | jellyfin | Jellyfin | `https://jellyfin.echo6.co/sso/OID/redirect/Authentik` | | 11 | jellyseer | jellyseer | Jellyseer | `https://requests.echo6.co/(login\|api/v1/auth/oidc-callback).*` (regex) | | 12 | PeerTube | peertube | PeerTube | `https://stream.echo6.co/plugins/auth-openid-connect/...` | | 13 | WATCHTOWER | watchtower | WATCHTOWER | Forward auth (proxy provider) | | 14 | Open WebUI | open-webui | Open WebUI | `https://ai.echo6.co/oauth/oidc/callback` | | 15 | Matrix | 93kCoZkBlnJyD9EcAm7E4btKflecOcBm9DGONB5T | Matrix | `https://matrix.echo6.co/_synapse/client/oidc/callback` | | 16 | Files Forward Auth | — | Files | Forward auth (proxy provider) | | 17 | LiveSync Provisioner | ZBoLdYmxlSUyMqgekswIPS4YuaeBn5uCr8GtWm5H | LiveSync | Forward auth (proxy provider) | ## Groups | Name | PK | Superuser | Members | Used By | |------|----|-----------|---------|----- ---| | authentik Admins | `9944e153-f860-4443-81d1-ae544f611806` | Yes | akadmin, matt | All apps (admin access) | | media-users | `0820b2b8-6c54-4c20-9a0a-872820e6d9ea` | No | jodie, matt | Jellyfin, Jellyseer, PeerTube | | ai-users | `0631b273-cfd8-4ed1-afa6-e262d0dc5a69` | No | matt | Open WebUI | | cloud-users | `db3cbf5d-8057-4e33-8e8d-95bfdb35fbac` | No | — | Immich, Nextcloud | | communication-users | `31bce176-cd86-4aea-8db3-a57e03d5c2d1` | No | — | Mailcow, Matrix | | productivity-users | `698d80c7-7c29-43cd-b5d4-9eb24c85a6cc` | No | — | — | | security-users | `f345a043-c2a4-4906-a43b-9860eae86ee1` | No | — | — | | proxmox_admins | `d85a868d-7d1e-4585-92a8-b8bb86771b53` | No | akadmin, matt | Proxmox VE | | proxmox_users | `cf26703a-a824-47dd-9550-30b848a8ce5f` | No | — | Proxmox VE | | authentik Read-only | `ce03664b-46f3-43b7-9967-5f65d591fdb6` | No | — | RBAC read-only role | | livesync-users | `8e575a86-326e-4df8-8828-8379d8ab861f` | No | matt | LiveSync | ## Application Access Pattern Every application uses `policy_engine_mode: "any"` with two group bindings: 1. **authentik Admins** — gives admin/superuser access to all apps 2. **Service-specific group** — controls which regular users can access the app Users must be in at least one bound group to access the application. ## Create New API Token ```bash ssh root@100.64.0.1 'docker exec -i authentik-server ak shell' <<'PYEOF' from authentik.core.models import Token, TokenIntents, User user = User.objects.get(username="akadmin") Token.objects.filter(identifier="token-name").delete() t = Token(identifier="token-name", user=user, intent=TokenIntents.INTENT_API, expiring=False, managed=None) t.save() print(t.key) PYEOF ``` ## Full OAuth2 App Setup (Provider + Application + Group + Bindings) ### Step 1: Create the OAuth2 provider ```bash curl -s -X POST "https://auth.echo6.co/api/v3/providers/oauth2/" \ -H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "AppName", "authorization_flow": "86051292-389f-4bd9-b0f9-53cd32f197fd", "invalidation_flow": "ed861c0d-2c81-4c3d-819b-946a21c4296a", "property_mappings": [ "c6426dad-0d85-4daa-89fe-cb850ad4bfd7", "02c22323-da89-457a-bc12-7f4dd6a3d8ab", "113ab791-fa04-4e8c-b103-09d706bc21b4" ], "client_type": "confidential", "client_id": "appname", "client_secret": "", "redirect_uris": [{"matching_mode": "strict", "url": "https://app.echo6.co/callback"}], "sub_mode": "user_username", "signing_key": "09f508f0-6b8e-4031-8563-0d3cebf86868", "include_claims_in_id_token": true }' # Note the "pk" from the response — needed for Step 2 ``` ### Step 2: Create the application ```bash curl -s -X POST "https://auth.echo6.co/api/v3/core/applications/" \ -H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "AppName", "slug": "appname", "provider": PROVIDER_PK, "meta_launch_url": "https://app.echo6.co", "policy_engine_mode": "any" }' # Note the "pk" from the response — needed for Step 4 ``` ### Step 3: Create the user group ```bash curl -s -X POST "https://auth.echo6.co/api/v3/core/groups/" \ -H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "appname-users", "is_superuser": false, "users": [7] }' # user 7 = matt. Note the "pk" from the response. ``` ### Step 4: Bind groups to the application ```bash # Bind authentik Admins curl -s -X POST "https://auth.echo6.co/api/v3/policies/bindings/" \ -H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "target": "APPLICATION_PK", "group": "9944e153-f860-4443-81d1-ae544f611806", "order": 0, "enabled": true, "negate": false, "timeout": 30 }' # Bind service-specific group curl -s -X POST "https://auth.echo6.co/api/v3/policies/bindings/" \ -H "Authorization: Bearer $AUTHENTIK_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "target": "APPLICATION_PK", "group": "GROUP_PK", "order": 0, "enabled": true, "negate": false, "timeout": 30 }' ``` ### Step 5: Verify ```bash # OIDC discovery should return endpoints curl -s "https://auth.echo6.co/application/o/appname/.well-known/openid-configuration" | python3 -m json.tool # Check bindings curl -s "https://auth.echo6.co/api/v3/policies/bindings/?target=APPLICATION_PK" \ -H "Authorization: Bearer $AUTHENTIK_API_TOKEN" ``` ## Common Redirect URI Patterns | Application Type | Redirect URI Pattern | |------------------|---------------------| | Web app (generic) | `https://app.echo6.co/callback` | | Web app (oauth path) | `https://app.echo6.co/oauth/callback` | | Open WebUI (OIDC) | `https://app.echo6.co/oauth/oidc/callback` | | Forgejo | `https://app.echo6.co/user/oauth2/Authentik/callback` | | Jellyfin (SSO plugin) | `https://app.echo6.co/sso/OID/redirect/Authentik` | | Caddy forward auth | `https://app.echo6.co/outpost.goauthentik.io/callback` | ## Users | PK | Username | Name | Email | |----|----------|------|-------| | 6 | akadmin | authentik Default Admin | root@example.com | | 7 | matt | Matt Johnson | matt@echo6.co | | 9 | jodie | Jodie | johnsonsinidaho@gmail.com | ## Email Invitation System ### SMTP Configuration Authentik sends email via Mailcow: | Setting | Value | |---------|-------| | SMTP Host | mail.echo6.co | | SMTP Port | 587 (STARTTLS) | | Username | no-reply@echo6.co | | From | no-reply@echo6.co | **Important:** The no-reply@echo6.co mailbox MUST have `authsource=mailcow` in the Mailcow database (not `generic-oidc`). If it gets reset to `generic-oidc`, SMTP auth will fail because Dovecot tries to authenticate via Authentik SSO instead of the local password. Fix with: ```sql docker exec mailcowdockerized-mysql-mailcow-1 mysql -u mailcow -p mailcow \ -e "UPDATE mailbox SET authsource='mailcow' WHERE username='no-reply@echo6.co'" ``` ### Enrollment Flow | Component | PK | |-----------|-----| | Flow (invitation-enrollment) | `184a9e20-f5c5-4b44-8775-266a568439c0` | | Invitation stage (enrollment-invitation) | `994252a6-a659-4304-b9aa-a6591857a53b` | | Prompt stage (enrollment-credentials) | `a5e2a2f1-95c9-4627-a3dd-e0915ff64cda` | | User write stage (enrollment-user-write) | `05f565e0-9d64-4ecc-b738-8ffc697ad829` | | User login stage (enrollment-user-login) | `f23958ea-47bd-4fb2-a657-b6f85fc6331a` | Stage order: Invitation (10) → Prompt (20) → User Write (30) → User Login (40) Flow settings: `authentication=require_unauthenticated`, `continue_flow_without_invitation=false` User write creates users under `users/enrolled` path. ### Email Automation Uses the community pattern from [authentik/discussions/13305](https://github.com/goauthentik/authentik/discussions/13305): | Component | PK | |-----------|-----| | Expression policy (invitation-email-sender) | `770188de-2b60-4e87-820a-fa64d419ed89` | | Notification rule (invitation-email-trigger) | `a145a8c9-b3eb-440d-8b77-27139c346a17` | When an invitation is created with an `email` field in custom attributes, the expression policy triggers `ak_send_email()` to send the enrollment link to the invitee automatically. ### How to Use **Invite via email** (Admin UI → Directory → Invitations → Create): 1. Name it, select **Invitation Enrollment** flow, toggle **Single use** on, set expiry 2. Custom attributes: ```yaml name: Jane Smith email: jane@example.com ``` 3. Click Create — email is sent automatically **Invite via link** (no email): 1. Same as above but leave custom attributes empty (or omit `email` field) 2. Click Create → expand the row → copy the invitation link 3. Send the link manually --- ## Branding & Theming Echo6 cyberpunk branding applied to Authentik 2025.12.4 via System → Brands. ### Brand Settings | Setting | Value | |---------|-------| | Brand title | `echo6` | | Theme | `dark` (forced, not automatic) | | Logo | `/media/custom/echo6-logo.png` (uploaded via Customization → Files) | | Favicon | `/media/custom/echo6-favicon.png` (uploaded via Customization → Files) | | Custom CSS | Echo6 Authentik CSS (~200 rules, applied via Brand → Custom CSS field) | ### Flow Titles | Flow | Title | |------|-------| | Authentication | `echo6 // login` | | Invalidation | `echo6 // logout` | | Recovery | `echo6 // recovery` | | User Settings | `echo6 // settings` | ### Custom CSS Highlights - **Font:** JetBrains Mono globally (with `:not()` exclusions for FontAwesome/PatternFly icon fonts) - **Colors:** Cyan `#28C0E8` primary accent, Yellow `#F0D848` secondary, dark backgrounds `#0a0e17`/`#111827`/`#1a2332` - **Login card:** Dark background, cyan-glow focus on inputs, branded submit button - **Admin sidebar:** Dark with cyan hover/active states - **User dashboard:** 3-column grid layout, dark cards with cyan border on hover - **Application icons:** Custom SVG icons uploaded for all 15 services ### CSS Storage The custom CSS is stored in the Brand model's `branding_custom_css` field. To update: ```bash # Copy CSS to Contabo scp /path/to/echo6-authentik.css root@100.64.0.1:/opt/authentik/branding/custom.css # Load into Brand model via ak shell ssh root@100.64.0.1 'docker exec -i authentik-server ak shell' <<'PYEOF' from authentik.brands.models import Brand b = Brand.objects.get(domain="auth.echo6.co") b.branding_custom_css = open("/media/custom/custom.css").read() b.save() PYEOF # Restart to apply ssh root@100.64.0.1 'cd /opt/authentik && docker compose restart server worker' ``` ### SSO Launch URL Pattern All authenticated service links use Authentik's application launch URL: ``` https://auth.echo6.co/application/launch// ``` This provides seamless SSO: authenticated users pass through to the app, unauthenticated users get the login page then redirect to the app. Used by the SearXNG waffle menu and nav bar. | App Slug | Service | Launch URL | |----------|---------|-----------| | open-webui | Aurora (AI) | `https://auth.echo6.co/application/launch/open-webui/` | | peertube | Stream | `https://auth.echo6.co/application/launch/peertube/` | | files | Files | `https://auth.echo6.co/application/launch/files/` | | watchtower | Watchtower | `https://auth.echo6.co/application/launch/watchtower/` | | immich | Photos | `https://auth.echo6.co/application/launch/immich/` | | mailcow | Mail | `https://auth.echo6.co/application/launch/mailcow/` | | nextcloud | Cloud | `https://auth.echo6.co/application/launch/nextcloud/` | | jellyfin | Jellyfin | `https://auth.echo6.co/application/launch/jellyfin/` | | jellyseer | Requests | `https://auth.echo6.co/application/launch/jellyseer/` | ### Brand Color Reference | Color | Hex | Usage | |-------|-----|-------| | Cyan | `#28C0E8` | Primary accent, links, focus states | | Cyan Light | `#5DD4F5` | Hover states | | Yellow | `#F0D848` | Secondary accent, badges | | BG Primary | `#0a0e17` | Page backgrounds | | BG Secondary | `#111827` | Cards, inputs | | BG Tertiary | `#1a2332` | Elevated surfaces | | Border | `#1e3a5f` | All borders | | Text Primary | `#e0e6ed` | Main text | | Text Muted | `#7a8ca0` | Secondary text | --- *Last updated: 2026-02-18 — Added LiveSync proxy provider (PK 17) + livesync-users group, Files Forward Auth (PK 16). Authentik 2025.12.4*