Skip to content

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

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 TermBrowser v3 part of OpenConceptLab/ocl_online#535. This is the same fix as OpenConceptLab/oclmap#109.

Why

TBv3's forced sign-out (a 401 from the API) built post_logout_redirect_uri=<origin>/#/signin?returnTo=…, unencoded. The browser never sends the #… part, so Keycloak receives a bare origin with a trailing slash, rejects it, and the user ends on "We are sorry… Invalid redirect uri" instead of the sign-in page.

What changes

  • Renew before signing out. On a 401, the app first gets a new token from Keycloak with prompt=none and reloads. It tries this at most once every 10 minutes. The forced sign-out runs only if renewal fails.
  • A logout address Keycloak accepts. Every sign-out returns to LOGIN_REDIRECT_URL, encoded. On a forced sign-out, the page to come back to waits in sessionStorage (forced_sign_out), and the next load goes straight to sign-in and 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 is never redirected away.
    • Old records expire.
    • Storage errors can't stop the app from rendering.
  • Cleanup: the unused session_expired sessionStorage flag is removed. The auth.session_expired locale string was already unused and is left as is.
  • Tests: adds an npm test script (node --test, as in oclmap) and src/common/__tests__/ssoSession.test.js. These are the first tests for ssoSession.js here; the file's original part is identical to oclmap's.

Verification

  • npm test: 20 pass. ESLint, including the spellcheck rule, is clean on the changed files.
  • Ran the app against a local mock API that answers 401 and 302s on logout:
    • Four parallel 401s on /orgs/CIEL/?x=1 produced exactly one logout, with post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A4011, then sign-in, with /orgs/CIEL/?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.
  • Not tested: the reload after a successful silent renewal, and a real Keycloak logout.

Before deploying

Confirm in the Keycloak admin console that the oclv3 client accepts https://app.v3.openconceptlab.org as a post-logout redirect URI.

Companion PRs (deployable in any order): OpenConceptLab/oclmap#109 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"

Same fix as the Mapper's (OpenConceptLab/oclmap#109). A 401 first renews the
token silently while the Keycloak session lives, at most once every 10
minutes. The forced sign-out returns to LOGIN_REDIRECT_URL, encoded, instead
of an unencoded /#/signin?returnTo=... address Keycloak rejects. The page to
come back to waits in sessionStorage, and the next load goes straight to
sign-in.

- 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
- adds `npm test` (node:test) with tests for src/common/ssoSession.js

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. This applies the same safe forced-sign-out flow as the Mapper: the registered base URL is used for logout, the current page is retained in same-tab storage, and callback/loop/storage failure cases are guarded. Normal sign-out remains unchanged.

Security blockers: none. The return target originates from the current same-origin location and continues through the existing same-site return handling.

Verification: all 20 local tests passed, including the new forced logout and resume coverage. GitHub ESLint and Snyk checks pass.

(snyaggarwal • ReviewAutomation/GPT-5)

@snyaggarwal
snyaggarwal merged commit cb92aeb into main Oct 19, 2026
2 checks passed
@snyaggarwal
snyaggarwal deleted the ocl_online#535 branch October 19, 2026 04:52
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