Skip to content

fix(sso): use query response mode so the Entra ID callback keeps its session - #3215

Merged
marevol merged 2 commits into
masterfrom
entraid-response-mode-query
Aug 10, 2026
Merged

fix(sso): use query response mode so the Entra ID callback keeps its session#3215
marevol merged 2 commits into
masterfrom
entraid-response-mode-query

Conversation

@marevol

@marevol marevol commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Problem

getAuthUrl() asked Entra ID for response_mode=form_post:

+ "/oauth2/v2.0/authorize?response_type=code&scope=...&response_mode=form_post&redirect_uri="

so the authorization response comes back as a cross-site POST from
login.microsoftonline.com.

Fess sets SameSite=Lax on the session cookie — tomcat.sameSiteCookies = lax in
tomcat_config.properties, applied to the Tomcat CookieProcessor in FessBoot. A Lax
cookie is not sent on a cross-site POST. And because the attribute is set explicitly,
Chrome's two-minute "Lax + POST" grace period does not apply either — that grace period only
covers cookies with no SameSite attribute at all.

So on the callback:

  1. request.getSession(false) returns null
  2. the stored state / nonce are unreachable, so the response can never be validated
  3. getLoginCredential() falls through to ActionResponseCredential → redirect to the
    authorization endpoint → the loop repeats forever

The default value of tomcat.sameSiteCookies was only added recently (#3136). Before that the
attribute was unset, so the grace period kept form_post working — this is a regression rather
than a long-standing bug.

Fix

Ask for response_mode=query. With response_type=code the response is then a top-level
GET navigation, which does carry a Lax cookie. query is also the default response mode for
the authorization code flow, so nothing about the app registration changes — response_mode is
chosen by us in the authorization request, not configured on the Entra ID side.

containsAuthenticationData() now accepts GET as well as POST, so a deployment that already
set tomcat.sameSiteCookies=none and relies on form_post keeps working.

Stop bouncing a session-less callback back to the IdP. That is what turned a missing cookie
into an infinite redirect. It now logs a warning naming the likely cause and returns null, so
SsoAction shows the SSO login error and goes to the login page once.

The comment on tomcat.sameSiteCookies said "Lax keeps OAuth/OIDC redirects working", which is
true for redirect-based callbacks but not for POST ones. Reworded to say which setups need
none — SAML's HTTP-POST binding has the same constraint.

On putting the code in the query string

The authorization code is single-use, short-lived, and cannot be redeemed without the client
secret, which is why query is the standard mode for a confidential client. #3213 stops the code
being written to the debug log.

Tests

EntraIdAuthenticator's unit test class, 6 new tests:

  • test_getAuthUrl_requestsQueryResponseMode / ...OnV1Endpoint — asserts response_mode=query,
    no form_post, and that response_type, state and nonce are unchanged
  • test_containsAuthenticationData_acceptsQueryModeCallback — GET with code
  • test_containsAuthenticationData_acceptsFormPostCallback — POST still accepted
  • test_containsAuthenticationData_acceptsErrorCallback — GET with error
  • test_containsAuthenticationData_ignoresRequestWithoutArtifacts — a plain visit to /sso still
    starts a fresh login

All 4 behavioural ones failed before the change.

Tests run: 108, Failures: 0, Errors: 0, Skipped: 0

(the whole org.codelibs.fess.sso package, including the OIDC and SAML authenticator tests)

…session

getAuthUrl() asked Entra ID for response_mode=form_post, so the authorization
response came back as a cross-site POST. Fess sets SameSite=Lax on the session
cookie (tomcat.sameSiteCookies, applied in FessBoot), and a Lax cookie is not
sent on a cross-site POST. Because the attribute is set explicitly, Chrome's
two-minute "Lax + POST" grace period does not apply either -- that grace period
only covers cookies with no SameSite attribute at all.

getSession(false) therefore returned null on the callback, the stored state was
unreachable, and getLoginCredential() fell through to redirecting back to the
authorization endpoint, looping forever.

The default value of tomcat.sameSiteCookies was only introduced recently; before
that the attribute was unset and the grace period kept form_post working.

Ask for response_mode=query instead. With response_type=code the response is
then a top-level GET navigation, which does carry a Lax cookie, and query is the
default response mode for the authorization code flow. containsAuthenticationData()
accepts GET as well as POST, so a deployment that already set
tomcat.sameSiteCookies=none and relies on form_post keeps working.

Also stop bouncing a callback that arrived without a session back to the
authorization endpoint. That is what turned a missing cookie into an infinite
redirect; it now logs a warning and falls through to the login page.
Covers the loop-prevention added in the previous commit: without it,
getLoginCredential() returns an ActionResponseCredential that redirects back to
the authorization endpoint, and the callback arrives without a session again.
@marevol
marevol force-pushed the entraid-response-mode-query branch from de48505 to 22f3532 Compare August 10, 2026 01:06
@marevol
marevol merged commit 7959ac5 into master Aug 10, 2026
2 checks passed
marevol added a commit that referenced this pull request Aug 10, 2026
java-saml exposes Saml2Settings#getSecurityWarnings(), which flags weak but
non-fatal configuration. Nothing called it, so a deployment running on the
permissive defaults had no way to learn about it except by reading the code.
getSettings() now logs the warnings at WARN. Since the settings are rebuilt
on every request, the warnings are repeated only when they change, so editing
the configuration from the admin UI re-reports the new state without flooding
the log in between.

With the shipped defaults the message is:

  Insecure SAML settings: deprecated_signature_algorithms_not_rejected,
  assertions_and_messages_not_required_signed. See the SAML SSO documentation
  for the recommended values.

The tomcat_config.properties note this change originally carried about
SameSite and the SAML HTTP-POST binding is dropped: #3215 landed the same
guidance on master, covering both SAML and Entra ID form_post.
marevol added a commit that referenced this pull request Aug 10, 2026
java-saml exposes Saml2Settings#getSecurityWarnings(), which flags weak but
non-fatal configuration. Nothing called it, so a deployment running on the
permissive defaults had no way to learn about it except by reading the code.
getSettings() now logs the warnings at WARN. Since the settings are rebuilt
on every request, the warnings are repeated only when they change, so editing
the configuration from the admin UI re-reports the new state without flooding
the log in between.

With the shipped defaults the message is:

  Insecure SAML settings: deprecated_signature_algorithms_not_rejected,
  assertions_and_messages_not_required_signed. See the SAML SSO documentation
  for the recommended values.

The tomcat_config.properties note this change originally carried about
SameSite and the SAML HTTP-POST binding is dropped: #3215 landed the same
guidance on master, covering both SAML and Entra ID form_post.
marevol added a commit that referenced this pull request Aug 10, 2026
java-saml exposes Saml2Settings#getSecurityWarnings(), which flags weak but
non-fatal configuration. Nothing called it, so a deployment running on the
permissive defaults had no way to learn about it except by reading the code.
getSettings() now logs the warnings at WARN. Since the settings are rebuilt
on every request, the warnings are repeated only when they change, so editing
the configuration from the admin UI re-reports the new state without flooding
the log in between.

With the shipped defaults the message is:

  Insecure SAML settings: deprecated_signature_algorithms_not_rejected,
  assertions_and_messages_not_required_signed. See the SAML SSO documentation
  for the recommended values.

The tomcat_config.properties note this change originally carried about
SameSite and the SAML HTTP-POST binding is dropped: #3215 landed the same
guidance on master, covering both SAML and Entra ID form_post.
marevol added a commit that referenced this pull request Aug 10, 2026
java-saml exposes Saml2Settings#getSecurityWarnings(), which flags weak but
non-fatal configuration. Nothing called it, so a deployment running on the
permissive defaults had no way to learn about it except by reading the code.
getSettings() now logs the warnings at WARN. Since the settings are rebuilt
on every request, the warnings are repeated only when they change, so editing
the configuration from the admin UI re-reports the new state without flooding
the log in between.

With the shipped defaults the message is:

  Insecure SAML settings: deprecated_signature_algorithms_not_rejected,
  assertions_and_messages_not_required_signed. See the SAML SSO documentation
  for the recommended values.

The tomcat_config.properties note this change originally carried about
SameSite and the SAML HTTP-POST binding is dropped: #3215 landed the same
guidance on master, covering both SAML and Entra ID form_post.
marevol added a commit that referenced this pull request Aug 10, 2026
java-saml exposes Saml2Settings#getSecurityWarnings(), which flags weak but
non-fatal configuration. Nothing called it, so a deployment running on the
permissive defaults had no way to learn about it except by reading the code.
getSettings() now logs the warnings at WARN. Since the settings are rebuilt
on every request, the warnings are repeated only when they change, so editing
the configuration from the admin UI re-reports the new state without flooding
the log in between.

With the shipped defaults the message is:

  Insecure SAML settings: deprecated_signature_algorithms_not_rejected,
  assertions_and_messages_not_required_signed. See the SAML SSO documentation
  for the recommended values.

The tomcat_config.properties note this change originally carried about
SameSite and the SAML HTTP-POST binding is dropped: #3215 landed the same
guidance on master, covering both SAML and Entra ID form_post.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant