---
title: Authentik SSO Configuration
type: reference
tags:
  - auth
aliases: []
related:
  - [[authentik-oidc-application]]
  - [[mailcow-create-mailbox]]
  - [[caddy]]
  - [[echo6-landing-page-data-export]]
  - [[authentik-access-groups]]
updated: 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`

```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 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

```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": "<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

```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<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](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 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 | 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*
