Skip to content

NP-51314 Fix redirect on public pages when token expires#8568

Merged
iBuzza merged 8 commits into
developfrom
NP-51314-sessions-expired-redirect-bug
Jun 11, 2026
Merged

NP-51314 Fix redirect on public pages when token expires#8568
iBuzza merged 8 commits into
developfrom
NP-51314-sessions-expired-redirect-bug

Conversation

@iBuzza

@iBuzza iBuzza commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Description

Link to Jira issue: NP-51314

When a user's session token expired while they were viewing a public page (e.g. a published registration landing page, search, a research profile, a project), the app would redirect them to the SignedOut page. This was disruptive because those pages don't require authentication — the user was kicked off content they were allowed to see.

The root cause was twofold:

  1. getAccessToken unconditionally redirected to SignedOut whenever no valid token was present, even on public pages.
  2. The registration landing page gated its curator branch on the Redux user (populated once at startup), which stays "logged in" after the token silently expires.

That triggered an authenticated tickets request that failed.

After this change:

  • getAccessToken skips the SignedOut redirect when the current page is public, returning null so the request proceeds unauthenticated instead. On protected pages the redirect still happens as before.
  • The public-page check (isPublicPage) was extracted from Logout.tsx into urlPaths.ts (deduped) and rewritten to match against actual route templates via matchPath. This correctly keeps protected routes like /registration (new), /projects/new, and /projects/:id/edit non-public — broad prefix/suffix matching had wrongly classified them as public — and also handles paths with query strings (e.g. /search?query=...).
  • RegistrationLandingPage now gates the curator branch on a live token via the new useIsAuthenticated hook instead of the possibly-stale Redux user, so an expired token no longer fires a failing authenticated tickets request on this public page.

A dev-only simulateExpiredToken switch was added to reproduce an expired session in the GUI without touching Cognito. It is compiled out of production builds via import.meta.env.DEV.

How to test

Affected part of the application: a published registration landing page, e.g. /registration/ (and other public pages: /search, /research-profile/, /projects/).

This requires the dev-only token-expiry simulation, so run the app locally (npm run dev / npm start).

Reproduce the bug (without this branch) / verify the fix (with it):

  1. Log in as a user with a curator role.
  2. Navigate to a published registration landing page (/registration/).
  3. Open the browser console and simulate an expired token:
    localStorage.setItem('simulateExpiredToken', 'true');
  4. Reload the page (or navigate to another public page).
    - Expected (this branch): you stay on the public page and can read its content. No redirect to SignedOut, and no failing authenticated tickets request in the
    network tab.
  5. Now navigate to a protected page (e.g. /my-page, /tasks, /projects/new, or a registration wizard /registration//edit).
    - Expected: you are redirected to SignedOut (with a redirect path so re-login returns you there).
  6. Restore the normal state:
    localStorage.removeItem('simulateExpiredToken');
    - Expected: authenticated behaviour returns to normal (curator actions/tickets load again on the landing page).

Checklist

Ensure that the changes are aligned with the expectations of PO/designer before marking the PR as ready for review.

Also ensure that the following criterias are met:

  • The changes are working as expected
  • The changes are tested OK for different screen sizes
  • The changes are tested OK for a11y
  • Interactive elements have data-testids
  • I have done a QA of my own changes

Note: some of these criterias may not always be relevant, and can simply be marked as completed.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 38d6c16e-6d7c-49ac-b752-1017f48fdba9

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title directly and clearly describes the main change: fixing redirect behaviour on public pages when a token expires, which aligns with the primary objective of the changeset.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description comprehensively addresses the problem, solution, and testing instructions, with all required sections properly filled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch NP-51314-sessions-expired-redirect-bug

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@iBuzza iBuzza changed the title check if page is public before triggering redirect NP-51314 Fix redirect on public pages when token expires Jun 5, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/utils/urlPaths.ts`:
- Around line 103-110: isPublicPage is using broad startsWith/endsWith checks
that mark protected routes (e.g. UrlPathTemplate.RegistrationNew, /projects/new,
/projects/:id/edit) as public; update the logic in isPublicPage to use stricter
matching: for UrlPathTemplate.ResearchProfileRoot and
UrlPathTemplate.ProjectsRoot only treat the exact root or exactly one-segment
public views (e.g. /projects or /projects/:identifier) as public (reject any
further segments like "new" or "edit"), and replace the
registrationLandingPageParts startsWith/endsWith check with an explicit check
for the allowed registration landing path(s) rather than a prefix/suffix
exclusion; locate isPublicPage and modify the predicate to use exact equality or
a one-segment regex/pattern for those symbols
(UrlPathTemplate.ResearchProfileRoot, UrlPathTemplate.ProjectsRoot,
registrationLandingPageParts) to prevent protected subroutes from being
classified public.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a4f361c9-e05b-4162-bfe0-dec43298cdb3

📥 Commits

Reviewing files that changed from the base of the PR and between fe60f73 and 3ab462d.

📒 Files selected for processing (6)
  • src/api/authApi.ts
  • src/api/hooks/useIsAuthenticated.ts
  • src/components/registration-landing-page/RegistrationLandingPage.tsx
  • src/layout/Logout.tsx
  • src/utils/constants.ts
  • src/utils/urlPaths.ts

Comment thread src/utils/urlPaths.ts Outdated
coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 5, 2026
@iBuzza iBuzza marked this pull request as ready for review June 5, 2026 13:01
Comment thread src/api/authApi.ts
Comment on lines +25 to +26
// Don't redirect to SignedOut when viewing public content (e.g. a registration landing page);
// the request simply proceeds unauthenticated instead of kicking the user off a public page.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ikke så nyttig kommentar i mine øyne

Suggested change
// Don't redirect to SignedOut when viewing public content (e.g. a registration landing page);
// the request simply proceeds unauthenticated instead of kicking the user off a public page.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Jeg liker kommentaren, den sier noe om at det løser et faktisk problem vi har hatt, heller enn at det er "overbeskyttende" som det kan være lett å tenke

Comment thread src/api/authApi.ts
Comment on lines +50 to +52
// Keep the simulated-expired state consistent across full-page reloads (e.g. after a redirect to
// SignedOut): without this the Redux `user` would be repopulated from the still-valid real token,
// causing SignedOutPage to bounce back to Root in a redirect loop.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ikke så nyttig kommentar i mine øyne. Blir fort stale denne om man endrer på Redux user etc.

Suggested change
// Keep the simulated-expired state consistent across full-page reloads (e.g. after a redirect to
// SignedOut): without this the Redux `user` would be repopulated from the still-valid real token,
// causing SignedOutPage to bounce back to Root in a redirect loop.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Jeg syns denne også er nyttig. Det fungerer som dokumentasjon for featuren som simulateExpiredToken faktisk er. Mulig den (også?) skulle vært dokumentert på en egen side da.

Comment on lines +45 to +48
// Gate the curator branch on a live token, not just the (possibly stale) Redux user, so that an
// expired token doesn't trigger a failing authenticated tickets request on this public page.
const isAuthenticatedQuery = useIsAuthenticated();
const isTicketCurator = hasTicketCuratorRole(user) && isAuthenticatedQuery.data === true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Burde dette vært bakt inn i hjelpefunksjonen? Er det noen ganger man ønsker å vite om en bruker er kurator, men ikke verifisere at bruker er autentisert?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Jeg tenker de er greie å ha separate, som her. Jeg har ikke satt meg veldig inn i use casene, men jeg tror ikke de alltid trenger å sjekkes sammen, siden mange sider er for autentiserte brukere i sin helhet.

Comment thread src/utils/constants.ts Outdated
Beta = 'beta',
EnvironmentBanner = 'environmentBanner',
RedirectPath = 'redirectPath',
/** Dev-only: when set to 'true', the app behaves as if the session token has expired. Compiled out of production builds. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
/** Dev-only: when set to 'true', the app behaves as if the session token has expired. Compiled out of production builds. */

denektenina
denektenina previously approved these changes Jun 9, 2026

@denektenina denektenina left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approvet men les over spesielt kommentaren min om mulig misforståelse av hva useIsAuthenticated gjør.

Comment thread src/utils/urlPaths.ts
Comment on lines +123 to +130
export const isPublicPage = (path: string) => {
const pathname = path.split('?')[0];
if (protectedPageTemplates.some((template) => matchPath(template, pathname))) {
return false;
}
return publicPageTemplates.some((template) => matchPath(template, pathname));
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Kunne vært lurt med tester på isPublicPage siden det kan være vanskelig å se av koden hvordan den forholder seg til ulike routes, og dessuten for å sikre at vi ikke endrer funksjonalitet ved en feil hvis den endres senere.

Comment thread src/api/hooks/useIsAuthenticated.ts Outdated
Comment on lines +4 to +9
/**
* Reactively reports whether the current session has a valid (non-expired) token.
* Unlike the Redux `user`, which is populated once at startup and goes stale when the token
* expires, this reflects the live token state and can be used to gate authenticated requests.
*/
export const useIsAuthenticated = () =>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Jeg er litt bekymret for at navn og kommentar gjør det lett å misforstå hva denne hooken gjør. Hvis man ser noe slikt i koden:
const isAuthenticatedQuery = useIsAuthenticated(); vil man kanskje tro at man gjør en "live" sjekk på nåværende status på tokenet, men av det jeg har lest virker det som den bare gjør dette kallet ved mounting (første render) og når siden får fokus igjen.

For denne bugfixen det er bra nok funksjonalitet, men kanskje kommentaren burde endres til noe ala:
"Checks whether the current session has a valid (non-expired) token. Updates at mount-time and on window focus. Can be used to gate authenticated requests."

eller

"Gate the curator branch on the token state at mount time, not just the (possibly stale) Redux user — this prevents a failing authenticated request when navigating to a public page with an expired token."

eller noe annet bedre som du kommer på.

Comment thread src/api/authApi.ts
Comment on lines +25 to +26
// Don't redirect to SignedOut when viewing public content (e.g. a registration landing page);
// the request simply proceeds unauthenticated instead of kicking the user off a public page.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Jeg liker kommentaren, den sier noe om at det løser et faktisk problem vi har hatt, heller enn at det er "overbeskyttende" som det kan være lett å tenke

Comment thread src/api/authApi.ts
Comment on lines +50 to +52
// Keep the simulated-expired state consistent across full-page reloads (e.g. after a redirect to
// SignedOut): without this the Redux `user` would be repopulated from the still-valid real token,
// causing SignedOutPage to bounce back to Root in a redirect loop.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Jeg syns denne også er nyttig. Det fungerer som dokumentasjon for featuren som simulateExpiredToken faktisk er. Mulig den (også?) skulle vært dokumentert på en egen side da.

Comment on lines +45 to +48
// Gate the curator branch on a live token, not just the (possibly stale) Redux user, so that an
// expired token doesn't trigger a failing authenticated tickets request on this public page.
const isAuthenticatedQuery = useIsAuthenticated();
const isTicketCurator = hasTicketCuratorRole(user) && isAuthenticatedQuery.data === true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Jeg tenker de er greie å ha separate, som her. Jeg har ikke satt meg veldig inn i use casene, men jeg tror ikke de alltid trenger å sjekkes sammen, siden mange sider er for autentiserte brukere i sin helhet.

@iBuzza iBuzza dismissed stale reviews from denektenina and coderabbitai[bot] via f88f389 June 10, 2026 06:16

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🚀

@iBuzza iBuzza merged commit 8525241 into develop Jun 11, 2026
7 checks passed
@iBuzza iBuzza deleted the NP-51314-sessions-expired-redirect-bug branch June 11, 2026 05:42
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.

3 participants