# 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:///user/oauth2//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="-users") ``` Oder via REST API: ```bash 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= # 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\": \" 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\": \"\"} ], \"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": "", "slug": "", "provider": , "meta_launch_url": "https:///user/oauth2/", "meta_description": "", "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//.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/` 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= GROUP_PK= 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=, user_id=) ``` 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": }' ``` ## 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//.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 "" \ --secret "" \ --auto-discover-url "https://welcome.sdda.eu/application/o//.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)