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:
co-authored by
Claude Sonnet 4.6
parent
acdf61500c
commit
a937de7bda
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user