dotCMS / dotCMS/core

SAML: return to original front-end URL after login (default RelayState) + fix logout redirect fall-through

Open
#36,591 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Team : Maintenance
Dominant language
Java
Stars
970
Forks
486
Avg merge
3d 33m
Merged PRs (30d)
170

Description

Follow-up to #32365 / PR #36590 (which broke the infinite loop and added the default 403 "Access Denied" page + deterministic loop-guard). Two related improvements were deliberately deferred:

1. Return the user to the page they requested (default RelayState)

Today, when a front-end SAML user authenticates, the original front-end URL is lost across the IdP round-trip and the callback defaults to DotSamlConstants.DEFAULT_LOGIN_PATH = /dotAdmin/:

  • The RelayState dotCMS sends is built from the auth.relaystate velocity template; if not configured (the default), an empty RelayState is sent, so DotSamlResource.processLogin gets RelayState == null.
  • The session fallback (REDIRECT_AFTER_LOGIN) is also null on the callback because the session doesn't survive the cross-site IdP POST (SameSite cookies → fresh session).
  • With no redirect.after.login config set, it defaults to /dotAdmin/ → a front-end-only user loops (now caught by the loop-guard as a 403, but they still don't reach their page).

Proposed fix: default the RelayState to the original request URI (ORIGINAL_REQUEST) when no auth.relaystate template is configured, so the return URL survives the round-trip via the SAML RelayState param (which the IdP echoes reliably).

Security requirement: DotSamlResource.processLogin currently redirects straight to the RelayState value (RedirectUtil.sendRedirectHTML(resp, loginPath)). Before honoring an auto-derived RelayState we must add open-redirect validation — only allow relative / same-host paths — otherwise it becomes a redirect vulnerability.

Needs a small design pass + testing across IdPs (Entra ID, Okta).

Immediate workaround (no code): set redirect.after.login (or an auth.relaystate template) on the SAML app to a front-end URL so front-end users land there instead of /dotAdmin/.

2. Fix logout redirect fall-through (spurious DoesNotExistException)

DotSamlResource.logoutGet and logoutPost do not return after RedirectUtil.sendRedirectHTML(...), so after sending the redirect they fall through and always throw new DoesNotExistException("No idpConfig …") — even when the idpConfig was found and the redirect succeeded. The redirect still works (response already committed) but it logs a misleading error every logout. Fix: add return; after the redirect in both methods.

Refs: #32365, #36541, #36590

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with DotSamlResource.processLogin, logoutGet, and logoutPost, then inspect the auth.relaystate template and ORIGINAL_REQUEST/REDIRECT_AFTER_LOGIN handling. Verify the RelayState fallback is safely restricted to relative or same-host paths, add the logout returns, and test login redirects and logout behavior across the referenced IdPs.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
authentication, backend, security
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.