Session Timeout

Feature Owner: scorevi (Sean Patrick Caintic)
Module: Auth & Security
Priority: P1
Sprint #12: Partially Implemented (~60%)
Date: 2026-06-29


EXECUTIVE SUMMARY

What is this feature?
An idle timeout hook (useIdleTimeout) that tracks user activity and auto-signs out via Clerk after 20 minutes of inactivity, with a 2-minute warning. Includes cross-tab sync via localStorage and iOS Safari bfcache protection.

Why does it matter?
Without idle timeout, a user who leaves their session open on a shared/public computer exposes their account. For enterprise/edu settings with compliance requirements, this is a security gap.

What's the MVP scope?
useIdleTimeout hook implemented in hooks/quest/useIdleTimeout.ts (130 lines). Used in LearnerLayoutClient and CreatorLayoutClient. NOT yet integrated into Admin, Agency, or Reviewer layouts. Cache-Control headers enforced in middleware.


1. USER PAIN POINT & SOLUTION

Current State (Without Feature)

A user signs in on a library computer, walks away, and the session remains active indefinitely. Anyone can access their dashboard, quests, or personal data.

Pain Point

Emotional: Anxiety about leaving a session exposed.
Functional: No automatic session termination. Manual sign-out is the only protection.
Business Impact: Compliance risk (especially for education/GDPR). Potential data breaches from unattended sessions.

Future State (With Feature)

After 20 minutes of inactivity, the user receives a warning. If they don't respond within 2 minutes, they are automatically signed out and redirected to /sign-in?reason=session_timeout.

Marketing Hook

"Your session is protected. Always."


2. 4D FRAMEWORK MAPPING

Diagnose

N/A — security infrastructure.

Design

N/A — security infrastructure.

Develop

N/A — security infrastructure.

Deliver

N/A — security infrastructure.


3. USER FLOWS

Entry Point

User signs in and begins interacting with the application. The idle timer starts tracking activity.

Success Criteria

After 20 minutes of inactivity, user is signed out and redirected to sign-in page with a timeout reason.

Main Flow (Happy Path)

  1. User signs in and starts browsing. Timer resets on every mousemove, keydown, click, scroll, or touchstart.

  2. After 18 minutes of inactivity (20 min - 2 min warning), onWarning callback fires.

  3. If no activity detected in the next 2 minutes, handleTimeout fires.

  4. signOut() is called via Clerk. On success, user is redirected to /sign-in?reason=session_timeout.

Edge Cases

  • Multiple tabs: One tab's activity resets the timer in all tabs via localStorage.setItem("wyzquests_last_active") and storage event listener.

  • SSR/storage quota error: localStorage write failure is silently caught.

  • Disabled state: Hook accepts disabled prop (e.g., when unauthenticated).

  • iOS Safari bfcache: BfCacheGuard component forces hard reload if page is restored from bfcache after sign-out.

Decision Points

  • IF disabled === true → hook does nothing.

  • ELSE → start tracking activity, set timers.


4. INFORMATION ARCHITECTURE

Primary Information (Always visible)

  • Invisible in normal state. Warning toast/modal appears 2 minutes before timeout.

Secondary Information

  • Session timer (if made visible in future UI).

Actions

Primary CTA: Respond to warning (any activity resets timer).
Secondary Actions: N/A — timeout is automatic.


5. WIREFRAMES

[Excluded — existing implementation, no dedicated UI beyond optional warning toast]

6. WIREFLOWS

Excluded.

7. PROTOTYPE

[Excluded — existing implementation]


8. BACKEND SCHEMA

Database Tables

No dedicated table. Client-side only via React hook.

Client-Side State

Key

Storage

Purpose

wyzquests_last_active

localStorage

Cross-tab activity sync timestamp (ISO epoch)


9. API ENDPOINTS

No dedicated API endpoints. Uses Clerk's signOut() client-side method.


10. DATA REQUIREMENTS

Frontend Needs

  • useIdleTimeout hook (hooks/quest/useIdleTimeout.ts, 130 lines).

  • useClerk() for signOut().

  • useRouter() for redirect.

API Calls Frontend Will Make

  • Clerk signOut() on timeout.

Caching Strategy

N/A.


11. PERFORMANCE CONSIDERATIONS

Database Optimization

N/A.

API Response Time

N/A — client-side only. Timer operations are O(1). Event listeners use { passive: true } for scroll/touch performance.


12. SECURITY & AUTHORIZATION

Who can access this feature?

Feature is integrated for learners and creators only (as of Sprint #12). Pending: admin, agency, reviewer layouts.

Authorization Logic

N/A — hook behavior, not authorization.

Data Validation

N/A.


13. ERROR HANDLING

Error

Response

signOut() fails

Error logged to console. onTimeout callback still fires.

localStorage write fails (quota, SSR)

Silently caught — timer still works within current tab.

Timer fires after component unmount

hasSignedOut ref prevents double-sign-out. Cleanup in useEffect return clears all timers.


14. TESTING CHECKLIST

Happy Path
□ After 20 min inactivity, user is signed out.
□ Warning fires at 18 min (2 min before timeout).
□ Any activity (mouse, keyboard, touch, scroll) resets timer.
□ Redirect URL includes ?reason=session_timeout.

Edge Cases
□ Activity in Tab A resets timer in Tab B (cross-tab sync).
disabled=true prevents all timer logic.
□ Sign-out failure is handled gracefully (no infinite loop).
□ Component unmounts cleanly (no lingering timers).
□ iOS Safari bfcache restore after timeout → forced reload.


15. OPEN QUESTIONS

  • Should DEFAULT_TIMEOUT_MS (20 min) be configurable per role or agency?

  • Should the warning modal require explicit "Stay Signed In" button click, or is any activity sufficient?

  • When will Admin/Agency/Reviewer layouts be integrated?


16. OUT OF SCOPE (v1.1+)

  • Server-side session expiration enforcement (currently client-only).

  • Force logout from admin panel (remote session termination).

  • Idle timeout configuration per agency/role.


17. SUCCESS METRICS

  • 100% of learner and creator sessions auto-terminate after idle timeout.

  • 0 false timeouts from legitimate cross-tab activity (cross-tab sync prevents this).

  • Integration into remaining layouts (Admin, Agency, Reviewer) is pending.


18. DEPENDENCIES

This feature depends on:

  • Clerk signOut() function.

  • BfCacheGuard component (31 lines) for iOS Safari protection.

  • middleware.ts Cache-Control headers (no-store, no-cache, must-revalidate, proxy-revalidate).

These features depend on this:

  • None directly. Security hardening.


19. TIMELINE & OWNERSHIP

  • Status: ~60% implemented.

  • Implemented layouts: app/learner/LearnerLayoutClient.tsx (line 59), app/creator/CreatorLayoutClient.tsx (line 53).

  • Pending layouts: AdminLayoutClient, AgencyLayoutClient, ReviewerLayoutClient.

  • Owner: scorevi (Sean Patrick Caintic)


Document Version

1.0 - Initial version - 2026-06-29 08:03 UTC

1.1 (Current Version) - Added Document Version section and update author to have full name - 2026-06-29 08:37 UTC


Was this article helpful?