docs: document Authentik OIDC standard for new services

Pattern A (native OAuth2) is preferred — create provider + app in
Authentik, configure service with discovery URL. Pattern B (Caddy
forward_auth) is the fallback for services with no native auth.
Reusable Authentik flow/scope PKs pinned so they don't need re-deriving.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ginnoir
2026-06-15 22:44:59 -05:00
co-authored by Claude Sonnet 4.6
parent acdf61500c
commit a937de7bda
+61
View File
@@ -97,6 +97,67 @@ ssh ginnoir@valhalla "docker compose -f /config/portainer/compose/14/stacks/ownc
5. Run `./scripts/gen-bookmarks.ps1` to regenerate `bookmarks-domains.html` + `bookmarks-ports.html` from the new Caddy block / published ports.
6. `git push`. Portainer redeploys the stack within 5 min; the runner reloads Caddy on the same push if `Caddyfile` changed.
## Authentication standard
Every new service gets SSO. Pick the right pattern based on what the app supports:
### Pattern A — Native OAuth2/OIDC (preferred)
If the service has built-in OAuth2/OIDC login (Immich, Paperless-ngx, Gitea, etc.):
1. **Create an Authentik OAuth2 provider** via `mcp__authentik__authentik_write``POST /api/v3/providers/oauth2/`:
```json
{
"name": "homelab-<slug>",
"client_type": "confidential",
"redirect_uris": [{"matching_mode": "strict", "url": "https://<slug>.ginnoir.com/auth/callback"}],
"authorization_flow": "b88daebc-c54d-4e85-ae27-8b47384d5d74",
"invalidation_flow": "3259f9c5-7eab-40bf-81cb-2b0ede6eb7b3",
"property_mappings": ["718abef2-...", "19491dd8-...", "ca45a8da-..."],
"sub_mode": "hashed_user_id"
}
```
Note the returned `pk`, `client_id`, and `client_secret`.
2. **Create an Authentik application** → `POST /api/v3/core/applications/`:
```json
{"name": "<Service Name>", "slug": "<slug>", "provider": <pk>}
```
3. **Configure the service** with:
- Discovery URL: `https://auth.ginnoir.com/application/o/<slug>/`
- Client ID / Client secret from step 1
- No Caddy forward_auth needed — the app handles login itself
**Reusable Authentik IDs** (do not re-derive these):
| Item | Value |
|---|---|
| Implicit consent flow | `b88daebc-c54d-4e85-ae27-8b47384d5d74` |
| Invalidation flow | `3259f9c5-7eab-40bf-81cb-2b0ede6eb7b3` |
| Scope — openid | `718abef2-8e8e-4133-afe7-a0eec7b37aba` |
| Scope — email | `19491dd8-da08-469a-8e5e-3be8ed1cbe5d` |
| Scope — profile | `ca45a8da-a38f-4ffe-b7a3-f0c0bac2df92` |
To get the exact scope PKs if needed: `GET /api/v3/propertymappings/scope/?search=openid`.
### Pattern B — Caddy forward_auth (fallback)
If the service has no native auth (sonarr, qbittorrent, etc.):
```caddy
service.ginnoir.com {
import internal_only
route {
import authentik_outpost
handle /api/* { reverse_proxy service:port } # skip auth for API keys
handle {
import authentik_forward_auth
reverse_proxy service:port
}
}
}
```
## Caddyfile
- TLS uses the Cloudflare DNS-01 challenge (`acme_dns cloudflare {env.CF_API_TOKEN}`), so certs need no inbound ports.