Nach dem Update unserer selbst gehosteten Discourse-Umgebungen am 11. September 2026 begann der OIDC-Login mit folgendem Fehler zu scheitern:
(oidc) Authentication failure! jwt_decode_failed:
JWT::DecodeError, Nil JSON web token
Unsere ältere Entwicklungsumgebung funktionierte weiterhin einwandfrei.
Umgebung
- Discourse läuft in Docker auf einer Azure Linux VM.
- Azure Front Door → interner Lastausgleich → VM.
- Discourse ist unter
/forumgehostet. - Azure AD B2C Custom Policies für OIDC.
- Der ausgehende Traffic der VM wird über die Azure Firewall geroutet.
- Fehlgeschlagene Discourse-Revision:
74f839fb7. - Funktionierende Dev-Revision:
502aa3687.
Die relevanten Abhängigkeitsversionen waren:
| Abhängigkeit | Funktionierende Dev | Fehlgeschlagene Produktion |
|---|---|---|
| oauth2 | 1.4.11 | 2.0.25 |
| omniauth-oauth2 | 1.7.3 | 1.9.0 |
| jwt | 2.10.1 | 3.2.0 |
Das aktualisierte Repository enthält d881bf2d4aabcf2430863a96beafaeabaf915702, „DEPS: Upgrade oauth2 to 2.x“ (#43523).
Minimale Reproduktion
Dafür werden keine Netzwerkverbindung, Azure-Infrastruktur oder echte Zugangsdaten benötigt:
require "oauth2"
puts Gem.loaded_specs.fetch("oauth2").version
client = OAuth2::Client.new(
"dummy",
"dummy",
site: "https://example.invalid"
)
token = OAuth2::AccessToken.from_hash(
client,
{"id_token" => "dummy-id"}
)
puts "Main token: #{token.token.inspect}"
puts "ID token parameter: #{token["id_token"].inspect}"
Mit OAuth2 1.4.11 ist der Haupt-Token leer und der ID-Token-Parameter bleibt "dummy-id".
Mit OAuth2 2.0.25 wird der Haupt-Token zu "dummy-id" und der ID-Token-Parameter wird zu nil.
Zusammenhang mit dem Discourse-Fehler
Im no-userinfo-Zweig konstruiert das OIDC-Plugin den Token mit:
::OAuth2::AccessToken.from_hash(client, response.parsed)
Später versucht es:
::JWT.decode(access_token["id_token"], nil, false).first
Wenn die Bibliothek id_token als Haupt-Token verbraucht, liefert die nachfolgende Parameterabfrage nil zurück.
Getesteter Workaround
Wir haben die Token-Konstruktion geändert, um den ID-Token aus der ursprünglichen Antwort zu erhalten:
payload = response.parsed
token = ::OAuth2::AccessToken.from_hash(client, payload)
token.params["id_token"] = payload["id_token"] if payload["id_token"]
token
Nach dem Anwenden dieses Patches und dem Neustart des UAT-Containers war der Login erfolgreich. Für diese Wiederherstellung waren keine Änderungen an Front Door, Firewall oder B2C erforderlich.
Dies erhält die bestehende Claim- und Nonce-Validierung und lässt den userinfo-Zweig unverändert.
Die rohe Live-Token-Antwort ist nicht enthalten; das Bibliothekverhalten wurde mit Fingierdaten reproduziert und der Workaround wurde über einen echten UAT-Login validiert.
Ist dies bereits durch einen Upstream-Fix abgedeckt? Andernfalls kann ich einen fokussierten PR mit Regressionsabdeckung einreichen.