Authentik Teil 2: Apps anbinden – Forward-Auth & OIDC
Dienste an Authentik anbinden: Forward-Auth mit Traefik für Apps ohne Login und OpenID Connect für alles mit eigener SSO-Unterstützung – jeder Schritt geprüft.
Inhaltsverzeichnis
Authentik läuft – jetzt kommt der Teil, der den Aufwand rechtfertigt: die Dienste dahinter. Es gibt genau zwei Wege, und die Wahl hängt allein daran, ob die Anwendung selbst SSO versteht.
Was bauen wir?
Wir binden zwei Dienste an, jeweils auf dem passenden Weg:
- Forward-Auth für eine Anwendung ohne eigene Anmeldung. Traefik fragt vor jeder Anfrage bei Authentik nach, ob der Besucher angemeldet ist. Ist er es nicht, landet er auf der Authentik-Anmeldeseite; ist er es, bekommt die Anwendung seine Identität als HTTP-Header.
- OpenID Connect (OIDC) für Anwendungen mit SSO-Unterstützung – Grafana, Nextcloud, Forgejo, Immich und viele andere. Dort trägst du Client-ID, Secret und die Endpunkte ein, den Rest macht die App selbst.
Alles unten wurde mit Authentik 2026.8 hinter Traefik v3.7 auf Debian 13 durchgespielt: Am Ende zeigt der Testdienst die durchgereichten Identitäts-Header, und das OIDC-Discovery-Dokument liefert echte Endpunkte.
Welcher Weg für welchen Dienst?
Die Entscheidung ist fast immer eindeutig:
| Dienst | Unterstützt OIDC? | Weg |
|---|---|---|
| Nextcloud, Grafana, Forgejo, Immich, Paperless | ja | OIDC – echte Konten, Rollen aus Gruppen |
| Uptime Kuma, Dockge, Netdata, ein statisches Dashboard | nein | Forward-Auth |
Metrik-Endpunkte, /actuator, interne APIs | irrelevant | Forward-Auth oder einfacher: nicht öffentlich (VPN) |
| Dienste mit Mobil-App (Immich, Nextcloud) | ja | nur OIDC – Forward-Auth bricht die Apps (siehe unten) |
Kurz: OIDC, wo möglich; Forward-Auth, wo nötig. OIDC gibt der Anwendung ein echtes Benutzerkonto inklusive Rollen; Forward-Auth stellt nur eine Tür davor und schiebt einen Namen im Header durch.
Voraussetzungen
- Ein laufendes Authentik aus Teil 1, erreichbar unter
https://DEINE_DOMAIN - Ein Traefik v3 mit
proxy-Netz (Anleitung) – wir arbeiten hier viel mit Middlewares, die Grundlagen dazu stehen in Traefik härten - Ein Dienst zum Üben. Wir nehmen
traefik/whoami, weil er alle empfangenen Header ausgibt – damit ist sofort sichtbar, ob die Anbindung wirklich greift
Ein oder zwei Domains?
https://DEINE_DOMAIN/, der geschützte
Dienst unter https://DEINE_DOMAIN/whoami. Das ist die sparsame Variante und genau so getestet.
Üblicher ist die Trennung (auth.DEINE_DOMAIN für Authentik, app.DEINE_DOMAIN für den Dienst) – die
Schritte sind identisch, nur external_host und die Traefik-Regeln tragen dann den anderen Hostnamen.Schritt für Schritt
1. Zugang für die Automatisierung: ein API-Token
Provider und Anwendungen lassen sich vollständig über die API anlegen – praktisch, weil du damit reproduzierbar arbeitest statt zu klicken. Dafür braucht Authentik einen Token.
Weil du in Teil 1 den Einrichtungsassistenten durchlaufen hast, existiert akadmin bereits. Den
Token legst du deshalb in der Oberfläche an: Verzeichnis → Tokens und App-Passwörter → Token
erstellen. Als Bezeichner nimm etwas Sprechendes (api-setup), als Absicht API, als Benutzer
akadmin. Nach dem Speichern kopierst du den Schlüssel über das Kopiersymbol – er wird nur einmal
angezeigt.
Bei einer Neuinstallation geht es auch automatisch
Setzt du Authentik neu auf, kannst du Konto und Token beim allerersten Start erzeugen lassen –
ohne Assistenten. Die Variablen gehören an den worker-Dienst (nicht an server) und werden
nur beim ersten Start gelesen; bei einer bestehenden Installation bleiben sie wirkungslos.
Statt des Klartext-Passworts empfiehlt Authentik inzwischen einen Hash. Das Passwort nimmt
hash_password nicht als Argument – hash_password DEIN_PASSWORT bricht mit
unrecognized arguments ab –, sondern über eine Eingabeaufforderung oder die
Standardeingabe:
docker compose run --rm server hash_passwordAm Ende steht eine Zeile, die mit pbkdf2_sha256$ beginnt. Sie kommt als
AK_BOOTSTRAP_PASSWORD_HASH in die .env, der Token als AK_BOOTSTRAP_TOKEN (zum Beispiel aus
openssl rand -hex 32). In der compose.yaml sieht der worker dann so aus:
worker:
environment:
AUTHENTIK_BOOTSTRAP_PASSWORD_HASH: ${AK_BOOTSTRAP_PASSWORD_HASH}
AUTHENTIK_BOOTSTRAP_TOKEN: ${AK_BOOTSTRAP_TOKEN}
AUTHENTIK_BOOTSTRAP_EMAIL: du@DEINE_DOMAINDer Hash enthält $-Zeichen: in der .env in einfache Anführungszeichen setzen, direkt in der
compose.yaml jedes $ als $$ schreiben.
Prüfe den Token – egal auf welchem Weg er entstanden ist:
curl -s -H "Authorization: Bearer DEIN_LANGER_ZUFALLS_TOKEN" \
https://DEINE_DOMAIN/api/v3/core/users/me/{"user":{"username":"akadmin", … "is_superuser":true, …}}Achtung
.env
mit chmod 600, nicht in ein Skript im Repository – und wenn du ihn nur zum Einrichten brauchst,
lösch ihn danach unter Verzeichnis → Tokens.2. Weg A: Forward-Auth für Dienste ohne Login
Der Proxy-Provider ist Authentiks Antwort auf „diese App hat gar keine Benutzerverwaltung". Er kommt
in mehreren Betriebsarten; wir nehmen forward_single: eine Anwendung, ein Provider.
Zuerst die IDs der beiden Standard-Flows (aus Teil 1 bekannt):
API=https://DEINE_DOMAIN/api/v3
TOK="Authorization: Bearer DEIN_LANGER_ZUFALLS_TOKEN"
AUTHZ=$(curl -s -H "$TOK" "$API/flows/instances/?slug=default-provider-authorization-implicit-consent" \
| python3 -c "import json,sys; print(json.load(sys.stdin)['results'][0]['pk'])")
INVAL=$(curl -s -H "$TOK" "$API/flows/instances/?slug=default-provider-invalidation-flow" \
| python3 -c "import json,sys; print(json.load(sys.stdin)['results'][0]['pk'])")Dann den Provider:
curl -s -X POST -H "$TOK" -H "content-type: application/json" "$API/providers/proxy/" -d "{
\"name\": \"whoami-forwardauth\",
\"authorization_flow\": \"$AUTHZ\",
\"invalidation_flow\": \"$INVAL\",
\"mode\": \"forward_single\",
\"external_host\": \"https://DEINE_DOMAIN\"
}"Und die Anwendung, die ihn benutzt – sie erzeugt auch die Kachel in der Benutzer-Bibliothek:
curl -s -X POST -H "$TOK" -H "content-type: application/json" "$API/core/applications/" -d '{
"name": "whoami (geschützt)", "slug": "whoami", "provider": 1
}'Die 1 ist die pk aus der Antwort des Provider-Aufrufs. Danach stehen beide in der Übersicht:

3. Den Outpost scharf schalten – und die häufigste Falle
Ein Proxy-Provider arbeitet nicht allein: Ein Outpost liefert ihn aus. Authentik bringt einen
eingebetteten Outpost mit, der im server-Container läuft – du musst ihm den Provider nur zuweisen:
OUT=$(curl -s -H "$TOK" "$API/outposts/instances/" \
| python3 -c "import json,sys; print(json.load(sys.stdin)['results'][0]['pk'])")
curl -s -X PATCH -H "$TOK" -H "content-type: application/json" \
"$API/outposts/instances/$OUT/" -d '{"providers": [1]}'Ohne authentik_host landen deine Nutzer auf localhost
Der eingebettete Outpost kennt seine öffentliche Adresse nicht von selbst. Im Test war das Feld
authentik_host leer, und die Weiterleitung eines nicht angemeldeten Besuchers ging auf
http://localhost/application/o/authorize/… – für jeden Nutzer eine tote Seite. Einmal setzen, dann
stimmt sie:
curl -s -X PATCH -H "$TOK" -H "content-type: application/json" \
"$API/outposts/instances/$OUT/" \
-d '{"config": {"authentik_host": "https://DEINE_DOMAIN/"}}'Danach zeigt die Weiterleitung auf https://DEINE_DOMAIN/application/o/authorize/… – vorher und
nachher nachgemessen. In der Oberfläche liegt dasselbe Feld unter
Anwendungen → Outposts → Bearbeiten.
4. Traefik: die Forward-Auth-Middleware
Jetzt fehlt die Verbindung. Traefik fragt vor jeder Anfrage beim Outpost nach – das macht die
Middleware forwardauth. Am geschützten Dienst:
whoami:
image: traefik/whoami:v1.12
restart: unless-stopped
networks: [proxy]
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`DEINE_DOMAIN`) && PathPrefix(`/whoami`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=le"
- "traefik.http.routers.whoami.middlewares=ak-auth"
- "traefik.http.services.whoami.loadbalancer.server.port=80"
- "traefik.http.middlewares.ak-auth.forwardauth.address=http://server:9000/outpost.goauthentik.io/auth/traefik"
- "traefik.http.middlewares.ak-auth.forwardauth.trustForwardHeader=true"
- "traefik.http.middlewares.ak-auth.forwardauth.authResponseHeaders=X-authentik-username,X-authentik-groups,X-authentik-email,X-authentik-name,X-authentik-uid"Die drei entscheidenden Zeilen:
addresszeigt auf den Outpost – hier über den Container-Namenserverim gemeinsamen Docker-Netz, also ohne Umweg über das Internet. Der Pfad/outpost.goauthentik.io/auth/traefikist der Traefik-spezifische Prüf-Endpunkt.trustForwardHeader=truegibt dieX-Forwarded-*-Header weiter, damit Authentik die ursprünglich aufgerufene Adresse kennt und korrekt zurückleiten kann.authResponseHeadersist der eigentliche Nutzen: Diese Header kopiert Traefik aus der Antwort von Authentik in die Anfrage an deine Anwendung. Fehlt die Zeile, ist der Dienst geschützt, weiß aber nicht, wer da ist.
5. Prüfen, dass es wirklich schützt
Erst ohne Anmeldung – dafür reicht curl:
curl -s -o /dev/null -w "%{http_code} → %{redirect_url}\n" https://DEINE_DOMAIN/whoami302 → https://DEINE_DOMAIN/application/o/authorize/?client_id=…&redirect_uri=…%2Foutpost.goauthentik.io%2Fcallback…Ein 302 auf die Authentik-Anmeldung ist das gewünschte Ergebnis. Im Browser erscheint die
Anmeldeseite vor dem Dienst:

Nach der Anmeldung landest du wieder auf /whoami – und der Dienst zeigt, was bei ihm ankommt:
GET /whoami HTTP/1.1
Host: DEINE_DOMAIN
X-Authentik-Email: du@DEINE_DOMAIN
X-Authentik-Groups: authentik Admins
X-Authentik-Name: authentik Default Admin
X-Authentik-Uid: d216e191a6f972c39931a005a7998a68…
X-Authentik-Username: akadmin
X-Forwarded-Proto: httpsDas ist der ganze Trick: Die Anwendung hat keine Zeile Anmeldecode – und weiß trotzdem, wer vor ihr
sitzt. Dienste, die Header-Authentifizierung unterstützen (Grafana mit auth.proxy, Paperless,
Forgejo mit Reverse-Proxy-Auth), können daraus direkt eine Sitzung machen.
Header sind nur so sicher wie ihr Weg
X-Authentik-Username vertraut, darf dieser Header niemals von außen kommen
können. Deshalb: Der Dienst ist ausschließlich über Traefik erreichbar (kein veröffentlichter Port),
und die Middleware hängt an jedem Router, der zu ihm führt. Ein zweiter Router ohne Middleware ist
eine offene Tür.6. Die Grenzen von Forward-Auth kennen
Bevor du alles hinter Forward-Auth hängst: Der Mechanismus setzt einen Browser voraus. Eine nicht
angemeldete Anfrage bekommt eine 302-Weiterleitung auf ein Anmeldeformular – und genau daran
scheitert alles, was kein Browser ist:
- Mobil-Apps (Nextcloud, Immich, Audiobookshelf) können mit der Weiterleitung nichts anfangen. Sie sehen HTML statt JSON und melden „Server nicht erreichbar". Für solche Dienste ist OIDC der einzige sinnvolle Weg – oder du nimmst die App-Pfade gezielt aus.
- API-Zugriffe und Skripte (
curl, Backup-Jobs, Webhooks) laufen ins Leere. Wo ein Dienst per Token angesprochen wird, muss dieser Pfad an der Middleware vorbei – oder du verlagerst den Schutz auf eine IP-Allowlist. - WebDAV und Sync-Clients verhalten sich wie APIs, mit demselben Ergebnis.
Für solche Ausnahmen legst du einen zweiten Router ohne die Auth-Middleware an, der nur den nötigen Pfad bedient – und schützt ihn anders, etwa mit dem Token der Anwendung selbst. Beispiel für einen API-Pfad:
- "traefik.http.routers.app-api.rule=Host(`DEINE_DOMAIN`) && PathPrefix(`/api`)"
- "traefik.http.routers.app-api.entrypoints=websecure"
- "traefik.http.routers.app-api.tls.certresolver=le"
- "traefik.http.routers.app-api.middlewares=rate@file"Achtung
/api, nicht /), und schreib dir auf, warum er existiert – in einem halben Jahr weiß das
sonst niemand mehr.Zur Vollständigkeit die dritte Betriebsart des Proxy-Providers: forward_domain. Damit bedient
ein Outpost alle Dienste einer Domain (*.DEINE_DOMAIN), und Nutzer melden sich einmal für alle an.
Das ist der bequemere Aufbau ab etwa drei Diensten, verlangt aber, dass Authentik und alle Anwendungen
unter derselben übergeordneten Domain liegen, damit das Sitzungs-Cookie geteilt werden kann. Hier
getestet ist forward_single – die Umstellung betrifft nur den Modus im Provider und die
Cookie-Domain.
7. Weg B: OpenID Connect für Dienste mit eigenem SSO
Hat die Anwendung eine OIDC-Anmeldung, ist das der bessere Weg: Sie führt dann echte Benutzerkonten inklusive Rollen, statt nur einen Namen im Header zu sehen.
Provider anlegen – wichtig ist die Redirect-URI, die genau der Rückadresse der Anwendung entsprechen muss (Beispiel Grafana):
curl -s -X POST -H "$TOK" -H "content-type: application/json" "$API/providers/oauth2/" -d "{
\"name\": \"grafana-oidc\",
\"authorization_flow\": \"$AUTHZ\",
\"invalidation_flow\": \"$INVAL\",
\"client_type\": \"confidential\",
\"redirect_uris\": [{\"matching_mode\": \"strict\", \"url\": \"https://GRAFANA_DOMAIN/login/generic_oauth\"}]
}"Die Antwort enthält client_id und client_secret – beide brauchst du in der Anwendung. Danach wie
oben eine Anwendung anlegen, die auf diesen Provider zeigt.
Ob alles sitzt, verrät das Discovery-Dokument. Authentik veröffentlicht es pro Anwendung unter deren Slug:
curl -s https://DEINE_DOMAIN/application/o/grafana/.well-known/openid-configuration \
| python3 -m json.tool | head -12"issuer": "https://DEINE_DOMAIN/application/o/grafana/",
"authorization_endpoint": "https://DEINE_DOMAIN/application/o/authorize/",
"token_endpoint": "https://DEINE_DOMAIN/application/o/token/",
"userinfo_endpoint": "https://DEINE_DOMAIN/application/o/userinfo/",Beachte: Der Issuer ist anwendungsspezifisch (er enthält den Slug), die übrigen Endpunkte sind für alle Anwendungen gleich. Genau diese Adressen – oder die Discovery-URL selbst, wenn die App sie unterstützt – trägst du in der Anwendung ein. Für Grafana etwa:
GF_AUTH_GENERIC_OAUTH_ENABLED=true
GF_AUTH_GENERIC_OAUTH_NAME=Authentik
GF_AUTH_GENERIC_OAUTH_CLIENT_ID=DEINE_CLIENT_ID
GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET=DEIN_CLIENT_SECRET
GF_AUTH_GENERIC_OAUTH_SCOPES=openid profile email
GF_AUTH_GENERIC_OAUTH_AUTH_URL=https://DEINE_DOMAIN/application/o/authorize/
GF_AUTH_GENERIC_OAUTH_TOKEN_URL=https://DEINE_DOMAIN/application/o/token/
GF_AUTH_GENERIC_OAUTH_API_URL=https://DEINE_DOMAIN/application/o/userinfo/Gruppen als Rollen
role_attribute_path. Damit steuerst du Admin-Rechte zentral über Authentik-Gruppen, statt sie in
jeder App zu pflegen. Genau deshalb der Rat aus Teil 1: Berechtigungen an Gruppen hängen, nicht an
einzelne Benutzer.8. Wer darf was? Zugriff einschränken
Solange an einer Anwendung keine Richtlinie hängt, darf sie jeder angemeldete Benutzer nutzen. Für den Regelfall bindest du eine Gruppe an die Anwendung: Anwendungen → deine Anwendung → Richtlinien / Gruppen / Benutzer-Bindungen, dort die Gruppe auswählen.
Erst danach verschwindet die Kachel in der Benutzer-Bibliothek für alle anderen – und der Zugriff ist gesperrt. Prüfen lässt sich das am ehrlichsten mit einem zweiten Testkonto, das nicht in der Gruppe ist: Es soll die Anwendung weder sehen noch aufrufen können.
Wenn es nicht funktioniert
Das Skript bricht beim Ermitteln der Flows mit IndexError: list index out of range ab. Das
passiert nur direkt nach einer frischen Installation: /-/health/ready/ meldet schon 200, während
der worker die mitgelieferten Abläufe noch einspielt. Im Test gab es in diesem Moment erst einen
Flow, 20 bis 30 Sekunden später alle 15. Kurz warten und neu starten.
Der Aufruf leitet auf http://localhost/application/o/authorize/… weiter. Der eingebettete
Outpost kennt seine öffentliche Adresse nicht: Das Feld authentik_host ist leer. Setz es auf
https://DEINE_DOMAIN/ (Schritt 3) – vorher landet jeder Nutzer auf einer toten Seite.
Traefik antwortet mit 500 oder es kommt gar keine Weiterleitung. Dann erreicht die Middleware den
Outpost nicht. Die address muss aus Traefiks Sicht auflösbar sein – also der Container-Name im
gemeinsamen Netz (http://server:9000/outpost.goauthentik.io/auth/traefik), nicht localhost. Prüfe
im Traefik-Ordner mit docker compose exec traefik wget -qO- http://server:9000/-/health/live/, ob
die Verbindung steht.
Der Dienst ist geschützt, kennt aber den Benutzer nicht. Es fehlt authResponseHeaders an der
Middleware. Ohne diese Liste verwirft Traefik die Identitäts-Header aus Authentiks Antwort – die
Anmeldung funktioniert, die Anwendung sieht aber nichts.
Nach der Anmeldung landet man in einer Weiterleitungsschleife. Meist passt external_host nicht
zur tatsächlich aufgerufenen Adresse (http statt https, mit oder ohne Subdomain). Er muss exakt die
URL sein, unter der Nutzer den Dienst aufrufen.
Die OIDC-Anmeldung bricht mit „redirect_uri mismatch" ab. Authentik prüft die Rückadresse strikt.
Trag im Provider genau die URI ein, die die Anwendung schickt – bei Grafana etwa
https://GRAFANA_DOMAIN/login/generic_oauth. Ein fehlender Schrägstrich reicht zum Scheitern.
Alle angemeldeten Nutzer kommen überall rein. Ohne Bindung an eine Gruppe ist eine Anwendung für jeden angemeldeten Benutzer offen. Häng eine Gruppen-Bindung an die Anwendung (Schritt 8) und prüfe mit einem Konto, das nicht in der Gruppe ist.
Dein Problem ist nicht dabei? Alle Fehlermeldungen durchsuchen →
Wartung & Backups
Nach jedem Authentik-Update stichprobenartig prüfen. Ein Update kann Flows und Provider
verändern; die Anbindungen selbst sind robust, aber der schnelle Test lohnt: curl auf den
geschützten Pfad muss weiterhin 302 liefern, und das Discovery-Dokument muss deine Endpunkte
zeigen. Zwei Kommandos, die verhindern, dass eine Anmeldung wochenlang unbemerkt kaputt ist.
Client-Secrets gehören in den Passwortmanager. Sie stehen in der Anwendung (Umgebungsvariablen) und in Authentik. Wird eines neu erzeugt, muss die andere Seite mitgezogen werden – notiere beim Anlegen, welche App welches Secret nutzt, sonst suchst du später in beiden Systemen.
Die Datenbank ist jetzt noch wichtiger. Provider, Anwendungen und Gruppen-Bindungen liegen alle in
PostgreSQL. Der pg_dump aus Teil 1 ist damit kein
Nice-to-have mehr: Ohne ihn baust du nach einem Ausfall jede Anbindung neu. Mit
Restic täglich wegsichern.
Den Notausgang offen halten. Jeder Dienst hinter Forward-Auth ist nur so erreichbar wie Authentik.
Behalte in wichtigen Anwendungen ein lokales Konto – und häng keine Werkzeuge hinter SSO, die du
zur Reparatur brauchst; das gilt besonders für Dockge und
ähnliche Verwaltungsoberflächen. Ein Monitoring auf
/-/health/ready/ merkt den Ausfall vor deinen Nutzern.
Tokens aufräumen. Der Bootstrap-Token aus Schritt 1 ist ein Admin-Zugang ohne zweiten Faktor. Wenn die Einrichtung steht, lösch ihn unter Verzeichnis → Tokens – und leg für spätere Automatisierung einen eigenen Token mit begrenzten Rechten an.
Feedback per E-Mail: feedback@serverkueche.de
Das könnte dir auch schmecken


