# OIDC csrf\_detected can mask user cancel / consent rejection - docs could clarify log inspection

**URL:** https://meta.discourse.org/t/oidc-csrf-detected-can-mask-user-cancel-consent-rejection-docs-could-clarify-log-inspection/395021
**Category:** SSO
**Tags:** openid-connect
**Created:** [February 3, 2026, 11:56am UTC](https://meta.discourse.org/t/oidc-csrf-detected-can-mask-user-cancel-consent-rejection-docs-could-clarify-log-inspection/395021 "2026-02-03T11:56:09Z")
**Posts on this page:** 2
**Page:** 1

<div class="post-metadata">

### Author: ![Ethsim2](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ethsim2/32/522255_2.png) [@Ethsim2](https://meta.discourse.org/u/Ethsim2)
#### Post date: [February 3, 2026, 11:56am UTC](https://meta.discourse.org/t/oidc-csrf-detected-can-mask-user-cancel-consent-rejection-docs-could-clarify-log-inspection/395021/1 "2026-02-03T11:56:09Z")

</div>

Continuing the discussion from [OIDC login via Discourse iOS app occasionally fails with csrf\_detected on callback](https://meta.discourse.org/t/oidc-login-via-discourse-ios-app-occasionally-fails-with-csrf-detected-on-callback/394838):

Hi all,

This is a follow-up observation linked to my earlier thread about OIDC login failures initiated from iOS in-app browsers. That discussion focused on deterministic csrf\_detected failures caused by WKWebView cookie isolation, which now seem well understood and expected.

This linked topic is about operator clarity, not a bug.

* * *

### Observation

While investigating a series of OIDC login failures, I’ve noticed that the same surface error in Discourse:

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

```

can correspond to multiple, fundamentally different upstream causes, depending on what the IdP returned.

From the application UI alone, these cases are indistinguishable. The difference is only visible by inspecting Admin → Logs → env / params.

* * *

### Examples seen in practice (Azure / Entra ID)

In addition to in-app browser cookie loss, I’ve observed callbacks where Entra ID explicitly returns structured errors such as:

**User declined consent**

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

```

**User cancelled login**

```plaintext
error=access_denied
error_subcode=cancel

```

In both cases:  
• Azure successfully identified the user  
• The user explicitly chose not to proceed (reject / cancel)  
• Discourse receives the callback  
• The flow ultimately resolves to /auth/failure?message=csrf\_detected

From Discourse’s perspective, this is correct and secure behaviour - the state cannot be validated or completed - but the underlying reason is very different from a missing session cookie.

* * *

### Why this matters for operators

Without checking the log env/params, an admin seeing repeated csrf\_detected failures may reasonably assume:  
• broken cookies  
• SameSite misconfiguration  
• mobile browser issues  
• IdP instability

…when in reality, some of those failures are simply users choosing not to consent or cancelling the Microsoft prompt.

This distinction only becomes clear if you already know to inspect the raw log payload.

* * *

### Suggestion (documentation / UX only)

I’m not suggesting any behavioural change to OmniAuth or CSRF handling.

It might be helpful if documentation or troubleshooting guidance explicitly noted that:  
• csrf\_detected can be the final error for multiple upstream IdP outcomes  
• including explicit user actions like cancel or consent rejection  
• and that admins should inspect Admin → Logs → env / params to distinguish these cases

This would make it easier for operators to:  
• correctly diagnose login failures  
• avoid unnecessary configuration changes  
• and give users accurate guidance (“you cancelled / declined consent” vs “your browser blocked cookies”).

* * *

### Context

For clarity: this is separate from the confirmed iOS in-app browser issue discussed in the linked topic. In that case, the IdP never reaches the point of user consent, whereas here the IdP explicitly reports user intent.

Both end up looking similar at the UI level unless logs are examined.

* * *

Thanks for reading - posting this mainly as a documentation clarity/data-point for others running OIDC in student-heavy environments where these cases occur frequently.

Happy to provide anonymised examples if useful.

---

<div class="post-metadata">

### Author: ![Ethsim2](https://sea3.discourse-cdn.com/meta/user_avatar/meta.discourse.org/ethsim2/32/522255_2.png) [@Ethsim2](https://meta.discourse.org/u/Ethsim2)
#### Post date: [February 5, 2026, 9:16pm UTC](https://meta.discourse.org/t/oidc-csrf-detected-can-mask-user-cancel-consent-rejection-docs-could-clarify-log-inspection/395021/2 "2026-02-05T21:16:57Z")

</div>

## Minor log-viewer gotcha (FYI)

One small thing that tripped me up while inspecting the logs:

In Admin → Logs → env → params, values may appear with escaped sequences such as:

```plaintext
\u0026

```

This is just JSON/Ruby escaping for &. For example:

```plaintext
error=access_denied\u0026error_subcode=cancel

```

should be read as:

```plaintext
error=access_denied&error_subcode=cancel

```

Nothing is being mangled or lost - it’s purely a display/serialization detail - but it’s worth knowing so operators don’t miss the underlying IdP signal when diagnosing `csrf_detected` failures.
