diff --git a/CLAUDE.md b/CLAUDE.md index 6c469dd..9444dca 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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-", + "client_type": "confidential", + "redirect_uris": [{"matching_mode": "strict", "url": "https://.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": "", "slug": "", "provider": } + ``` + +3. **Configure the service** with: + - Discovery URL: `https://auth.ginnoir.com/application/o//` + - 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.