Kurz gesagt
Problem: Jeder Login über einen neuen OAuth2/OpenID-Provider in Authentik scheitert mit invalid_request: „The request is otherwise malformed“.
Lösung: Der Provider wurde über die API angelegt, und seine Liste grant_types war leer. Setz sie auf authorization_code und refresh_token und teste danach die Authorize-URL mit curl.
Getestet mit: Authentik 2026.8, WordPress mit dem Plugin OpenID Connect Generic 3.11
Die meisten Apps lege ich in Authentik über die Admin-Oberfläche an. Für diesen Blog wollte ich es per Skript machen und habe den OAuth2-Provider mit einem einzigen API-Aufruf erstellt. Der Aufruf klappte, die Anwendung tauchte auf, das WordPress-Plugin war mit der richtigen Client-ID, dem Secret und den Endpunkten eingerichtet. Und dann funktionierte kein einziger Login.
Die Lösung war am Ende ein einziges Feld. Sie zu finden hat länger gedauert als nötig, weil die Fehlermeldung in die falsche Richtung zeigt.
1. Das Symptom: „request is otherwise malformed“
Ein Klick auf „Login with OpenID Connect“ in WordPress schickte mich zu Authentik, und Authentik schickte mich sofort zurück, mit einer Fehlerseite statt eines Login-Formulars:
invalid_request
The request is otherwise malformed
Keine Abfrage von Benutzername oder Passwort. Authentik hat die Anfrage abgelehnt, bevor der Login-Flow überhaupt begonnen hat.
2. Was ich zuerst geprüft habe (und warum mich das in die Irre geführt hat)
„Malformed request“ klingt so, als hätte der Client etwas Falsches geschickt. Also bin ich die üblichen Verdächtigen auf der WordPress-Seite durchgegangen:
- Redirect-URI: Zeichen für Zeichen mit dem Provider verglichen. Authentik vergleicht strikt, schon ein fehlender Schrägstrich oder ein anderer Query-String reicht für einen Fehler. Sie stimmte.
- Client-ID: noch einmal kopiert, keine Leerzeichen am Rand.
- Scopes:
openid email profile, alle drei im Provider vorhanden. - Endpunkte: aus der
.well-known/openid-configurationdes Providers übernommen.
Alles war richtig. Die Anfrage von WordPress war in Ordnung. Das Problem lag auf der Seite von Authentik, und das verrät dir die Fehlermeldung im Browser nicht.
3. Die eigentliche Ursache: eine leere grant_types-Liste
Das Server-Log von Authentik hatte die hilfreichere Meldung. Mit Docker Compose:
docker compose logs server --since 10m | grep -i grant
Dort stand „Invalid grant_type for provider“. Jeder OAuth2-Provider in Authentik hat eine Liste erlaubter Grant Types. Legst du einen Provider in der Admin-Oberfläche an, füllt das Formular sinnvolle Standardwerte ein. Die API macht das nicht: grant_types ist ein optionales Feld, und wenn du es weglässt, wird der Provider mit einer leeren Liste gespeichert. Eine leere Liste heißt: kein Grant Type ist erlaubt, nicht einmal der normale Authorization-Code-Flow.
Mein API-Aufruf sah ungefähr so aus (gekürzt):
curl -X POST https://id.example.com/api/v3/providers/oauth2/ \
-H "Authorization: Bearer $AUTHENTIK_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "Provider for My Blog",
"client_type": "confidential",
"authorization_flow": "<flow uuid>",
"invalidation_flow": "<flow uuid>",
"redirect_uris": [
{"matching_mode": "strict",
"url": "https://blog.example.com/wp-admin/admin-ajax.php?action=openid-connect-authorize"}
],
"signing_key": "<certificate uuid>",
"property_mappings": ["<openid>", "<email>", "<profile>"]
}'
Alle Pflichtfelder sind da, also nimmt die API den Aufruf ohne Warnung an. Genau das eine Feld, an dem hängt, ob sich überhaupt jemand anmelden kann, fehlt.
4. Die Lösung
Setz die Grant Types am bestehenden Provider. Ein Login-Flow für eine Website braucht authorization_code, und refresh_token, wenn die App ihre Tokens erneuert:
curl -X PATCH https://id.example.com/api/v3/providers/oauth2/<provider id>/ \
-H "Authorization: Bearer $AUTHENTIK_TOKEN" \
-H "Content-Type: application/json" \
-d '{"grant_types": ["authorization_code", "refresh_token"]}'
Du kannst den Provider auch in der Admin-Oberfläche öffnen und ihn dort mit beiden Grant Types angehakt speichern. Bei neuen Providern gehört die Zeile gleich in den Aufruf zum Anlegen:
"grant_types": ["authorization_code", "refresh_token"],
Wenn du schon dabei bist, vergleich auch die Token-Laufzeiten (access_token_validity, refresh_token_threshold) mit einem Provider, den du in der Oberfläche angelegt hast. Über die API angelegte Provider bekommen nicht immer dieselben Werte, und gleich eingestellte Provider sind leichter zu pflegen.
Achtung: Erlaube nur die Grant Types, die die App wirklich nutzt. password und client_credentials umgehen den Login-Flow und damit auch deine MFA und deine Richtlinien. Ein Website-Login braucht sie nicht.
5. So prüfst du es (ohne Browser)
Um einen Provider zu testen, musst du dich nicht durch den Login klicken. Ruf den Authorize-Endpunkt mit curl auf und schau, wohin Authentik dich schickt:
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' \
"https://id.example.com/application/o/authorize/?client_id=<client id>&response_type=code&scope=openid&redirect_uri=https%3A%2F%2Fblog.example.com%2Fwp-admin%2Fadmin-ajax.php%3Faction%3Dopenid-connect-authorize"
- Funktioniert: ein
302zum Login-Flow von Authentik, zum Beispiel/if/flow/default-authentication-flow/…. - Kaputt: ein
302zurück zur Redirect-URI deiner App miterror=…in der URL. Der Name des Fehlers sagt dir, was Authentik nicht gepasst hat.
Tipp: Die Redirect-URI im curl-Aufruf muss URL-kodiert sein und genau zum Provider passen, sonst testest du das falsche Problem. Ich mache diesen Test inzwischen nach jedem neuen Provider, egal ob über die API oder die Oberfläche angelegt. Er dauert fünf Sekunden.
Was ich gelernt habe
- „Malformed request“ im Browser heißt nicht, dass der Client falsch ist. Lies das Server-Log des Identity Providers, bevor du in der App etwas änderst.
- Admin-Oberfläche und API legen nicht dasselbe Objekt an. Die Oberfläche füllt Standardwerte ein, die API speichert genau das, was du schickst.
- Ein curl-Test der Authorize-URL prüft einen Provider in Sekunden, ganz ohne Login.
Wie der Blog-Login von Anfang bis Ende eingerichtet ist, steht in Single Sign-on für WordPress mit Authentik. Alle Felder eines Providers beschreibt die offizielle Authentik-Dokumentation.