OIDC csrf_detected pode mascarar cancelamento do usuário / rejeição de consentimento - documentação poderia esclarecer inspeção de logs

Continuando a discussão de OIDC login via Discourse iOS app occasionally fails with csrf_detected on callback:

Olá a todos,

Esta é uma observação de acompanhamento ligada ao meu tópico anterior sobre falhas de login OIDC iniciadas a partir de navegadores internos do iOS. Essa discussão focou em falhas determinísticas de csrf_detected causadas pelo isolamento de cookies do WKWebView, que agora parecem bem compreendidas e esperadas.

Este tópico vinculado é sobre clareza para os operadores, não um bug.


Observação

Ao investigar uma série de falhas de login OIDC, notei que a mesma mensagem de erro na superfície do Discourse:

/auth/oidc/callback → /auth/failure?message=csrf_detected

pode corresponder a múltiplas causas raiz fundamentalmente diferentes, dependendo do que o IdP retornou.

Apenas a partir da interface do usuário do aplicativo, esses casos são indistinguíveis. A diferença só é visível ao inspecionar Admin → Logs → env / params.


Exemplos vistos na prática (Azure / Entra ID)

Além da perda de cookies do navegador interno, observei callbacks onde o Entra ID retorna explicitamente erros estruturados como:

Usuário recusou o consentimento

error=consent_required
error_description=AADSTS65004: User declined to consent to access the app

Usuário cancelou o login

error=access_denied
error_subcode=cancel

Em ambos os casos:
\t•\tO Azure identificou o usuário com sucesso
\t•\tO usuário escolheu explicitamente não prosseguir (recusar / cancelar)
\t•\tO Discourse recebe o callback
\t•\tO fluxo acaba resolvendo para /auth/failure?message=csrf_detected

Do ponto de vista do Discourse, este é um comportamento correto e seguro - o estado não pode ser validado ou concluído - mas a razão subjacente é muito diferente de um cookie de sessão ausente.


Por que isso é importante para os operadores

Sem verificar o env/params do log, um administrador que vê falhas repetidas de csrf_detected pode razoavelmente presumir:
\t•\tcookies quebrados
\t•\tconfiguração incorreta do SameSite
\t•\tproblemas no navegador móvel
\t•\tinstabilidade do IdP

…quando, na realidade, algumas dessas falhas são simplesmente usuários escolhendo não consentir ou cancelar o prompt da Microsoft.

Essa distinção só fica clara se você já souber inspecionar a carga de log bruta.


Sugestão (apenas documentação / UX)

Não estou sugerindo nenhuma mudança de comportamento no OmniAuth ou no tratamento de CSRF.

Poderia ser útil se a documentação ou o guia de solução de problemas notassem explicitamente que:
\t•\tcsrf_detected pode ser o erro final para múltiplos resultados de IdP upstream
\t•\tincluindo ações explícitas do usuário, como cancelar ou rejeitar o consentimento
\t•\te que os administradores devem inspecionar Admin → Logs → env / params para distinguir esses casos

Isso facilitaria para os operadores:
\t•\tdiagnosticar corretamente as falhas de login
\t•\tevitar alterações de configuração desnecessárias
\t•\te fornecer orientação precisa aos usuários (“você cancelou / recusou o consentimento” vs “seu navegador bloqueou cookies”).


Contexto

Para maior clareza: isso é separado do problema confirmado do navegador interno do iOS discutido no tópico vinculado. Nesse caso, o IdP nunca chega ao ponto de consentimento do usuário, enquanto aqui o IdP relata explicitamente a intenção do usuário.

Ambos acabam parecendo semelhantes no nível da interface do usuário, a menos que os logs sejam examinados.


Obrigado por ler - estou postando isso principalmente como um ponto de clareza/dados de documentação para outros que executam OIDC em ambientes com muitos estudantes, onde esses casos ocorrem com frequência.

Fico feliz em fornecer exemplos anonimizados, se for útil.