Skip to content

fix(server-app): apply configured idle locking - #1418

Merged
khaira777 merged 1 commit into
mainfrom
codex/audit-server-app-idle
Oct 31, 2026
Merged

khaira777 merged 1 commit into
mainfrom
codex/audit-server-app-idle

Conversation

@khaira777

Copy link
Copy Markdown
Contributor

Summary

Addresses release audit finding F-6 for Server App standalone workstation security:

  • Applies the tenant's configured idle timeout duration to the Server App standalone session, automatically locking or logging out inactive server sessions.
  • Ensures the session locks reliably even if the network logout call fails or the server is momentarily disconnected.

Changes

  • frontend/src/app/server-standalone/page.tsx: Add inactivity listeners (pointer, keydown, touch) and timer bound to configured idle minutes; lock UI to PIN unlock screen upon timeout expiration.
  • main/server-app.ts: Provide configured idle duration to client and validate session freshness.
  • frontend/e2e/server-app-ordering.spec.ts: Add E2E coverage for Server App idle locking even on simulated logout failure.
  • tests/server-app-server-role.test.ts: Assert idle lock duration exposure and server-role contract.
  • frontend/src/locales/*.json: Add translation keys across all 24 locales.

Stack

Verification

  • npm run test:server-app-server-role: Passed
  • npm run test:server-app-station-routing: Passed
  • npm run test:pin-quick-switch: Passed
  • npm run test:kds-contract: Passed
  • Playwright Server App idle lock test (e2e/server-app-ordering.spec.ts:8): Passed
  • Full suite verification: npm test (all 229 suites passed), npm run test:e2e:browser (all 339 tests passed), npm run test:e2e:electron (passed)

@coderabbitai

coderabbitai Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

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

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
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Apply configured idle locking to Server App sessions

🐞 Bug fix 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Lock inactive Server App sessions after the tenant’s configured timeout, even when logout fails.
• Preserve an unfinished draft for the same staff member while preventing another from seeing it.
• Guard in-flight ordering against session changes and test the authenticated setting and offline
 lock.
Diagram

graph TD
  Setting["Tenant idle setting"] --> Proxy["Authenticated proxy"] --> Watch["Idle watchdog"] --> Lock["Local lock"] --> SignIn["Staff sign in"] --> Draft["Owned draft"]
  Lock --> Logout["Logout request"]
Loading
High-Level Assessment

Keep the client-side lock with best-effort server logout: a server-only timeout would not reliably hide workstation data while disconnected. The restricted settings route also avoids exposing general settings through the Server App.

Files changed (28) +237 / -16

Bug fix (2) +177 / -16
page.tsxLock inactive sessions and protect draft ownership +174/-16

Lock inactive sessions and protect draft ownership

• Fetches the configured timeout, tracks activity, and clears local authentication and sensitive UI state before attempting logout. Preserves drafts for the same user on reauthentication and guards asynchronous ordering, loading, and printing against session changes.

frontend/src/app/server-standalone/page.tsx

server-app.tsExpose the authenticated idle-timeout setting +3/-0

Expose the authenticated idle-timeout setting

• Adds a rate-limited Server App route that forwards the idle-timeout setting to the main API without proxying general settings.

main/server-app.ts

Tests (2) +36 / -0
server-app-ordering.spec.tsTest idle locking when logout fails +23/-0

Test idle locking when logout fails

• Uses a one-minute configured timeout and a simulated logout failure to verify activity resets the timer, the sign-in screen appears, and the local token is removed.

frontend/e2e/server-app-ordering.spec.ts

server-app-server-role.test.tsTest idle-setting access by Server App roles +13/-0

Test idle-setting access by Server App roles

• Verifies that server, manager, and owner tokens can read the configured timeout, unauthenticated requests are rejected, and unrelated settings remain unavailable.

tests/server-app-server-role.test.ts

Documentation (24) +24 / -0
ar.jsonTranslate discarded-draft notice in Arabic +1/-0

Translate discarded-draft notice in Arabic

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/ar.json

bn.jsonTranslate discarded-draft notice in Bengali +1/-0

Translate discarded-draft notice in Bengali

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/bn.json

de.jsonTranslate discarded-draft notice in German +1/-0

Translate discarded-draft notice in German

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/de.json

en.jsonAdd English discarded-draft notice +1/-0

Add English discarded-draft notice

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/en.json

es.jsonTranslate discarded-draft notice in Spanish +1/-0

Translate discarded-draft notice in Spanish

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/es.json

fa.jsonTranslate discarded-draft notice in Persian +1/-0

Translate discarded-draft notice in Persian

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/fa.json

fil.jsonTranslate discarded-draft notice in Filipino +1/-0

Translate discarded-draft notice in Filipino

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/fil.json

fr.jsonTranslate discarded-draft notice in French +1/-0

Translate discarded-draft notice in French

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/fr.json

hi.jsonTranslate discarded-draft notice in Hindi +1/-0

Translate discarded-draft notice in Hindi

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/hi.json

id.jsonTranslate discarded-draft notice in Indonesian +1/-0

Translate discarded-draft notice in Indonesian

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/id.json

it.jsonTranslate discarded-draft notice in Italian +1/-0

Translate discarded-draft notice in Italian

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/it.json

ja.jsonTranslate discarded-draft notice in Japanese +1/-0

Translate discarded-draft notice in Japanese

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/ja.json

ko.jsonTranslate discarded-draft notice in Korean +1/-0

Translate discarded-draft notice in Korean

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/ko.json

ne.jsonTranslate discarded-draft notice in Nepali +1/-0

Translate discarded-draft notice in Nepali

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/ne.json

nl.jsonTranslate discarded-draft notice in Dutch +1/-0

Translate discarded-draft notice in Dutch

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/nl.json

pt.jsonTranslate discarded-draft notice in Portuguese +1/-0

Translate discarded-draft notice in Portuguese

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/pt.json

ru.jsonTranslate discarded-draft notice in Russian +1/-0

Translate discarded-draft notice in Russian

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/ru.json

sq.jsonTranslate discarded-draft notice in Albanian +1/-0

Translate discarded-draft notice in Albanian

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/sq.json

th.jsonTranslate discarded-draft notice in Thai +1/-0

Translate discarded-draft notice in Thai

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/th.json

tr.jsonTranslate discarded-draft notice in Turkish +1/-0

Translate discarded-draft notice in Turkish

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/tr.json

ur.jsonTranslate discarded-draft notice in Urdu +1/-0

Translate discarded-draft notice in Urdu

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/ur.json

vi.jsonTranslate discarded-draft notice in Vietnamese +1/-0

Translate discarded-draft notice in Vietnamese

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/vi.json

zh-tw.jsonTranslate discarded-draft notice in Traditional Chinese +1/-0

Translate discarded-draft notice in Traditional Chinese

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/zh-tw.json

zh.jsonTranslate discarded-draft notice in Simplified Chinese +1/-0

Translate discarded-draft notice in Simplified Chinese

• Adds the notice shown when another staff member signs in after a draft was suspended.

frontend/src/lib/i18n/messages/zh.json

@qodo-code-review

qodo-code-review Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (4) 📘 Rule violations (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. A failed settings fetch leaves sessions open 🐞 Bug ⛨ Security
Description
refresh ignores a failed idle-setting request while autoLockMinutes remains at its initial value
of zero, so the timer effect installs no lock. If that request fails while the page stays focused,
there is no retry or active inactivity listener, even when the tenant has configured a nonzero
timeout.
Code

frontend/src/app/server-standalone/page.tsx[R419-423]

+      api.get('/api/settings/auto_lock_minutes').then((response) => {
+        if (cancelled || session !== authSessionSeqRef.current || token !== localStorage.getItem(TOKEN_KEY)) return;
+        const minutes = Number(response.data?.setting?.value);
+        if ([0, 1, 5, 15, 30].includes(minutes)) setAutoLockMinutes(minutes);
+      }).catch(() => {});
Relevance

●●● Strong

Recent accepted precedent covers failed auto-lock settings loads leaving timers inactive.

PR-#1392

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The setting starts at zero, only a successful response changes it, and the timer effect returns
immediately for zero. The only subsequent settings request is on window focus.

frontend/src/app/server-standalone/page.tsx[250-252]
frontend/src/app/server-standalone/page.tsx[413-433]
main/routes/settings.ts[1778-1803]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A failed initial settings request is treated like an explicit zero-minute policy, leaving an authenticated session without an idle lock.
## Fix Focus Areas
- frontend/src/app/server-standalone/page.tsx[413-429]
- frontend/src/app/server-standalone/page.tsx[431-491]
## Recommended Fix
Represent an unresolved policy separately from the configured zero value. On initial fetch failure, lock or otherwise block the authenticated session until the policy can be verified; retry the fetch so a transient outage can recover. Clear any prior policy at session boundaries and retain a known policy during later transient refresh failures.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗



Remediation recommended

2. Next staff member is told a draft was discarded when none existed ✓ Resolved
Description
The idle lock() always stores an object in suspendedDraftRef.current, even when the draft, notes
and customer fields are all empty, and handleLogin shows draftDiscardedOnUserChange whenever a
suspended object exists and belongs to someone else. So every time a different staff member signs in
after an idle lock, they see "The previous staff member’s draft was discarded", even if nothing was
lost, and that makes the message useless when a real draft is discarded.
Code

frontend/src/app/server-standalone/page.tsx[R734-737]

+      } else if (suspended) {
+        suspendedDraftRef.current = null;
+        toast(t('draftDiscardedOnUserChange'));
+      }
Relevance

●●● Strong

This is a deterministic local fix preventing a false discard message when no draft exists.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The lock path saves the suspended snapshot without checking whether there is anything in it. The
login path shows the toast for any non-matching suspended snapshot. A normal shift handover after an
idle timeout with an empty cart therefore shows a false 'discarded' notice.

frontend/src/app/server-standalone/page.tsx[436-441]
frontend/src/app/server-standalone/page.tsx[725-737]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
After an idle lock, `suspendedDraftRef` always holds a snapshot, even when it is empty. When a different user then signs in, they always get the `draftDiscardedOnUserChange` toast, even though nothing was discarded.

## Fix Focus Areas
- frontend/src/app/server-standalone/page.tsx[436-441]
- frontend/src/app/server-standalone/page.tsx[725-737]

## Recommended Fix
In `lock()`, set `suspendedDraftRef.current` only when the live draft has content (`draft.length > 0`, or non-empty `orderNotes`, `customerName` or `customerPhone`) or when `sendAttemptRef.current` is non-null; otherwise set it to null. In `handleLogin`, still clear the snapshot on a user change, but show the toast only when the discarded snapshot had content.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. An idle lock can duplicate sent items 🐞 Bug ≡ Correctness
Description
lock copies liveDraftRef, but that ref receives successful-send draft changes only in a later
React effect. If the idle timer runs after a send clears its attempt and schedules draft removal but
before the ref updates, re-login restores the already-submitted lines without their idempotency
nonce, allowing a second send to submit them again.
Code

frontend/src/app/server-standalone/page.tsx[440]

+      suspendedDraftRef.current = { ...liveDraftRef.current, ownerId: userId, attempt: sendAttemptRef.current };
Relevance

●●● Strong

Recent accepted precedents prioritize preserving send attempts and preventing duplicate submissions
across session changes.

PR-#1410
PR-#1151

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The snapshot ref updates in an effect, whereas send completion clears the attempt and schedules
draft removal separately. Lock snapshots the ref, and same-user login restores its draft and attempt
for the next send.

frontend/src/app/server-standalone/page.tsx[250-260]
frontend/src/app/server-standalone/page.tsx[436-446]
frontend/src/app/server-standalone/page.tsx[725-733]
frontend/src/app/server-standalone/page.tsx[1084-1095]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A lock between successful-send state updates and the effect that updates the draft ref can restore items that were already submitted.
## Fix Focus Areas
- frontend/src/app/server-standalone/page.tsx[253-260]
- frontend/src/app/server-standalone/page.tsx[436-446]
- frontend/src/app/server-standalone/page.tsx[1084-1095]
## Recommended Fix
Make the draft snapshot used by locking reflect send acknowledgments synchronously with draft reconciliation. Do not restore acknowledged lines as a fresh send attempt; preserve the nonce for any submission whose outcome is still uncertain.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


4. Re-login can leave ordering disabled 🐞 Bug ☼ Reliability
Description
lock clears the loaded-table reference while an existing sendDraft call may still be in flight,
and same-user login restores the selected table before that call settles. If the login-triggered
order load finishes during the send, it discards its result; the send's finally does not retry the
load, leaving the restored draft unable to pass the send gate until a manual refresh or table
change.
Code

frontend/src/app/server-standalone/page.tsx[R447-449]

+      selectedTableIdRef.current = '';
+      orderLoadedTableIdRef.current = '';
+      activeUserIdRef.current = null;
Relevance

●● Moderate

The race is plausible and concrete, but no closely matching accepted or rejected precedent was
found.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Lock clears the loaded-table ref; login restores the table and triggers a load whose callback
returns during an active send. Send completion changes only the sending flags, while both the send
handler and button require the table to be marked loaded.

frontend/src/app/server-standalone/page.tsx[441-460]
frontend/src/app/server-standalone/page.tsx[511-545]
frontend/src/app/server-standalone/page.tsx[725-739]
frontend/src/app/server-standalone/page.tsx[972-977]
frontend/src/app/server-standalone/page.tsx[1124-1127]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A user who signs back in before an old send finishes can have every login-triggered order load discarded, leaving the restored draft unsendable.
## Fix Focus Areas
- frontend/src/app/server-standalone/page.tsx[441-460]
- frontend/src/app/server-standalone/page.tsx[725-739]
- frontend/src/app/server-standalone/page.tsx[1124-1127]
## Recommended Fix
When the in-flight send settles, arrange a fresh load for the currently authenticated user's selected table if its order-loaded reference is still empty. Guard that reload against another session change, and ensure a load discarded during sending is retried rather than permanently forgotten.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


View medium (1)
5. Other tabs keep showing locked sessions 🐞 Bug ⛨ Security
Description
lock removes the shared token but clears authenticated React state only in the tab whose timer
expired. Another open Server App tab has no lock or storage-event handler, so its locally held user,
order, and customer data remain visible after that session is locked.
Code

frontend/src/app/server-standalone/page.tsx[R445-450]

+      localStorage.removeItem(TOKEN_KEY);
+      sendAttemptRef.current = null;
+      selectedTableIdRef.current = '';
+      orderLoadedTableIdRef.current = '';
+      activeUserIdRef.current = null;
+      setUser(null);
Relevance

●● Moderate

Cross-tab session visibility is a serious security concern, but no closely matching team precedent
was found.

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The lock mutates local component state and shared storage, while rendering depends on each tab's
component-local user state. The page has no listener that clears that state when another tab removes
the token.

frontend/src/app/server-standalone/page.tsx[201-218]
frontend/src/app/server-standalone/page.tsx[431-491]
frontend/src/app/server-standalone/page.tsx[1159-1160]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Removing the token in one tab does not clear authenticated content already rendered in another tab.
## Fix Focus Areas
- frontend/src/app/server-standalone/page.tsx[431-491]
- frontend/src/app/server-standalone/page.tsx[1159-1160]
## Recommended Fix
Handle token removal or an explicit lock broadcast in every Server App tab. In each tab, invalidate pending work and clear the authenticated UI state without relying on the next API request or that tab's own idle timer.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Dismiss ↗ | View ↗


Grey Divider

Context sources
✅ Linked PRs:
  🌐 PR #1417
✅ Compliance rules (platform): 5 rules
✅ Cross-repo context — repo relationships
Review mode: Auto: 🧠 Deep: Security-sensitive idle locking spans authentication, concurrency, UI state, proxying, and tests.

Grey Divider

Tip of the day
💡 Did you know, you can keep summaries lean with Findings visible per group, which tucks the rest behind a View link

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

@khaira777
khaira777 force-pushed the codex/audit-payment-correction branch from 408bb22 to d8f53c0 Compare October 31, 2026 21:26
@khaira777
khaira777 force-pushed the codex/audit-server-app-idle branch from 45bd10b to cc2c44e Compare October 31, 2026 21:26
@khaira777
khaira777 force-pushed the codex/audit-payment-correction branch from d8f53c0 to 7a325df Compare October 31, 2026 23:17
@khaira777
khaira777 changed the base branch from codex/audit-payment-correction to main October 31, 2026 23:17
@khaira777
khaira777 force-pushed the codex/audit-server-app-idle branch from cc2c44e to 7187d1d Compare October 31, 2026 23:30
@khaira777
khaira777 merged commit 34d63c4 into main Oct 31, 2026
23 checks passed
@khaira777
khaira777 deleted the codex/audit-server-app-idle branch October 31, 2026 23:39
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