Skip to content

OpenConceptLab/ocl_online#535 | An expired session signs the user back in instead of ending on Keycloak's "Invalid redirect uri" - #109

Merged
snyaggarwal merged 1 commit into
mainfrom
ocl_online#535
Oct 19, 2026
Merged

snyaggarwal merged 1 commit into
mainfrom
ocl_online#535

Conversation

@snyaggarwal

@snyaggarwal snyaggarwal commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Fixes the Mapper part of OpenConceptLab/ocl_online#535.

Why

When a user's session expired, the first API call got a 401 and the forced sign-out sent them to Keycloak with post_logout_redirect_uri=https://map.openconceptlab.org/#/signin?returnTo=…, unencoded. The browser kept everything from # onwards as a fragment and never sent it, so Keycloak only received https://map.openconceptlab.org/. It rejected that and showed "We are sorry… Invalid redirect uri". This broke in 6ab7f9f, which changed /signin to /#/signin.

What changes

  • Renew before signing out. On a 401, the app first gets a new token from Keycloak with prompt=none, then reloads so every request goes out again with it. It tries this at most once every 10 minutes. The forced sign-out runs only if renewal fails, so an expired Mapper token no longer signs the user out of the other OCL apps while their Keycloak session is still alive.
  • A logout address Keycloak accepts. Every sign-out now returns to LOGIN_REDIRECT_URL, encoded (buildSSOLogoutURL). On a forced sign-out, the page to come back to waits in sessionStorage (forced_sign_out). On the next load the app goes straight to sign-in without rendering, and the normal return-page handling brings the user back to that page.
  • Hardening:
    • logoutUser runs once even when several requests are turned down together.
    • A second forced sign-out within 10 minutes stays signed out instead of looping.
    • The sign-in callback (/oidc/login, ?code=) is never redirected away.
    • Old records expire.
    • Storage errors can't stop the app from rendering.
  • Cleanup: the unused session_expired flag is removed. The new record covers it.

Verification

  • npm test: 542 pass, including 9 new tests in src/common/__tests__/ssoSession.test.js. They cover the forced and normal logout URLs, resuming only once, expiry, the loop guard, the callback skip, blocked storage and the renewal limit. ESLint is clean on the changed files.
  • Ran the app against a local mock API that answers 401 and 302s on logout:
    • Five parallel 401s produced exactly one logout, with post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A4005, then sign-in, with /map-projects/?x=1 saved as the page to return to.
    • A second rejected token within the window produced one logout, then the user stayed signed out on / with no reload loop.
    • A sign-in callback load wasn't redirected.
  • Not tested: the reload after a successful silent renewal, and a real Keycloak logout.

Before deploying

Confirm in the Keycloak admin console that the oclmap client accepts https://map.openconceptlab.org as a post-logout redirect URI. That's + (same as the redirect URIs) or an explicit entry.

Companion PRs (deployable in any order): OpenConceptLab/oclweb3#67 (same fix) and OpenConceptLab/oclapi2#946 (encodes the URLs it passes to Keycloak).

🤖 Generated with Claude Code

…k in instead of ending on Keycloak's "Invalid redirect uri"

A 401 now first renews the token silently while the Keycloak session lives,
at most once every 10 minutes. Only if that fails does the forced sign-out run.

The forced sign-out now returns from Keycloak to LOGIN_REDIRECT_URL, encoded,
instead of an unencoded /#/signin?returnTo=... address whose fragment never
reached Keycloak. The page to come back to waits in sessionStorage
(forced_sign_out), and on the next load the app goes straight to sign-in and
back to that page.

- logoutUser runs once even when several requests are turned down together
- a second forced sign-out within 10 minutes stays signed out instead of looping
- the sign-in callback is never redirected away; old records expire
- storage errors can't stop the app from rendering
- the unused session_expired flag is gone

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@snyaggarwal snyaggarwal left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: Approve

Issue complete: yes. The forced 401 path now avoids the fragment-bearing logout redirect, preserves the same-tab return location, prevents concurrent/repeated recovery loops, and leaves normal sign-out using LOGIN_REDIRECT_URL. The callback guard and storage-failure handling cover the important failure modes.

Security blockers: none. The stored return target is derived from the current same-origin location and is passed through the existing same-site return handling.

Verification: all 542 local tests passed, including the new logout URL, resume, callback, storage, and renewal-window cases. GitHub lint/test and Snyk checks pass.

(snyaggarwal • ReviewAutomation/GPT-5)

@snyaggarwal
snyaggarwal merged commit e64732d into main Oct 19, 2026
2 checks passed
@snyaggarwal
snyaggarwal deleted the ocl_online#535 branch October 19, 2026 04:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant