Nach weiteren Nachforschungen und Bestätigung durch den realen Einsatz.
Nach eingehender Prüfung glaube ich, dass dies tatsächlich eine Einschränkung des iOS In-App-Browsers (WKWebView) und nicht etwas Spezifisches für Discourse oder die Azure-Konfiguration ist.
Was ich bestätigen konnte
Anhand von nginx- und Rails-Protokollen sowie Benutzertests:
- Der OAuth-Fluss wird manchmal innerhalb eines iOS In-App-Browsers (z. B. Snapchat) initiiert
- Die Microsoft-Anmeldung (login.microsoftonline.com) wird in diesem In-App-Browser geladen
- Der OIDC-Rückruf erreicht Discourse erfolgreich
- Aber das Sitzungscookie, das den Zustands-Wert enthält, überlebt nicht
- Was zu einem deterministischen Ergebnis führt:
GET /auth/failure?message=csrf_detected&strategy=oidc
Dies geschieht, obwohl der Referer Microsoft ist und die Weiterleitungs-URI korrekt ist.
Ein repräsentativer UA:
Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X)
Snapchat/13.76.1.0 (like Safari/..., panda)
Obwohl die Benutzeroberfläche Microsoft schön „einbettet“ (oneboxes), ist der zugrunde liegende Browser immer noch der WKWebView von Snapchat und nicht Safari / ASWebAuthenticationSession.
Theming-Komponenten-Abfangen funktioniert nicht
Ich habe versucht, dies clientseitig mithilfe einer Theming-Komponente zu mildern:
- Erkennen bekannter In-App-Browser-UAs
- Blockieren von /auth/oidc-Links
- Anzeigen eines persistenten Overlays, das Benutzer anweist, in Safari/Chrome zu öffnen
Dies fängt den Fluss jedoch nicht zuverlässig ab, weil:
- Die OAuth-Weiterleitung serverseitig initiiert wird
- Das Zustands-Cookie bereits existieren muss, bevor Client-JS ausgeführt wird
- Bis das Theme geladen wird, ist der Schaden bereits angerichtet
Eine Theming-Komponente kann dieses Fehlerverhalten also nicht zuverlässig verhindern.
Was zuverlässig funktioniert
Die einzige Abhilfemaßnahme, die ich gefunden habe und die konsistent funktioniert, ist das Überschreiben des Website-Textes:
login.omniauth_error.csrf_detected
um explizit zu erklären, dass:
- die Anmeldung aufgrund eines In-App-Browsers fehlgeschlagen ist
- Benutzer die Website in Safari oder Chrome öffnen sollten
- und dann die Anmeldung erneut versuchen sollten
Diese Nachricht wird serverseitig nach dem Fehler gerendert und erscheint daher auch in fehlerhaften In-App-Browser-Kontexten.
Dies hat die Verwirrung der Benutzer erheblich reduziert, da die Benutzer zuvor stillschweigend zur Anmeldeseite zurückgeleitet wurden und oft nicht merkten, dass etwas schiefgelaufen war.
Über SameSite-Cookies
Ich habe same_site_cookies nicht auf „None“ umgestellt.
Da es sich hier um ein WKWebView-Isolationsproblem handelt (und nicht um eine seitenübergreifende Navigation in Safari), scheint die Änderung von SameSite die Grundursache nicht zu beheben und könnte unnötige Sicherheitskompromisse mit sich bringen.
Offene Frage
Angesichts dessen wollte ich überprüfen, ob:
- dieses Verhalten als erwartet gilt, wenn OAuth von iOS In-App-Browsern initiiert wird
- und ob Discourse beabsichtigt, diesen Fluss zu unterstützen, oder ob es einfach dokumentieren soll, dass die Anmeldung von einem echten Browser aus erfolgen muss
Es könnte auch für Discourse nützlich sein, eine explizitere Standardnachricht für csrf_detected in OmniAuth-Kontexten anzuzeigen, da dieses Fehlerverhalten bei Studenten, die über Snapchat-/Instagram-Links kommen, zunehmend häufiger auftritt.
Gerne stelle ich weitere anonymisierte Protokolle zur Verfügung, falls dies hilfreich ist – aber an diesem Punkt scheint das Verhalten sehr konsistent und reproduzierbar zu sein.
Vielen Dank für die Prüfung.