Two Forgejo runbooks plus the Authentik OAuth2 provider guide, mirrored from the iCloud folder into the versioned repo. Runbooks: - forgejo-admin-recovery.md — fallback login when Authentik is down using the local admin-local user (prohibit_login reset via SQL). - forgejo-backup-restore.md — backup format, restore scenarios (full / DB-only / single file), disaster recovery on new host. Guides: - authentik-oauth2-provider.md — reusable template for adding native OIDC integrations in Authentik. First applied for Forgejo, ready to reuse for OpenProject, Nextcloud, Grafana. Includes the important launch-URL gotcha from ADR-0006. Each category folder has a README.md with format conventions and an index of the current documents. Refs OP#1118
224 lines
8.6 KiB
Markdown
224 lines
8.6 KiB
Markdown
# How-To: OAuth2/OIDC Provider in Authentik einrichten
|
|
|
|
**Stand:** 2026-04-11
|
|
**Erstanwendung:** M7.1 — Forgejo OIDC Setup
|
|
**Zielgruppe:** AI-Agents und Benjamin — wiederverwendbares Template für künftige OIDC-Integrationen
|
|
|
|
## Wann dieses Pattern nutzen
|
|
Wenn eine Applikation OIDC (OpenID Connect) als First-Class-Client unterstützt (z.B. Forgejo, OpenProject, Nextcloud, GitLab, Grafana, Mattermost). In diesem Fall ist nativer OIDC besser als ForwardAuth weil:
|
|
- User-Identität (Name, Email, Avatar, Gruppen) kommt direkt in der App an
|
|
- Auto-Registration / Account-Linking möglich
|
|
- API-Tokens der App bleiben unabhängig
|
|
- Migration zu anderer IdP-Software trivial
|
|
|
|
Für Apps **ohne** OIDC-Support (oder zu komplex zu integrieren): ForwardAuth via Embedded Outpost — siehe das LocoSoft/n8n Pattern.
|
|
|
|
## Voraussetzungen
|
|
- Authentik läuft und ist über `welcome.sdda.eu` erreichbar
|
|
- SSH-Zugang `authentik-sso` verfügbar
|
|
- MCP Tools `authentik_*` verfügbar ODER API-Token für akadmin
|
|
- Die App (z.B. Forgejo) ist bereits live oder zumindest geplant — du brauchst die **Redirect URI** (meist `https://<app-domain>/user/oauth2/<name>/callback` oder ähnlich)
|
|
|
|
## Schritt 1: Gruppe für Zugangskontrolle (optional aber empfohlen)
|
|
|
|
Statt "jeder in Authentik" → eigene Gruppe pro App, damit User-Anlage und -Abschaltung granular funktioniert.
|
|
|
|
```python
|
|
# Via MCP Tool
|
|
mcp.authentik_create_group(name="<app-slug>-users")
|
|
```
|
|
|
|
Oder via REST API:
|
|
```bash
|
|
TOKEN=<akadmin-api-token>
|
|
curl -X POST 'http://localhost:9000/api/v3/core/groups/' \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"name": "forgejo-users", "is_superuser": false}'
|
|
```
|
|
|
|
Notiere die zurückgegebene `pk` (UUID).
|
|
|
|
## Schritt 2: OAuth2/OpenID Provider anlegen
|
|
|
|
```bash
|
|
# Vorbereitung: Flow-IDs und Scope-Mapping-IDs holen
|
|
TOKEN=<akadmin-api-token>
|
|
|
|
# Authorization Flow (Default: explicit consent)
|
|
AUTH_FLOW="9f7af462-90cf-44d8-99fa-916f8459ba13"
|
|
|
|
# Invalidation Flow (für logout)
|
|
INVAL_FLOW="697e1b4c-db65-4c21-a458-dd7df2bf51f2"
|
|
|
|
# Scope Property Mappings (diese sind bei uns fest)
|
|
SCOPE_OPENID="f878f335-46dd-4104-8dfb-fe72f21dda22"
|
|
SCOPE_PROFILE="04294271-539c-44e6-824a-5ab5c1e4613d"
|
|
SCOPE_EMAIL="602c1e0a-0c6d-48ef-bada-97d4c6e45f3c"
|
|
|
|
# Signing Certificate (RS256 statt HMAC)
|
|
CERT="663c97fa-f336-49fe-8809-118d3745547a" # authentik Self-signed
|
|
|
|
# Eigenes Client Secret generieren
|
|
CLIENT_SECRET=$(openssl rand -hex 48)
|
|
|
|
# Provider anlegen
|
|
curl -X POST 'http://localhost:9000/api/v3/providers/oauth2/' \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H 'Content-Type: application/json' \
|
|
-d "{
|
|
\"name\": \"<App-Name> OIDC\",
|
|
\"authorization_flow\": \"$AUTH_FLOW\",
|
|
\"invalidation_flow\": \"$INVAL_FLOW\",
|
|
\"client_type\": \"confidential\",
|
|
\"client_secret\": \"$CLIENT_SECRET\",
|
|
\"access_code_validity\": \"minutes=1\",
|
|
\"access_token_validity\": \"hours=24\",
|
|
\"refresh_token_validity\": \"days=30\",
|
|
\"include_claims_in_id_token\": true,
|
|
\"signing_key\": \"$CERT\",
|
|
\"sub_mode\": \"user_email\",
|
|
\"issuer_mode\": \"per_provider\",
|
|
\"redirect_uris\": [
|
|
{\"matching_mode\": \"strict\", \"url\": \"<REDIRECT_URI>\"}
|
|
],
|
|
\"property_mappings\": [
|
|
\"$SCOPE_OPENID\",
|
|
\"$SCOPE_PROFILE\",
|
|
\"$SCOPE_EMAIL\"
|
|
]
|
|
}"
|
|
```
|
|
|
|
Die Response enthält:
|
|
- `pk` — die ID des Providers (numerisch)
|
|
- `client_id` — auto-generiert, brauchst du für die App-Config
|
|
- `client_secret` — das das du mitgegeben hast (bestätigt)
|
|
|
|
**Notiere client_id und client_secret sofort.** Das Secret wird in der Web-UI später maskiert.
|
|
|
|
## Schritt 3: Application anlegen
|
|
|
|
Die Application verbindet Provider + Namen + Zugangs-Policies.
|
|
|
|
```bash
|
|
curl -X POST 'http://localhost:9000/api/v3/core/applications/' \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{
|
|
"name": "<App-Name>",
|
|
"slug": "<app-slug>",
|
|
"provider": <PROVIDER_PK>,
|
|
"meta_launch_url": "https://<app-domain>/user/oauth2/<auth-name>",
|
|
"meta_description": "<Beschreibung>",
|
|
"meta_publisher": "Richter & Zech",
|
|
"policy_engine_mode": "any"
|
|
}'
|
|
```
|
|
|
|
Die Response enthält die App-ID (UUID).
|
|
|
|
**Wichtig:** Der `slug` bestimmt die Discovery-URL: `https://welcome.sdda.eu/application/o/<slug>/.well-known/openid-configuration`
|
|
|
|
**KRITISCH — Silent SSO:** `meta_launch_url` muss auf die **OAuth2-Initiate-Route** der App zeigen, nicht auf die App-Startseite. Nur so wird beim Klick im Authentik-Dashboard der OIDC-Flow automatisch getriggert ohne dass der User nochmal "Sign in with..." klicken muss. Beispiele:
|
|
- **Forgejo:** `https://code.sdda.eu/user/oauth2/authentik` (der `authentik` Teil ist der Auth-Source-Name aus `forgejo admin auth add-oauth --name`)
|
|
- **OpenProject:** `https://openproject.sdda.eu/auth/oidc_authentik/callback`
|
|
- **Grafana:** `https://grafana.sdda.eu/login/generic_oauth`
|
|
- **Nextcloud:** `https://app.sdda.eu/apps/user_oidc/login/<provider_id>`
|
|
|
|
Wenn man die nackte App-URL setzt (`https://code.sdda.eu/`), zeigt die App ihre eigene Login-Seite an, und der User muss nochmal klicken. Das ist keine silent SSO — das ist eine Frustration.
|
|
|
|
## Schritt 4: Policy Binding (Gruppen-Zugangskontrolle)
|
|
|
|
Wenn du in Schritt 1 eine Gruppe angelegt hast:
|
|
|
|
```bash
|
|
APP_PK=<uuid-aus-schritt-3>
|
|
GROUP_PK=<uuid-aus-schritt-1>
|
|
|
|
curl -X POST 'http://localhost:9000/api/v3/policies/bindings/' \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H 'Content-Type: application/json' \
|
|
-d "{
|
|
\"target\": \"$APP_PK\",
|
|
\"group\": \"$GROUP_PK\",
|
|
\"enabled\": true,
|
|
\"order\": 0,
|
|
\"timeout\": 30
|
|
}"
|
|
```
|
|
|
|
Damit können nur Mitglieder der Gruppe die App nutzen. User die nicht drin sind, bekommen beim OIDC-Login "Access Denied".
|
|
|
|
## Schritt 5: User zur Gruppe hinzufügen
|
|
|
|
```python
|
|
# Via MCP
|
|
mcp.authentik_add_user_to_group(group_id=<UUID>, user_id=<INT>)
|
|
```
|
|
|
|
Oder REST:
|
|
```bash
|
|
curl -X POST "http://localhost:9000/api/v3/core/groups/$GROUP_PK/add_user/" \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"pk": <USER_ID>}'
|
|
```
|
|
|
|
## Schritt 6: Discovery-URL verifizieren
|
|
|
|
Bevor du in der App den Provider konfigurierst, check dass Authentik die Discovery-URL korrekt ausliefert:
|
|
|
|
```bash
|
|
curl -s 'https://welcome.sdda.eu/application/o/<slug>/.well-known/openid-configuration' | jq .
|
|
```
|
|
|
|
Erwartete Felder:
|
|
- `issuer` — muss mit dem Issuer matchen den die App erwartet
|
|
- `authorization_endpoint`
|
|
- `token_endpoint`
|
|
- `userinfo_endpoint`
|
|
- `jwks_uri`
|
|
|
|
## Schritt 7: Client in der App konfigurieren
|
|
|
|
Je nach App unterschiedlich. Bei **Forgejo** z.B. via CLI:
|
|
```bash
|
|
docker exec -u git forgejo forgejo admin auth add-oauth \
|
|
--name authentik \
|
|
--provider openidConnect \
|
|
--key "<CLIENT_ID>" \
|
|
--secret "<CLIENT_SECRET>" \
|
|
--auto-discover-url "https://welcome.sdda.eu/application/o/<slug>/.well-known/openid-configuration" \
|
|
--scopes "openid profile email" \
|
|
--skip-local-2fa
|
|
```
|
|
|
|
Bei **OpenProject**: `config/settings.yml` oder ENV. Bei **Nextcloud**: App "user_oidc" installieren, dann UI. Bei **Grafana**: `grafana.ini` [auth.generic_oauth]. Immer die App-Docs konsultieren.
|
|
|
|
## Schritt 8: Test-Login
|
|
|
|
1. Browser: App-URL → Login-Button "Sign in with authentik"
|
|
2. Redirect zu welcome.sdda.eu → Login
|
|
3. Evtl. Consent-Screen bestätigen
|
|
4. Redirect zurück → User ist eingeloggt
|
|
|
|
## Gotchas
|
|
- **Issuer-URL muss public sein** (nicht `http://10.0.0.7:9000`). JWT-Signatur enthält den Issuer — wenn App und Authentik unterschiedliche Issuer sehen, schlägt die Validation fehl.
|
|
- **Subject-Mode `user_email`** funktioniert gut wenn alle User einzigartige Email-Adressen haben. Bei gemeinsamen Mailboxen (info@ etc.) lieber `hashed_user_id`.
|
|
- **Include Claims in id_token = true** ist wichtig für Apps die `email`/`name` aus dem id_token lesen statt separaten userinfo-Call.
|
|
- **Strict Redirect URIs:** Immer `"matching_mode": "strict"`, niemals Regex — Security.
|
|
- **Policy Timeout = 30s** gibt Authentik Luft bei langsamen LDAP/SCIM-Backends.
|
|
|
|
## Template-Reference
|
|
Als konkretes funktionierendes Beispiel für alle oben genannten Schritte siehe:
|
|
- `../Forgejo/adr/0003-native-oidc-not-forwardauth.md`
|
|
- Die Forgejo-`.env` auf ai-apps enthält die konkreten Werte
|
|
- Die Authentik-Admin-UI unter `welcome.sdda.eu/if/admin/#/core/applications` zeigt die "Forgejo" Application als lebendes Beispiel
|
|
|
|
## Nächste Apps die dieses Template nutzen könnten
|
|
- **OpenProject** — hat nativen OIDC-Client, aktuell noch ForwardAuth nicht genutzt
|
|
- **Nextcloud** — braucht `user_oidc` App-Installation
|
|
- **Grafana** — falls wir Monitoring einführen
|
|
- **Mattermost** — falls wir Chat einführen
|
|
- Potenzielle Migration von n8n und locosoft-hilfe-system von ForwardAuth auf OIDC (großer Eingriff, nicht dringlich)
|