Skip to content

fix(session): use ~/ so Refresh Session keeps the /2 PathBase - #302

Open
rlorenzo wants to merge 1 commit into
mainfrom
fix/session-refresh-pathbase
Open

fix(session): use ~/ so Refresh Session keeps the /2 PathBase#302
rlorenzo wants to merge 1 commit into
mainfrom
fix/session-refresh-pathbase

Conversation

@rlorenzo

@rlorenzo rlorenzo commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

The bug

On TEST and PROD, the session timeout warning appears ("Your session will expire at 10:30 AM. Click Refresh Session to continue working.") and clicking Refresh Session does nothing. The console shows:

Uncaught (in promise) SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

Root cause

extendSession fetched a bare root-relative "/RefreshSession". TEST and PROD run VIPER 2 as an IIS sub-application under a /2 PathBase, with legacy VIPER 1 at /. So the call landed on legacy VIPER 1, which returned an HTML page. r.json() threw, and with no .catch on the chain it surfaced as an unhandled rejection, so the button looked dead.

This is pre-existing on main, Development, and every feature branch. It is not a regression. It does not reproduce locally because local development has no PathBase, so the bare path happens to resolve correctly.

Changes

  • extendSession now uses @Url.Content("~/RefreshSession"), which includes the PathBase. This matches the login method at the bottom of the same file.
  • A non-OK response is rejected rather than passed into the success handler. Previously a non-200 fed the Response object straight through, rendering "Your session has been extended to Invalid Date" while the session was never extended.
  • A failed extend now renders a role="alert" q-banner inside the dialog using the shared .error-surface treatment, and leaves the warning dialog on screen so the user can retry. Per DESIGN.md, Razor pages use q-banner with accessible classes rather than a toast, and an actionable error belongs next to the control that failed rather than in something transient. The banner is suppressed once the session actually expires, so the expired message and a stale failure message cannot show together.
  • The poll's try { ... } catch (e) { void e } was dead code: a synchronous catch cannot catch an async rejection. It is replaced with a real .catch that reschedules the next check. Beyond making the failure non-silent, this fixes a second bug: a rejected poll previously scheduled no follow-up, so session checking stopped for the remaining life of the page.

checkSessionTimeout's target is deliberately unchanged. It reads the shared Viper.dbo.SessionTimeout row through the legacy VIPER 1 CFM endpoint at the domain root, so its root-relative URL is correct.

Verification

Run locally, one at a time:

Command Result
npm run lint -- --fix web/Views/Shared/Components/SessionTimeout/Default.cshtml No issues found
npm run verify:build All build verifications passed
npm run test:backend 2708 passed, 0 failed

What is not verified locally. Local development has no PathBase. There is no UsePathBase call in Program.cs; the /2 prefix comes only from IIS sub-application hosting (see the comment at web/Classes/Scheduler/HangfireExtensions.cs:111). Locally, "/RefreshSession" and @Url.Content("~/RefreshSession") therefore render to the identical string, so a local run cannot demonstrate the fix. The fix rests on the documented behaviour of Url.Content("~/..."), which prepends Request.PathBase. Confirming the TEST symptom is resolved requires a TEST deploy: check that the rendered markup contains /2/RefreshSession and that the button extends the session.

@codecov-commenter

Copy link
Copy Markdown

Bundle Report

Bundle size has no change ✅

@rlorenzo

rlorenzo commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@codecov-commenter

codecov-commenter commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 41.88%. Comparing base (394ae2d) to head (ba86fee).

Additional details and impacted files
@@           Coverage Diff           @@
##             main     #302   +/-   ##
=======================================
  Coverage   41.88%   41.88%           
=======================================
  Files         986      986           
  Lines       49488    49488           
  Branches     5843     5843           
=======================================
  Hits        20726    20726           
  Misses      27852    27852           
  Partials      910      910           
Flag Coverage Δ
backend 39.94% <ø> (ø)
frontend 58.15% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The session timeout view now rejects failed responses, reschedules polling after errors, uses ~/RefreshSession, and records renewal failures. The dialog remains visible after failed renewal and shows login and retry guidance.

Changes

Session Timeout Handling

Layer / File(s) Summary
Handle session polling and renewal failures
web/Views/Shared/Components/SessionTimeout/Default.cshtml
Session polling rejects non-OK responses and reschedules after errors. Renewal uses ~/RefreshSession, rejects non-OK responses, records failure state, and keeps the dialog visible with login and retry guidance. Hiding the dialog clears the failure state.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the session refresh fix and explains that the application-relative path preserves the /2 PathBase.
Description check ✅ Passed The description directly explains the PathBase bug, the implementation changes, and the verification results.
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.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/session-refresh-pathbase

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.

@coderabbitai coderabbitai Bot 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.

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 `@web/Views/Shared/Components/SessionTimeout/Default.cshtml`:
- Around line 79-86: Replace the renewal failure `$q.notify` handling in the
session-timeout dialog with stored failure state, then conditionally render a
`q-banner` for that state using `class="bg-warning text-dark"` and
`role="alert"`, while preserving the existing failure message and retry/login
behavior.
🪄 Autofix

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: ASSERTIVE

Plan: Pro Plus

Run ID: 387615ea-156b-410e-9d38-cac711f62305

📥 Commits

Reviewing files that changed from the base of the PR and between 553849d and cad4fb3.

📒 Files selected for processing (1)
  • web/Views/Shared/Components/SessionTimeout/Default.cshtml

Comment thread web/Views/Shared/Components/SessionTimeout/Default.cshtml Outdated

Copilot AI 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.

Pull request overview

Fixes the “Refresh Session does nothing on TEST/PROD under /2 PathBase” issue by ensuring the session-extension request targets VIPER 2 (not legacy VIPER 1 at /) and by making the polling / refresh flows handle non-OK and failed responses without silently stalling.

Changes:

  • Update extendSession to call @Url.Content("~/RefreshSession") so the generated URL includes the IIS sub-application PathBase (e.g., /2/RefreshSession).
  • Treat non-OK HTTP responses as failures (reject) rather than flowing a Response object into the “success” handler.
  • Replace the ineffective synchronous try/catch around an async fetch chain with a real .catch that reschedules session polling; add an in-dialog error banner when session extension fails.

@rlorenzo
rlorenzo force-pushed the fix/session-refresh-pathbase branch from e05b9b3 to cd69eaa Compare August 7, 2026 23:47
@rlorenzo
rlorenzo force-pushed the fix/session-refresh-pathbase branch 2 times, most recently from 8df5d47 to 0f34d9d Compare August 10, 2026 03:26
@rlorenzo

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@rlorenzo
rlorenzo requested a lite review from Copilot August 10, 2026 05:37
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI 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.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

Suppressed comments (2)

web/Views/Shared/Components/SessionTimeout/Default.cshtml:75

  • The try/catch around clearTimeout is unnecessary: clearTimeout does not throw for an unknown/invalid timeout id, so this swallows errors without benefit and adds noise. Removing it also aligns with the earlier fix that removed ineffective try/catch usage.
                        try {
                            clearTimeout(this.sessionTimeoutCheckEventId)
                        }
                        catch(e) { void e }

web/Views/Shared/Components/SessionTimeout/Default.cshtml:63

  • Use an explicit seconds-to-milliseconds conversion instead of the magic number 300000 so it’s immediately clear this is a 5-minute retry interval and consistent with the nextCheck * 1000 usage above.
                        this.sessionTimeoutCheckEventId = window.setTimeout(this.checkSessionTimeout, 300000)

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

🤖 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 `@web/Views/Shared/Components/SessionTimeout/Default.cshtml`:
- Around line 55-64: Update the catch handler for checkSessionTimeout so a
failed poll retries promptly rather than always waiting 300000 milliseconds; cap
the retry using the last known session-expiry deadline or a short bounded
backoff that preserves near-expiry warning behavior, and add a test covering a
failed poll near expiry.
- Around line 48-55: Update the secondsUntilTimeout condition in the
session-timeout polling logic to use <= 300 so the warning is shown at exactly
five minutes remaining. Add a boundary test covering secondsUntilTimeout === 300
and verify that the warning state and nextCheck behavior are set correctly.
- Around line 44-45: Update the fetch calls in checkSessionTimeout and
extendSession to use a supported timeout via AbortController or
AbortSignal.timeout, aborting requests that exceed the timeout and routing those
failures through the existing catch handlers so polling restarts and renewal
reports sessionExtendFailed.
🪄 Autofix

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: ASSERTIVE

Plan: Pro Plus

Run ID: b30e247d-03e3-46c8-ac09-ccbe5dd740eb

📥 Commits

Reviewing files that changed from the base of the PR and between cad4fb3 and 0f34d9d.

📒 Files selected for processing (1)
  • web/Views/Shared/Components/SessionTimeout/Default.cshtml

Comment thread web/Views/Shared/Components/SessionTimeout/Default.cshtml Outdated
Comment thread web/Views/Shared/Components/SessionTimeout/Default.cshtml Outdated
Comment thread web/Views/Shared/Components/SessionTimeout/Default.cshtml
- Reject non-OK responses instead of passing the Response to the
  success handler, which rendered "extended to Invalid Date" while
  the session was never extended
- Report a failed extend in a role="alert" q-banner, and leave the
  dialog up offering both Refresh Session and Log in, so a user whose
  refresh keeps failing still has a way to re-authenticate
- Replace the poll's dead try/catch, which cannot catch an async
  rejection, with a catch that reschedules the next check
@rlorenzo
rlorenzo force-pushed the fix/session-refresh-pathbase branch from 0f34d9d to ba86fee Compare August 10, 2026 05:56
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