OIDC csrf_detected가 사용자 취소 또는 동의 거부를 가릴 수 있음 - 로그 검사 관련 문서 명확화 필요

OIDC 로그인 via Discourse iOS 앱이 콜백에서 간헐적으로 csrf_detected 오류로 실패하는 문제에 대한 토론을 이어서 말씀드립니다:

안녕하세요,

이것은 이전에 iOS 인앱 브라우저에서 시작된 OIDC 로그인 실패에 대해 작성했던 스레드와 연결된 후속 관찰 사항입니다. 당시 토론은 WKWebView 쿠키 격리로 인한 결정론적 csrf_detected 실패에 초점을 맞추었으며, 현재는 이 문제가 충분히 이해되고 예상되는 현상인 것으로 보입니다.

이 연결된 토픽은 버그가 아니라 운영자(Operator)의 명확성(Clarity)에 관한 것입니다.


관찰 사항

OIDC 로그인 실패 일련의 문제를 조사하는 동안, Discourse에서 동일한 표면적 오류:

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

이 IdP가 반환한 내용에 따라 여러 가지 근본적으로 다른 상류(Upstream) 원인에 대응할 수 있음을 알게 되었습니다.

애플리케이션 UI만으로는 이러한 경우를 구별할 수 없습니다. 차이는 Admin → Logs → env / params를 검사해야만 확인할 수 있습니다.


실제 확인된 예시 (Azure / Entra ID)

인앱 브라우저 쿠키 손실 외에도, Entra ID가 명시적으로 구조화된 오류를 반환하는 콜백을 관찰했습니다:

사용자가 동의 거부

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

사용자가 로그인 취소

error=access_denied
error_subcode=cancel

두 경우 모두:
• Azure가 사용자를 성공적으로 식별했습니다.
• 사용자가 명시적으로 진행하지 않음(거부/취소)을 선택했습니다.
• Discourse가 콜백을 수신합니다.
• 플로우가 최종적으로 /auth/failure?message=csrf_detected로 해결됩니다.

Discourse의 관점에서 이는 상태(state)를 검증하거나 완료할 수 없으므로 정확하고 안전한 동작이지만, 근본적인 이유는 세션 쿠키 누락과 매우 다릅니다.


왜 이것이 운영자에게 중요한가

로그의 env/params를 확인하지 않으면, 반복적인 csrf_detected 실패를 보는 관리자는 합리적으로 다음과 같은 가정을 할 수 있습니다:
• 깨진 쿠키
• SameSite 구성 오류
• 모바일 브라우저 문제
• IdP 불안정성

그러나 실제로는 이러한 실패 중 일부는 단순히 사용자가 동의하지 않거나 Microsoft 프롬프트를 취소한 것입니다.

이 차이는 원시 로그 페이로드를 검사해야 한다는 것을 이미 알고 있을 때만 명확해집니다.


제안 (문서화/UX 전용)

OmniAuth 또는 CSRF 처리에 대한 동작 변경을 제안하는 것은 아닙니다.

문서화나 문제 해결 가이드에서 다음 사항을 명시적으로 언급하는 것이 도움이 될 수 있습니다:
• csrf_detected는 여러 상류 IdP 결과에 대한 최종 오류가 될 수 있으며
• 취소 또는 동의 거부와 같은 명시적인 사용자 동작을 포함합니다.
• 관리자는 이러한 경우를 구별하기 위해 Admin → Logs → env / params를 검사해야 합니다.

이렇게 하면 운영자가 다음을 수행하기 쉬워집니다:
• 로그인 실패를 정확히 진단하고
• 불필요한 구성 변경을 피하며
• 사용자에게 정확한 가이드를 제공합니다(예: “브라우저가 쿠키를 차단했습니다” 대신 “취소했습니다/동의를 거부했습니다”).


배경

명확성을 위해: 이것은 연결된 토픽에서 논의된 확인된 iOS 인앱 브라우저 문제와는 별개입니다. 그 경우 IdP는 사용자 동의 단계에 도달하지 못하지만, 여기서는 IdP가 명시적으로 사용자 의도를 보고합니다.

로그를 조사하지 않는 한, 두 경우 모두 UI 수준에서 유사해 보입니다.


읽어주셔서 감사합니다. 주로 이러한 경우가 빈번하게 발생하는 학생 중심 환경에서 OIDC를 운영하는 다른 사람들을 위한 문서 명확성/데이터 포인트로 게시합니다.

유용하다면 익명화된 예시를 제공할 수 있습니다.

로그 뷰어의 사소한 주의사항 (참고)

로그를 확인하던 중 작은 부분에서 당황했던 점이 하나 있습니다:

Admin → Logs → env → params에서 값이 다음과 같은 이스케이프 시퀀스를 포함하여 표시될 수 있습니다:

\u0026

이것은 단순히 &에 대한 JSON/Ruby 이스케이프입니다. 예를 들어:

error=access_denied\u0026error_subcode=cancel

은 다음과 같이 읽어야 합니다:

error=access_denied&error_subcode=cancel

데이터가 손상되거나 손실되는 것은 아니며, 순수하게 표시/직렬화 관련 사항일 뿐입니다. 하지만 csrf_detected 실패를 진단할 때 IdP의 근본적인 신호를 놓치지 않도록 알아두는 것이 좋습니다.