Files changed: ", c.get(k))\nPY\n\\\"\n\"" engine/config.yaml engine/lib/lint.py engine/lib/vocab_gen.py engine/lint-report.md engine/sweep.sh vault/.obsidian/workspace.json vault/archive/projects/meshai-native-fire-severity-audit-cc-handoff.md vault/archive/projects/vaultwarden-plan.md vault/docs/matrix/matrix_host.md vault/docs/matrix/synapse.md vault/docs/services/services.md vault/docs/software/authentik.md vault/docs/software/caddy.md vault/docs/software/dns.md vault/docs/software/recon.md vault/docs/software/searxng.md vault/glossary.md vault/notes/echo6-landing-page-data-export.md vault/projects/matrix-synapse-deployment.md vault/projects/meshai.md vault/projects/meshtastic-headscale-runbook.md vault/projects/mmud-project.md vault/runbooks/add-peertube-channel.md vault/runbooks/authentik-access-groups.md vault/runbooks/authentik-create-invitation.md vault/runbooks/authentik-oidc-application.md vault/runbooks/authentik-upgrade.md vault/runbooks/expose-service-contabo.md vault/runbooks/lxc-service-migration.md vault/runbooks/mailcow-create-mailbox.md vault/runbooks/meshtastic-sidecar-node.md vault/runbooks/meshtasticd-sim-nodes-runbook.md vault/runbooks/proxmox-create-ubuntu-vm.md vault/runbooks/recon-operations.md vault/runbooks/recon-service-integration.md vault/runbooks/syncthing-add-node.md
15 KiB
| title | type | tags | aliases | related | updated | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Authentik SSO Configuration | reference |
|
|
2026-07-11 |
Authentik SSO Configuration
Migrated off Contabo → edge2 on 2026-06-18.
Location
- Server: edge2 CT 105 (100.64.0.36)
- URL: https://auth.echo6.co
- Internal Port: 9000
API Access
API token stored in /home/zvx/projects/.ref/credentials as AUTHENTIK_API_TOKEN
# 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:
- authentik Admins — gives admin/superuser access to all apps
- 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
ssh edge2 'sudo pct exec 105 -- 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
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": "<generate-a-long-random-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
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
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
# 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
# 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 | |
|---|---|---|---|
| 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:
docker exec mailcowdockerized-mysql-mailcow-1 mysql -u mailcow -p<DBPASS> 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:
| 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):
- Name it, select Invitation Enrollment flow, toggle Single use on, set expiry
- Custom attributes:
name: Jane Smith email: jane@example.com - Click Create — email is sent automatically
Invite via link (no email):
- Same as above but leave custom attributes empty (or omit
emailfield) - Click Create → expand the row → copy the invitation link
- 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
#28C0E8primary accent, Yellow#F0D848secondary, 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:
# Copy CSS to edge2 CT 105 (via edge2 host, then pct push into the CT)
scp /path/to/echo6-authentik.css edge2:/tmp/echo6-authentik.css
ssh edge2 'sudo pct push 105 /tmp/echo6-authentik.css /opt/authentik/branding/custom.css'
# Load into Brand model via ak shell
ssh edge2 'sudo pct exec 105 -- 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 edge2 'sudo pct exec 105 -- bash -c "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/<app-slug>/
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 | 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