Im Betrieb aufgetreten: Die Anwendung lief hinter einem Reverse Proxy unter einer Domain, in der .env stand aber noch die interne Adresse aus der Einrichtung. Der Anmeldeknopf setzt den Cookie mit dem laufenden Vorgang auf dem Host, ueber den der Browser da ist — die Rueckkehradresse fuer den Anbieter kommt dagegen aus BASE_URL. Der Anbieter schickte den Browser also auf den anderen Host, der Cookie blieb zurueck, und am Ende stand „Der Anmeldevorgang ist abgelaufen". Eine Meldung, die die Ursache gut versteckt. Jetzt prueft der Start beide Adressen und bricht sofort mit einer Erklaerung ab. Auf dem Bildschirm steht nur die aufgerufene Adresse — die interne gehoert ins Protokoll, nicht auf eine Seite, die jeder sieht. Aus demselben Grund faellt der Hinweis „Die Anmeldung mit Passwort ist abgeschaltet (OIDC_ONLY=true)" weg. Wie die Anmeldung eingerichtet ist, geht Fremde nichts an; wer hierher gehoert, sieht den einen Knopf und weiss, was zu tun ist. Beim allerersten Aufruf bleibt der Hinweis, dass der Erste Administrator wird — der ist dann nuetzlich. Dazu die Anleitungen: Der Reverse-Proxy-Abschnitt im README nennt jetzt die drei Kopfzeilen, die die Anwendung vom Proxy erwartet, und was ohne sie passiert; dazu client_max_body_size fuer Uploads, ein Weg zum Nachpruefen und die Frage, fuer wen die Codes erreichbar sein sollen. In authentik.md steht, warum BASE_URL und die Adresse im Browser dieselbe sein muessen.
14 KiB
Anmeldung über Authentik (OIDC)
Diese Anleitung richtet die Anmeldung am QR-Code-Generator über Authentik ein. Sie funktioniert genauso mit anderen OpenID-Connect-Anbietern — Keycloak, Zitadel, Entra ID —, weil die Anwendung alles Nötige aus dem Discovery-Dokument des Anbieters liest. Die Beispiele nennen Authentik, weil die Feldnamen dort so heißen.
Zeitbedarf: etwa 15 Minuten.
Vorher klären
| Frage | Antwort für diese Anleitung |
|---|---|
| Adresse der Anwendung | https://qr.example.com |
| Adresse von Authentik | https://auth.example.com |
| Läuft die Anwendung über HTTPS? | Ja — siehe Hinweis unten |
HTTPS ist Pflicht, sobald es über den eigenen Rechner hinausgeht. Beim Anmelden gehen Token über die Leitung, und der Sitzungs-Cookie wird nur bei
https://in derBASE_URLalsSecuregesetzt. Für einen Versuch im LAN geht auch HTTP; produktiv gehört ein Reverse Proxy davor (siehe README, Abschnitt „Hinter einem Reverse Proxy“).
Die Anwendung muss Authentik erreichen können und umgekehrt der Browser beide. Laufen beide in Docker auf demselben Host, genügt es meist, sie ins selbe Netz zu hängen — der Browser braucht aber in jedem Fall öffentlich auflösbare Adressen.
Schritt 1 — In Authentik einen Provider anlegen
Applications → Providers → Create → OAuth2/OpenID Provider
| Feld | Wert |
|---|---|
| Name | QR-Code-Generator |
| Authorization flow | default-provider-authorization-explicit-consent (oder implicit, wenn niemand zustimmen soll) |
| Client type | Confidential |
| Client ID | notieren — kommt gleich in die .env |
| Client Secret | notieren — ebenfalls |
| Redirect URIs | deine BASE_URL + /api/auth/oidc/callback — siehe unten |
| Signing Key | ein vorhandenes Zertifikat auswählen |
| Subject mode | Based on the User's hashed ID (Voreinstellung) |
Zu den Scopes: openid, email und profile müssen ausgewählt sein.
Ohne email liefert Authentik keine Adresse, und die Anwendung lehnt die
Anmeldung mit einer entsprechenden Meldung ab.
Die Redirect URI
Das ist der häufigste Stolperstein. Der Wert ist kein Beispiel zum
Abschreiben, sondern ergibt sich aus deiner BASE_URL:
{BASE_URL}/api/auth/oidc/callback
Was dein Container als BASE_URL kennt, zeigt:
docker compose exec app env | grep BASE_URL
Ein paar Beispiele:
BASE_URL |
Redirect URI |
|---|---|
https://qr.example.com |
https://qr.example.com/api/auth/oidc/callback |
http://10.10.10.150:3005 |
http://10.10.10.150:3005/api/auth/oidc/callback |
http://192.168.1.50:3000 |
http://192.168.1.50:3000/api/auth/oidc/callback |
Zu beachten:
- Kein Schrägstrich am Ende.
- Schema und Port gehören dazu —
httpist nichthttps, und:3005darf nicht fehlen. - Neben dem Feld steht in neueren Authentik-Fassungen eine Auswahl Strict
oder Regex. Für eine feste Adresse muss dort Strict stehen. Bei
Regexwäre jeder Punkt einer IP-Adresse ein Platzhalter und müsste maskiert werden. - Bleibt das Feld leer, lässt Authentik gar nichts durch.
Wenn es trotzdem klemmt: Authentik zeigt den Fehler „Redirect URI Error“ noch auf seiner eigenen Seite an — die Anwendung bekommt davon nichts mit. Welche Adresse tatsächlich ankam, steht zum Vergleich
- in der Adresszeile des Browsers, im Parameter
redirect_uri, und- in Authentik unter Events → Logs im Eintrag
redirect_uri_error.Weicht die ankommende Adresse von deiner
BASE_URLab, ist nicht Authentik falsch eingestellt, sondern dieBASE_URL— und die gehört korrigiert, bevor der erste dynamische Code entsteht: Sie steckt in jedem davon.
BASE_URL und die Adresse im Browser müssen dieselbe sein
Nicht nur „irgendeine erreichbare Adresse“ — dieselbe, über die ihr die Anwendung tatsächlich aufruft.
Der Grund liegt im Ablauf: Beim Klick auf den Anmeldeknopf legt die Anwendung
einen Cookie mit dem laufenden Vorgang ab — auf dem Host, über den der Browser
gerade da ist. Die Rückkehradresse für Authentik kommt dagegen aus BASE_URL.
Zeigen die beiden auf verschiedene Hosts, schickt Authentik den Browser auf den
anderen Host, der Cookie bleibt beim ersten zurück, und die Anmeldung endet mit
„Der Anmeldevorgang ist abgelaufen“. Erkennbar ist es daran, dass in der
Adresszeile plötzlich eine andere Adresse steht als die, mit der man begonnen
hat.
Seit Version vom 17.08.2026 fängt die Anwendung das ab und nennt beide Adressen, statt den Vorgang bis zum Ende laufen zu lassen.
Ein häufiges Muster: Die Anwendung läuft hinter einem Reverse Proxy unter
https://qr.example.com, in der .env steht aber noch die interne Adresse
http://10.0.0.5:3005 aus der Einrichtung. Dann gehören drei Werte in
Übereinstimmung gebracht:
| Aufruf im Browser | https://qr.example.com |
BASE_URL in der .env |
https://qr.example.com |
| Redirect URI in Authentik | https://qr.example.com/api/auth/oidc/callback |
Schritt 2 — Eine Application anlegen
Applications → Applications → Create
| Feld | Wert |
|---|---|
| Name | QR-Code-Generator |
| Slug | qrcode-generator |
| Provider | der eben angelegte Provider |
| Launch URL | https://qr.example.com |
Der Slug steckt gleich in der Issuer-Adresse — merken.
Wer den Zugang einschränken will, hängt hier unter Policy / Group / User Bindings eine Gruppe ein. Das ist der empfohlene Weg: Wer sich bei Authentik anmelden darf, kommt sonst auch in den QR-Code-Generator.
Schritt 3 — Die Issuer-Adresse herausfinden
Authentik zeigt sie beim Provider unter Provider Info als OpenID Configuration Issuer. Sie sieht so aus:
https://auth.example.com/application/o/qrcode-generator/
Prüfen lässt sich das direkt:
curl https://auth.example.com/application/o/qrcode-generator/.well-known/openid-configuration
Kommt ein JSON-Dokument mit authorization_endpoint, token_endpoint und
jwks_uri zurück, stimmt die Adresse.
Schritt 4 — Die .env ergänzen
AUTH_ENABLED=true
OIDC_ENABLED=true
OIDC_ISSUER=https://auth.example.com/application/o/qrcode-generator/
OIDC_CLIENT_ID=<Client ID aus Schritt 1>
OIDC_CLIENT_SECRET=<Client Secret aus Schritt 1>
OIDC_BUTTON_LABEL=Mit Authentik anmelden
OIDC_SCOPES=openid email profile
OIDC_DEFAULT_ROLE=EDITOR
OIDC_ADMIN_GROUP=qr-admins
OIDC_GROUPS_CLAIM=groups
OIDC_ONLY=false
Danach neu starten:
docker compose up -d
OIDC_ENABLEDallein genügt nicht —AUTH_ENABLEDmuss ebenfallstruesein. Ohne Anmeldung gibt es keine Benutzer, denen eine Anmeldung zugeordnet werden könnte.
Schritt 5 — Ausprobieren
https://qr.example.com/anmelden aufrufen. Über dem Passwortfeld steht jetzt
„Mit Authentik anmelden“. Ein Klick führt zu Authentik, nach der Anmeldung
geht es zurück in die Anwendung.
Alle Anmeldenden bekommen die Rolle aus OIDC_DEFAULT_ROLE in der
Standardgruppe. Wer die Verwaltung öffnen darf, hängt davon ab, ob
OIDC_ADMIN_GROUP gesetzt ist:
- Leer: Der erste Mensch, der sich anmeldet, wird Administrator dieser Installation — genau wie bei der Ersteinrichtung mit Passwort. Danach vergibt man das Recht unter Verwaltung → Benutzer.
- Gesetzt: Dann entscheidet allein die Gruppe, und zwar bei jeder Anmeldung neu — auch beim allerersten Benutzer.
Achtung beim ersten Mal: Wenn du
OIDC_ADMIN_GROUPsetzt, vergewissere dich, dass dein eigenes Konto in dieser Gruppe ist. Sonst hat niemand Zugang zur Verwaltung. Der Weg zurück ist einfach: die Variable leeren,docker compose up -d, neu anmelden — dann greift wieder die Regel „der erste wird Administrator“, sofern es noch gar keinen gibt.
Was die Anwendung aus Authentik übernimmt — und was nicht
| Übernommen | Bleibt in der Anwendung |
|---|---|
Wer jemand ist (sub) |
Mitgliedschaft in Gruppen |
| E-Mail-Adresse | Rolle je Gruppe (Admin/Bearbeiter/Betrachter) |
| Anzeigename | Zuteilung von Domains |
Instanz-Administration, falls OIDC_ADMIN_GROUP gesetzt ist |
alles Übrige |
Das ist eine bewusste Entscheidung. Die Gruppen in Authentik und die Gruppen hier bedeuten nicht dasselbe: dort geht es um Organisation und Zugang, hier um die Frage, wer welche QR-Codes sehen und ändern darf. Eine automatische Übersetzung wäre eine Quelle stiller Überraschungen — deshalb pflegt man Gruppen und Rollen weiterhin unter Verwaltung → Gruppen.
Wiedererkannt wird jemand an der Kennung von Authentik (sub), nicht an
der E-Mail-Adresse. Ändert sich die Adresse, bleibt es dasselbe Konto.
Die Admin-Gruppe
Steht in OIDC_ADMIN_GROUP ein Gruppenname, entscheidet Authentik bei jeder
Anmeldung neu, wer die Verwaltung öffnen darf: Mitglieder der Gruppe
bekommen das Recht, alle anderen verlieren es. Bleibt die Variable leer,
rührt die Anmeldung das Recht nicht an — dann pflegt man es hier.
Bestehende Konten übernehmen
Gibt es hier schon Konten mit Passwort, werden sie beim ersten Anmelden über Authentik anhand der E-Mail-Adresse verknüpft. Aus dem lokalen Administrator wird also kein zweites Konto; Gruppen, Rollen und die angelegten Codes bleiben.
Verknüpft wird nur, wenn Authentik die Adresse als bestätigt meldet. Sonst könnte sich beim Anbieter jemand eine fremde Adresse eintragen und damit ein hiesiges Konto übernehmen. Meldet Authentik sie als unbestätigt, bricht die Anmeldung mit einer Erklärung ab.
Ist ein Konto einmal verknüpft, gibt es dafür kein Passwort mehr: Der Bereich Konto zeigt dann statt der Passwortänderung einen Hinweis auf den Anbieter.
Nur noch über Authentik anmelden
OIDC_ONLY=true
Danach verschwindet das Passwortfeld von der Anmeldeseite.
Vorher an den Notausgang denken. Fällt Authentik aus oder ist der Provider falsch konfiguriert, kommt niemand mehr hinein. Der Weg zurück:
OIDC_ONLY=falsein der.env,docker compose up -d— dann geht die Anmeldung mit Passwort wieder. Lege dir dafür ein lokales Administratorkonto an, bevor du umstellst.
Wenn etwas nicht klappt
| Symptom | Ursache und Abhilfe |
|---|---|
| Der Knopf erscheint nicht | AUTH_ENABLED oder OIDC_ENABLED steht nicht auf true. Prüfen mit docker compose exec app env | grep OIDC. Steht dort nichts, fehlt die Variable im environment:-Block der docker-compose.yml — die .env allein reicht nicht. |
| „Die Anmeldung über den externen Anbieter ist unvollständig eingerichtet“ | Es fehlt OIDC_ISSUER, OIDC_CLIENT_ID oder OIDC_CLIENT_SECRET. Die Meldung nennt, welche. |
| „Der Anmelde-Anbieter ist nicht erreichbar“ | Der Container kommt nicht an Authentik. Testen mit docker compose exec app wget -qO- <issuer>/.well-known/openid-configuration. Häufig: interner DNS-Name statt öffentlicher Adresse. |
| Authentik zeigt „Redirect URI Error“ | Die Redirect URI im Provider stimmt nicht exakt mit {BASE_URL}/api/auth/oidc/callback überein — siehe Die Redirect URI. Auf Schema, Port, Schrägstrich am Ende und die Auswahl Strict/Regex achten. |
| „Der Anbieter hat keine E-Mail-Adresse geliefert“ | Der Scope email fehlt im Provider, oder der Benutzer hat in Authentik keine Adresse hinterlegt. |
| „Der Anbieter hat die Anmeldung abgelehnt“ | Der Benutzer ist der Application in Authentik nicht zugeordnet — Policy/Group Binding prüfen. |
| „Der Anmeldevorgang ist abgelaufen“ | Der Cookie mit dem laufenden Vorgang kam nicht zurück. Entweder sind mehr als 10 Minuten vergangen — oder, weit häufiger, BASE_URL zeigt auf einen anderen Host als den, über den der Browser kommt. Erkennbar daran, dass in der Adresszeile eine andere Adresse steht als beim Start. Siehe BASE_URL und die Adresse im Browser. |
| „Du rufst die Anwendung über … auf, BASE_URL zeigt aber auf …“ | Genau der Fall von oben, nur früh erkannt. Die Meldung nennt beide Adressen; BASE_URL in der .env auf die erste setzen und docker compose up -d ausführen. |
| „Das Anmelde-Token des Anbieters ist ungültig“ | Uhrzeit-Abweichung zwischen den Servern oder ein gewechselter Signaturschlüssel. Beides zeigt docker compose logs app genauer. |
| Nach der Anmeldung fehlt die Verwaltung | OIDC_ADMIN_GROUP ist gesetzt, der Benutzer aber nicht in dieser Gruppe. Entweder die Gruppe in Authentik ergänzen oder die Variable leeren und das Recht hier vergeben. |
Ausführlichere Meldungen stehen im Protokoll:
docker compose logs -f app
Was technisch passiert
- Klick auf den Knopf →
/api/auth/oidc/start. Die Anwendung holt das Discovery-Dokument, würfeltstate,nonceund ein PKCE-Geheimnis und legt sie in einen kurzlebigen,httpOnly-Cookie. - Weiterleitung zu Authentik mit
code_challenge(S256). - Nach der Anmeldung kommt Authentik mit
codeundstatezurück auf/api/auth/oidc/callback. Stimmt derstatenicht, endet es hier. - Die Anwendung tauscht den Code gegen Token — mit Client-Geheimnis und PKCE-Verifier. Ein abgefangener Code allein nützt also niemandem.
- Das ID-Token wird gegen den JWKS von Authentik geprüft: Signatur,
Aussteller, Empfänger, Ablauf und
nonce. - Konto suchen (über
sub, ersatzweise über die bestätigte Adresse), anlegen oder verknüpfen — und die Sitzung wie bei der Passwort-Anmeldung setzen.
Die Sitzung selbst ist danach dieselbe wie sonst: ein Zufallstoken im Cookie, in der Datenbank nur dessen SHA-256. Abmelden beendet sie hier; bei Authentik bleibt man angemeldet.