A seguito di ulteriori indagini e conferme derivanti dall’uso nel mondo reale.
Dopo aver approfondito, penso che questo sia effettivamente un limite del browser in-app di iOS (WKWebView) piuttosto che qualcosa di specifico di Discourse o della configurazione di Azure.
Ciò che sono riuscito a confermare
Dai log di nginx + Rails e dai test utente:
- Il flusso OAuth viene talvolta avviato all’interno di un browser in-app di iOS (ad esempio Snapchat)
- L’accesso a Microsoft (login.microsoftonline.com) viene caricato all’interno di quel browser in-app
- Il callback OIDC raggiunge Discourse con successo
- Ma il cookie di sessione contenente il valore di stato non sopravvive
- Risultando in un deterministico:
GET /auth/failure?message=csrf_detected&strategy=oidc
Ciò si verifica anche se il referer è Microsoft e l’URI di reindirizzamento è corretto.
Un UA rappresentativo:
Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X)
Snapchat/13.76.1.0 (like Safari/..., panda)
Quindi, sebbene l’interfaccia utente “onebox” Microsoft in modo elegante, il browser sottostante è ancora il WKWebView di Snapchat, non Safari / ASWebAuthenticationSession.
L’intercettazione del componente tema non funziona
Ho tentato di mitigare questo problema lato client utilizzando un componente tema:
- rilevando i UA di browser in-app noti
- bloccando i link /auth/oidc
- visualizzando un overlay persistente che istruisce gli utenti ad aprire in Safari/Chrome
Tuttavia, questo non intercetta in modo affidabile il flusso, perché:
- il reindirizzamento OAuth viene avviato lato server
- il cookie di stato deve esistere prima che il JS client venga eseguito
- quando il tema viene caricato, il danno è già fatto
Quindi un componente tema non può prevenire in modo affidabile questa modalità di errore.
Ciò che funziona in modo affidabile
L’unica mitigazione che ho trovato che funziona in modo coerente è sovrascrivere il testo del sito:
login.omniauth_error.csrf_detected
per spiegare esplicitamente che:
- l’accesso è fallito a causa di un browser in-app
- gli utenti dovrebbero aprire il sito in Safari o Chrome
- quindi riprovare l’accesso
Questo messaggio viene renderizzato lato server dopo il fallimento e quindi appare anche in contesti di browser in-app non funzionanti.
Ciò ha ridotto significativamente la confusione degli utenti, poiché in precedenza gli utenti venivano riportati silenziosamente alla pagina di accesso e spesso non si rendevano conto che qualcosa fosse andato storto.
Informazioni sui cookie SameSite
Non ho impostato same_site_cookies su “None”.
Dato che si tratta di un problema di isolamento di WKWebView (piuttosto che di navigazione cross-site in Safari), la modifica di SameSite non sembra affrontare la causa principale e potrebbe introdurre compromessi di sicurezza non necessari.
Domanda aperta
Dato quanto sopra, volevo verificare se:
- questo comportamento è considerato previsto quando OAuth viene avviato da browser in-app iOS
- e se Discourse intende supportare tale flusso, o semplicemente documentare che l’accesso deve avvenire da un browser reale
Potrebbe anche essere utile per Discourse mostrare un messaggio predefinito più esplicito per csrf_detected nei contesti OmniAuth, poiché questa modalità di errore sembra essere sempre più comune con gli utenti studenti che arrivano tramite link di Snapchat / Instagram.
Sono lieto di fornire altri log anonimizzati se utili, ma a questo punto il comportamento sembra molto coerente e riproducibile.
Grazie per aver dato un’occhiata.