Files
qrcode-generator/docs/authentik.md
T
Florian Wilde fcf41f56fe Authentik-Anleitung: die Redirect URI deutlicher erklaert
Der Beispielwert https://qr.example.com/... liess sich als etwas zum
Abschreiben lesen. Er ist aber keiner: Die Adresse ergibt sich aus der
eigenen BASE_URL, und wer den Beispielwert uebernimmt, landet bei Authentik
auf „Redirect URI Error", ohne dass die Anwendung ueberhaupt etwas mitbekommt.

Jetzt ein eigener Abschnitt mit der Formel, dem Befehl zum Nachsehen der
BASE_URL, drei Beispielen und den vier Fallen: Schraegstrich am Ende, Schema
und Port, die Auswahl Strict/Regex neben dem Feld, und ein leeres Feld.

Dazu, wo die tatsaechlich angekommene Adresse steht — in der Adresszeile und
in Authentik unter Events, Logs. Und der Hinweis, dass eine abweichende
Adresse nicht fuer eine falsche Authentik-Einstellung spricht, sondern fuer
eine falsche BASE_URL — die gehoert korrigiert, bevor der erste dynamische
Code entsteht.
2026-08-16 23:51:28 +02:00

292 lines
12 KiB
Markdown

# 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 der `BASE_URL` als `Secure` gesetzt. 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:
```bash
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** — `http` ist nicht `https`, und `:3005`
darf 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
`Regex` wä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
> 1. in der Adresszeile des Browsers, im Parameter `redirect_uri`, und
> 2. in Authentik unter **Events → Logs** im Eintrag `redirect_uri_error`.
>
> Weicht die ankommende Adresse von deiner `BASE_URL` ab, ist nicht Authentik
> falsch eingestellt, sondern die `BASE_URL` — und die gehört korrigiert,
> bevor der erste dynamische Code entsteht: Sie steckt in jedem davon.
---
## 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:
```bash
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
```bash
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:
```bash
docker compose up -d
```
> `OIDC_ENABLED` allein genügt nicht — **`AUTH_ENABLED` muss ebenfalls `true`
> sein**. 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_GROUP` setzt, 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
```bash
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=false` in 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](#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“ | Zwischen Klick und Rückkehr sind mehr als 10 Minuten vergangen, oder der Cookie ging verloren. Häufigste echte Ursache: `BASE_URL` zeigt auf eine andere Adresse als die, über die der Browser kommt. |
| „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:
```bash
docker compose logs -f app
```
---
## Was technisch passiert
1. Klick auf den Knopf → `/api/auth/oidc/start`. Die Anwendung holt das
Discovery-Dokument, würfelt `state`, `nonce` und ein PKCE-Geheimnis und legt
sie in einen kurzlebigen, `httpOnly`-Cookie.
2. Weiterleitung zu Authentik mit `code_challenge` (S256).
3. Nach der Anmeldung kommt Authentik mit `code` und `state` zurück auf
`/api/auth/oidc/callback`. Stimmt der `state` nicht, endet es hier.
4. Die Anwendung tauscht den Code gegen Token — mit Client-Geheimnis **und**
PKCE-Verifier. Ein abgefangener Code allein nützt also niemandem.
5. Das ID-Token wird gegen den JWKS von Authentik geprüft: Signatur,
Aussteller, Empfänger, Ablauf und `nonce`.
6. 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.