Seguindo com alguma investigação adicional e confirmação de uso no mundo real.
Após investigar mais a fundo, acho que esta é, de fato, uma limitação do navegador in-app do iOS (WKWebView) em vez de algo específico do Discourse ou da configuração do Azure.
O que consegui confirmar
A partir dos logs do nginx + Rails e testes com usuários:
- O fluxo OAuth é iniciado às vezes dentro de um navegador in-app do iOS (ex: Snapchat)
- O login da Microsoft (login.microsoftonline.com) é carregado dentro desse navegador in-app
- O callback OIDC alcança o Discourse com sucesso
- Mas o cookie de sessão contendo o valor do estado não sobrevive
- Resultando em um determinístico:
GET /auth/failure?message=csrf_detected&strategy=oidc
Isso ocorre mesmo que o referenciador seja a Microsoft e a URI de redirecionamento esteja correta.
Um UA representativo:
Mozilla/5.0 (iPhone; CPU iPhone OS 18_7 like Mac OS X)
Snapchat/13.76.1.0 (like Safari/..., panda)
Portanto, embora a interface do usuário “oneboxe” a Microsoft de forma elegante, o navegador subjacente ainda é o WKWebView do Snapchat, e não o Safari / ASWebAuthenticationSession.
A interceptação do componente de tema não funciona
Tentei mitigar isso no lado do cliente usando um componente de tema:
- detectando UAs de navegadores in-app conhecidos
- bloqueando links /auth/oidc
- exibindo uma sobreposição persistente instruindo os usuários a abrir no Safari/Chrome
No entanto, isso não intercepta o fluxo de forma confiável, porque:
- o redirecionamento OAuth é iniciado no lado do servidor
- o cookie de estado deve existir antes que o JS do cliente seja executado
- quando o tema carrega, o dano já está feito
Portanto, um componente de tema não pode impedir confiavelmente esse modo de falha.
O que funciona de forma confiável
A única mitigação que encontrei que funciona consistentemente é substituir o texto do site:
login.omniauth_error.csrf_detected
para explicar explicitamente que:
- o login falhou devido a um navegador in-app
- os usuários devem abrir o site no Safari ou Chrome
- e então tentar o login novamente
Esta mensagem é renderizada no lado do servidor após a falha e, portanto, aparece mesmo em contextos de navegador in-app quebrados.
Isso reduziu significativamente a confusão do usuário, já que anteriormente os usuários eram silenciosamente retornados à página de login e muitas vezes não percebiam que algo havia dado errado.
Sobre cookies SameSite
Eu não mudei same_site_cookies para “None”.
Dado que este é um problema de isolamento do WKWebView (em vez de navegação entre sites no Safari), mudar SameSite não parece resolver a causa raiz e pode introduzir trocas de segurança desnecessárias.
Pergunta em aberto
Dado o exposto, eu queria verificar se:
- este comportamento é considerado esperado quando o OAuth é iniciado a partir de navegadores in-app do iOS
- e se o Discourse pretende suportar esse fluxo, ou simplesmente documentar que o login deve ocorrer a partir de um navegador real
Também pode ser útil para o Discourse exibir uma mensagem padrão mais explícita para csrf_detected em contextos OmniAuth, já que esse modo de falha parece estar se tornando cada vez mais comum com usuários estudantes que chegam por meio de links do Snapchat / Instagram.
Fico feliz em fornecer mais logs anonimizados, se for útil - mas neste ponto o comportamento parece muito consistente e reprodutível.
Obrigado por dar uma olhada.