SAML: return to original front-end URL after login (default RelayState) + fix logout redirect fall-through
Nobody has claimed this yet.
- 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.relaystatevelocity template; if not configured (the default), an empty RelayState is sent, soDotSamlResource.processLogingetsRelayState == 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.loginconfig 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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