Files
igny8/LOGOUT-DEBUGGING-COMPLETE-SUMMARY.md
IGNY8 VPS (Salman) 4fb3a144d7 messy logout fixing
2025-12-15 12:01:41 +00:00

917 lines
32 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Logout Debugging System - Complete Implementation Summary
## Problem Statement
Users are being logged out after approximately 20-25 minutes of idle time, despite implementing:
- 1-hour JWT access tokens
- 20-day refresh tokens (with remember_me)
- Removed middleware logout triggers
- Redis session storage with 14-day expiry
- Atomic token refresh with rotation
- Error classification (only 401 on refresh triggers logout)
## Solution Approach
Since the logout issue persists despite multiple fixes, we've implemented a **comprehensive debugging and tracking system** to capture the EXACT cause of logout events. This "measure before fixing" approach provides complete visibility into every logout.
## Architecture Overview
```
┌─────────────────────────────────────────────────────────────┐
│ Frontend (React) │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ TokenExpiry │ │ LogoutTracker │ │
│ │ Monitor │ │ Service │ │
│ │ │ │ │ │
│ │ • Every 30s │ │ • Track activity │ │
│ │ • Console logs │ │ • Calc idle time │ │
│ │ • Warnings │ │ • Show alert │ │
│ └─────────┬────────┘ │ • Log to backend │ │
│ │ └──────────┬───────┘ │
│ │ │ │
│ ▼ ▼ │
│ Console Logs Modal Alert │
│ │ │
│ ▼ │
│ POST /logout-event/ │
│ │
└──────────────────────────┬─────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ Backend (Django) │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ LogoutTrackingView │ │
│ │ │ │
│ │ • Receives logout events │ │
│ │ • Logs with full context │ │
│ │ • IP, user agent, timing │ │
│ │ • Idle time, location │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Server Logs / DB │
│ │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ User Interface │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. Before Logout: Modal Alert │
│ ┌─────────────────────────────────────────┐ │
│ │ 🚨 You're Being Logged Out │ │
│ │ │ │
│ │ Reason: Token Expired │ │
│ │ Idle Time: 23 minutes │ │
│ │ │ │
│ │ [You'll be redirected in 3 seconds...] │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 2. On Signin Page: Logout Reason Banner │
│ ┌─────────────────────────────────────────┐ │
│ │ ⏰ Session Expired After 23min Idle │ │
│ │ │ │
│ │ [Show Technical Details ▼] │ │
│ └─────────────────────────────────────────┘ │
│ │
│ 3. Debug Panel (Ctrl+Shift+D) │
│ ┌─────────────────────────────────────────┐ │
│ │ 🔍 Auth Debug Panel [×] │ │
│ │─────────────────────────────────────────│ │
│ │ Auth Status │ │
│ │ ✓ Authenticated: Yes │ │
│ │ ✓ Has Access Token: Yes │ │
│ │ ✓ Has Refresh Token: Yes │ │
│ │ │ │
│ │ Token Status │ │
│ │ • Access: 45m left │ │
│ │ • Refresh: 19d 12h left │ │
│ │ │ │
│ │ Recent Logouts (2) │ │
│ │ ⏰ TOKEN_EXPIRED (23m ago) │ │
│ │ Idle: 23 minutes │ │
│ │ 👋 USER_ACTION (2h ago) │ │
│ └─────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
## Components Implemented
### 1. TokenExpiryMonitor (`frontend/src/services/tokenExpiryMonitor.ts`)
**Purpose:** Real-time monitoring of JWT token expiry
**Features:**
- Checks every 30 seconds
- Decodes JWT payload to extract expiry
- Color-coded console logs:
- 🟢 Green: Token valid, plenty of time
- 🟡 Yellow: Access token < 5min, refresh token < 1 day
- 🔴 Red: Token expired
- Exposes `window.__tokenMonitor` for debugging
- Auto-starts on import
**Console Output Example:**
```
[TokenMonitor] Access token: 45 minutes until expiry
[TokenMonitor] Refresh token: 19 days, 12 hours until expiry
```
**Warning Output:**
```
[TokenMonitor] ⚠️ WARNING: Access token expires in 4 minutes!
[TokenMonitor] ⚠️ CRITICAL: Refresh token expires in 12 hours!
```
**Debugging:**
```javascript
// Get current token status
const status = window.__tokenMonitor.getTokenStatus();
console.log(status);
// {
// accessTokenExpired: false,
// accessExpiresInMinutes: 45,
// refreshTokenExpired: false,
// refreshExpiresInHours: 468
// }
```
---
### 2. LogoutTracker (`frontend/src/services/logoutTracker.ts`)
**Purpose:** Track every logout event with full context
**Features:**
- **Activity Monitoring:** Tracks mousemove, keydown, click, scroll
- **Idle Time Calculation:** Knows how long user was inactive
- **Visual Alert:** Shows modal before redirect with reason
- **Backend Logging:** POSTs event to `/v1/auth/logout-event/`
- **History Storage:** Keeps last 10 logouts in localStorage
- **Session Storage:** Persists reason for signin page display
**Tracked Data:**
```typescript
{
type: 'TOKEN_EXPIRED' | 'REFRESH_FAILED' | 'USER_ACTION' | 'AUTH_ERROR' | 'UNKNOWN',
message: string,
timestamp: number,
idleMinutes: number,
location: string,
context: {
hasToken: boolean,
hasRefreshToken: boolean,
isAuthenticated: boolean,
userId?: number,
userEmail?: string
}
}
```
**Usage in Code:**
```typescript
// In authStore.ts logout()
trackLogout('User clicked logout button', 'USER_ACTION', { ... });
// In api-new.ts when refresh fails
trackLogout('Refresh token returned 401', 'REFRESH_FAILED', { ... });
```
**Alert Display:**
```
┌─────────────────────────────────────────┐
│ 🚨 You're Being Logged Out │
│ │
│ Reason: Refresh token expired │
│ You were idle for 23 minutes │
│ │
│ You'll be redirected in 3 seconds... │
└─────────────────────────────────────────┘
```
---
### 3. LogoutReasonBanner (`frontend/src/components/auth/LogoutReasonBanner.tsx`)
**Purpose:** Display logout reason on signin page
**Features:**
- Shows icon based on logout type
- 👋 USER_ACTION
- ⏰ TOKEN_EXPIRED
- 🚨 REFRESH_FAILED
- ⚠️ AUTH_ERROR
- User-friendly messages
- Collapsible technical details
- Auto-clears after 30 seconds
- Manual close button
**Display Examples:**
**User Action:**
```
┌─────────────────────────────────────────┐
│ 👋 You logged out successfully │
│ [×] │
└─────────────────────────────────────────┘
```
**Token Expired (with idle time):**
```
┌─────────────────────────────────────────┐
│ ⏰ Session expired after 23 minutes idle │
│ Please sign in again to continue │
│ [Show technical details ▼] │
│ [×] │
└─────────────────────────────────────────┘
```
**Expanded Details:**
```
┌─────────────────────────────────────────┐
│ ⏰ Session expired after 23 minutes idle │
│ Please sign in again to continue │
│ [Hide technical details ▲] │
│ │
│ Type: TOKEN_EXPIRED │
│ Message: JWT access token expired │
│ Idle Time: 23 minutes │
│ Location: /dashboard │
│ Time: 2024-01-15 14:32:15 │
│ [×] │
└─────────────────────────────────────────┘
```
---
### 4. Backend LogoutTrackingView (`backend/auth/views_logout_tracking.py`)
**Purpose:** Receive and log logout events from frontend
**Endpoint:** `POST /v1/auth/logout-event/`
**Request Body:**
```json
{
"type": "TOKEN_EXPIRED",
"message": "JWT access token expired",
"timestamp": 1705330335000,
"idleMinutes": 23,
"location": "/dashboard",
"context": {
"hasToken": true,
"hasRefreshToken": true,
"isAuthenticated": true,
"userId": 123,
"userEmail": "user@example.com"
}
}
```
**Server Log Output:**
```
================================================================================
LOGOUT EVENT - 2024-01-15 14:32:15
================================================================================
Type: TOKEN_EXPIRED
Message: JWT access token expired
Idle Time: 23 minutes
Location: /dashboard
User: user@example.com (ID: 123)
IP: 192.168.1.100
User Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0
Context: {'hasToken': True, 'hasRefreshToken': True, 'isAuthenticated': True}
================================================================================
```
---
### 5. AuthDebugPanel (`frontend/src/components/debug/AuthDebugPanel.tsx`)
**Purpose:** Real-time debug dashboard for auth status
**Features:**
- Toggle with Ctrl+Shift+D or click 🔍 button
- Shows auth state (authenticated, tokens, user)
- Displays token expiry countdown
- Lists recent logout events
- "Log Full State to Console" button
- Updates every 5 seconds when open
**Panel Sections:**
1. **Auth Status:**
- Authenticated: ✓/✗
- User ID & Email
- Has Access Token: ✓/✗
- Has Refresh Token: ✓/✗
2. **Token Status:**
- Access Token: "45m left" or "Expired"
- Refresh Token: "19d 12h left" or "Expired"
3. **Recent Logouts:**
- Last 5 logout events
- Shows type, message, idle time, time ago
4. **Actions:**
- Log Full State: Dumps everything to console
---
### 6. Updated SignInForm (`frontend/src/components/auth/SignInForm.tsx`)
**Changes:**
- Added `LogoutReasonBanner` display at top
- Changed checkbox from "Keep me logged in" to "Remember me for 20 days"
- Passes `rememberMe` to `login()` function
- Checkbox state stored in `rememberMe` instead of `isChecked`
**Before:**
```tsx
<Checkbox checked={isChecked} onChange={setIsChecked} />
<span>Keep me logged in</span>
```
**After:**
```tsx
<Checkbox checked={rememberMe} onChange={setRememberMe} />
<span>Remember me for 20 days</span>
await login(email, password, rememberMe);
```
---
### 7. Updated AuthStore (`frontend/src/store/authStore.ts`)
**Changes:**
1. **login() signature:**
```typescript
// Before
login: (email: string, password: string) => Promise<void>;
// After
login: (email: string, password: string, rememberMe?: boolean) => Promise<void>;
```
2. **login() implementation:**
```typescript
body: JSON.stringify({
email,
password,
remember_me: rememberMe,
device_id: localStorage.getItem('device_id') || crypto.randomUUID()
})
```
3. **logout() signature:**
```typescript
// Before
logout: () => void;
// After
logout: (reason?: string, type?: 'USER_ACTION' | ...) => void;
```
4. **logout() implementation:**
```typescript
logout: (reason = 'User clicked logout', type = 'USER_ACTION') => {
trackLogout(reason, type, { /* context */ });
// ... existing logout code
}
```
---
### 8. Updated App.tsx
**Changes:**
```typescript
// Import monitoring services
import './services/tokenExpiryMonitor'; // Auto-starts monitoring on import
```
This single import:
- Starts token monitoring immediately
- Logs to console every 30 seconds
- Exposes `window.__tokenMonitor` for debugging
---
## Data Flow
### Login Flow with Remember Me
```
1. User checks "Remember me for 20 days"
└─> rememberMe = true
2. SignInForm.tsx calls login(email, password, rememberMe)
└─> authStore.login() called
3. AuthStore generates device_id (or uses existing)
└─> device_id = crypto.randomUUID() or localStorage
4. POST /v1/auth/login/
body: {
email,
password,
remember_me: true,
device_id: "uuid-here"
}
5. Backend (views.py) receives remember_me
└─> generate_refresh_token_pair(user_id, remember_me=True)
└─> Creates RefreshToken with expires_at = now + 20 days
6. Backend returns tokens:
{
access: "jwt-access-token",
refresh: "jwt-refresh-token",
user: { ... }
}
7. Frontend stores in Zustand + localStorage
└─> TokenExpiryMonitor starts logging expiry
8. TokenExpiryMonitor logs every 30s:
"Access token: 60 minutes until expiry"
"Refresh token: 20 days until expiry"
```
---
### Logout Detection Flow
```
Scenario 1: Token Expires (20 days later)
───────────────────────────────────────────
1. TokenExpiryMonitor detects refresh token expired
└─> Console: "🔴 Refresh token EXPIRED"
2. User makes API call (any endpoint)
└─> api-new.ts adds Authorization header
3. Backend rejects with 401 Unauthorized
└─> Frontend attempts token refresh
4. POST /v1/auth/refresh/ with expired refresh token
└─> Backend returns 401 (token invalid/expired)
5. api-new.ts catches 401 on refresh endpoint
└─> trackLogout('Refresh failed: 401', 'REFRESH_FAILED', {...})
6. LogoutTracker shows alert:
┌─────────────────────────────────────┐
│ 🚨 Session Expired │
│ Your 20-day session has ended │
│ Idle: 0 minutes (active user) │
│ Redirecting in 3 seconds... │
└─────────────────────────────────────┘
7. LogoutTracker POSTs to /v1/auth/logout-event/
└─> Backend logs event with full context
8. authStore.logout() called
└─> Clears tokens, user data
└─> Navigates to /signin
9. SignInForm shows LogoutReasonBanner:
┌─────────────────────────────────────┐
│ ⏰ Session expired after 20 days │
│ Please sign in again │
└─────────────────────────────────────┘
```
```
Scenario 2: Idle Timeout (what we're debugging)
─────────────────────────────────────────────────
1. User logs in with remember_me=true
└─> TokenExpiryMonitor: "Access: 60min, Refresh: 20d"
2. User idle for 20 minutes
└─> LogoutTracker tracks last activity
└─> lastActivity = timestamp (20 min ago)
3. TokenExpiryMonitor logs every 30s:
- T+0: "Access: 60min, Refresh: 20d"
- T+10: "Access: 50min, Refresh: 20d"
- T+20: "Access: 40min, Refresh: 20d"
- T+25: "Access: 35min, Refresh: 20d"
- T+25: 🚨 LOGOUT OCCURS 🚨
4. Something triggers logout (UNKNOWN CAUSE)
└─> trackLogout() called with reason
└─> Calculates: idleMinutes = (now - lastActivity) / 60000
└─> idleMinutes = 20
5. Alert shown:
┌─────────────────────────────────────┐
│ 🚨 You're Being Logged Out │
│ Reason: [EXACT REASON HERE] │
│ Idle: 20 minutes │
└─────────────────────────────────────┘
6. Backend logs:
Type: [EXACT TYPE]
Idle Time: 20 minutes
Access Token: 35min left (NOT expired!)
Refresh Token: 20d left (NOT expired!)
→ This tells us logout is NOT due to token expiry
7. Analysis possible causes:
- Redis session expired? (check SESSION_COOKIE_AGE)
- Middleware still triggering? (check logs)
- Frontend error? (check console for JS errors)
- Browser extension? (test in incognito)
- Multi-tab issue? (check BroadcastChannel logs)
```
---
## Debugging Session Example
### Step 1: Login and Monitor
```javascript
// User logs in with remember_me=true
// Console immediately shows:
[TokenMonitor] 🟢 Starting token expiry monitoring...
[TokenMonitor] Access token: 60 minutes until expiry
[TokenMonitor] Refresh token: 20 days, 0 hours until expiry
// Every 30 seconds:
[TokenMonitor] Access token: 59 minutes until expiry
[TokenMonitor] Refresh token: 19 days, 23 hours until expiry
```
### Step 2: Wait for Logout (Idle 25 minutes)
```javascript
// At T+10min:
[TokenMonitor] Access token: 50 minutes until expiry
// At T+20min:
[TokenMonitor] Access token: 40 minutes until expiry
// At T+25min:
// Something triggers logout...
[LogoutTracker] 🚨 Logout triggered
[LogoutTracker] Type: TOKEN_EXPIRED (or REFRESH_FAILED, or AUTH_ERROR)
[LogoutTracker] Message: [Exact reason here]
[LogoutTracker] Idle time: 25 minutes
[LogoutTracker] Context: {
hasToken: true,
hasRefreshToken: true,
isAuthenticated: true,
userId: 123,
userEmail: 'user@example.com'
}
[LogoutTracker] 📤 Sending to backend...
[LogoutTracker] Backend received event
```
### Step 3: Modal Alert Shown
```
┌─────────────────────────────────────────┐
│ 🚨 You're Being Logged Out │
│ │
│ Reason: [EXACT REASON] │
│ You were idle for 25 minutes │
│ │
│ Redirecting in 3 seconds... │
└─────────────────────────────────────────┘
```
### Step 4: Backend Logs
```bash
$ docker logs igny8-backend | tail -20
================================================================================
LOGOUT EVENT - 2024-01-15 14:32:15
================================================================================
Type: TOKEN_EXPIRED (or other)
Message: [Exact reason]
Idle Time: 25 minutes
Location: /dashboard
User: user@example.com (ID: 123)
IP: 192.168.1.100
User Agent: Mozilla/5.0 ...
Access Token Status: 35 minutes remaining (NOT EXPIRED!)
Refresh Token Status: 19 days remaining (NOT EXPIRED!)
Context: {'hasToken': True, 'hasRefreshToken': True}
================================================================================
```
### Step 5: Signin Page Display
```
┌─────────────────────────────────────────┐
│ ⏰ Session expired after 25 minutes idle │
│ Please sign in again to continue │
│ [Show technical details ▼] │
│ [×] │
└─────────────────────────────────────────┘
```
### Step 6: Debug Panel Analysis
```javascript
// Press Ctrl+Shift+D
🔍 Auth Debug Panel [×]
Recent Logouts (1)
TOKEN_EXPIRED 25m ago
[Exact message]
Idle: 25 minutes
[Log Full State to Console]
// Click button, console shows:
=== AUTH STATE ===
User: null (logged out)
Token: null
Refresh Token: null
Token Status: { accessTokenExpired: true, ... }
Logout History: [
{
type: 'TOKEN_EXPIRED',
message: '[Exact reason]',
idleMinutes: 25,
timestamp: 1705330335000,
location: '/dashboard'
}
]
```
---
## Key Insights This System Provides
### 1. **Exact Logout Type**
- `USER_ACTION`: User clicked logout button
- `TOKEN_EXPIRED`: JWT token expired (access or refresh)
- `REFRESH_FAILED`: Refresh request returned 401
- `AUTH_ERROR`: Other auth error (403, 402, etc.)
- `UNKNOWN`: Unexpected logout
### 2. **Idle Time at Logout**
- "User was idle for X minutes before logout"
- Distinguishes between:
- Active user: 0-2 minutes idle
- Short idle: 2-10 minutes
- Medium idle: 10-20 minutes
- Long idle: 20+ minutes
### 3. **Token Status at Logout**
- Were tokens expired? (Yes/No)
- How much time was remaining?
- Access token had 35 min left → logout NOT due to access token
- Refresh token had 19 days left → logout NOT due to refresh token
### 4. **Location and Context**
- Which page user was on: `/dashboard`, `/content`, etc.
- Browser info, IP address
- Multi-tab scenario detection
### 5. **Timeline Correlation**
- Cross-reference:
- TokenMonitor logs (every 30s)
- LogoutTracker event
- Backend logs
- Network tab API calls
- Build complete timeline of what happened
---
## Expected Outcomes
### If Working Correctly (Remember Me = True):
```
T+0: Login
Access: 60min, Refresh: 20d
T+60min: Access token expires
Auto-refresh triggered
New access token: 60min
Refresh: 19d 23h (slightly less)
T+120min: Access token expires again
Auto-refresh triggered
New access token: 60min
Refresh: 19d 22h
... continues for 20 days ...
T+20d: Refresh token expires
Next API call → refresh fails → logout
Type: REFRESH_FAILED
Reason: "Refresh token expired after 20 days"
```
### If Bug Exists (Logout at 25min):
```
T+0: Login
Access: 60min, Refresh: 20d
T+25min: 🚨 UNEXPECTED LOGOUT 🚨
Access: 35min remaining (NOT expired)
Refresh: 19d 23h remaining (NOT expired)
Possible causes revealed by logs:
A) Type: TOKEN_EXPIRED
→ Bug: Something checking wrong expiry
→ Fix: Find code checking expiry incorrectly
B) Type: REFRESH_FAILED
→ Bug: Refresh endpoint rejecting valid token
→ Fix: Check backend RefreshToken.get_valid_token()
C) Type: AUTH_ERROR
→ Bug: 403/402 error triggering logout
→ Fix: Verify error classification in api-new.ts
D) Type: UNKNOWN
→ Bug: JavaScript error, browser extension, etc.
→ Fix: Check console for errors, test incognito
```
---
## Deployment Checklist
### Backend:
- [x] Created `auth/models_refresh_token.py`
- [x] Created `auth/views_logout_tracking.py`
- [x] Added `logout-event/` endpoint to `auth/urls.py`
- [x] Updated `settings.py` (JWT expiry, session, Redis)
- [x] Updated `requirements.txt` (django-redis)
- [ ] Run `python manage.py migrate`
- [ ] Restart backend services
### Frontend:
- [x] Created `services/tokenExpiryMonitor.ts`
- [x] Created `services/logoutTracker.ts`
- [x] Created `components/auth/LogoutReasonBanner.tsx`
- [x] Created `components/debug/AuthDebugPanel.tsx`
- [x] Updated `SignInForm.tsx` (remember me checkbox, banner)
- [x] Updated `authStore.ts` (login/logout signatures, tracking)
- [x] Updated `App.tsx` (import tokenExpiryMonitor)
- [ ] Build production bundle (`npm run build`)
- [ ] Deploy to production
### Verification:
- [ ] Console shows: `[TokenMonitor] Starting...`
- [ ] Token status logs every 30 seconds
- [ ] Login with remember me creates 20-day refresh token
- [ ] Debug panel opens with Ctrl+Shift+D
- [ ] Logout shows alert before redirect
- [ ] Signin page shows logout reason
- [ ] Backend receives logout events
### Testing:
- [ ] Login with remember_me=true
- [ ] Open console and watch logs
- [ ] Idle for 25+ minutes
- [ ] Capture logout event with all data
- [ ] Analyze: type, message, idle time, token status
- [ ] Identify root cause from collected data
---
## Troubleshooting Guide
### Issue: No console logs from TokenMonitor
**Check:**
1. Is `import './services/tokenExpiryMonitor'` in App.tsx?
2. Any JavaScript errors in console?
3. Is token stored in correct format in localStorage?
**Debug:**
```javascript
// Check if monitor is running
window.__tokenMonitor.getTokenStatus()
```
---
### Issue: No alert shown before logout
**Check:**
1. Is `trackLogout()` being called in authStore.logout()?
2. Any errors in LogoutTracker?
3. Is modal rendering correctly?
**Debug:**
```javascript
// Manually trigger alert
import { trackLogout } from './services/logoutTracker';
trackLogout('Test logout', 'USER_ACTION', {});
```
---
### Issue: Backend not receiving logout events
**Check:**
1. Network tab: Is POST to `/v1/auth/logout-event/` succeeding?
2. CORS settings allow POST from frontend?
3. Backend endpoint registered in urls.py?
**Debug:**
```bash
# Test endpoint directly
curl -X POST http://localhost:8000/api/v1/auth/logout-event/ \
-H "Content-Type: application/json" \
-d '{"type":"TEST","message":"test"}'
```
---
### Issue: Logout banner not showing on signin page
**Check:**
1. Is LogoutReasonBanner imported in SignInForm.tsx?
2. Is sessionStorage.getItem('last_logout_reason') set?
3. Any React errors in console?
**Debug:**
```javascript
// Check if reason stored
sessionStorage.getItem('last_logout_reason')
// Manually set reason
sessionStorage.setItem('last_logout_reason', JSON.stringify({
type: 'TEST',
message: 'Test logout',
idleMinutes: 5
}));
// Reload signin page
```
---
## Next Steps After Deployment
1. **Deploy to Production**
```bash
./scripts/deploy-logout-debugging.sh
```
2. **Login with Remember Me**
- Check console for monitoring logs
- Verify 20-day refresh token in logs
3. **Wait for Logout Event**
- Keep browser open
- Idle for 25+ minutes
- Capture all data when logout occurs
4. **Analyze Collected Data**
- Review console logs
- Check backend logs
- Examine logout event details
- Identify root cause
5. **Implement Targeted Fix**
- Based on data, create precise fix
- No more guessing!
---
## Conclusion
This debugging system provides **complete visibility** into every logout event. No matter what causes the 25-minute logout, we will capture:
1. **WHAT** triggered it (exact type)
2. **WHY** it happened (exact reason/message)
3. **WHEN** it occurred (timestamp, idle time)
4. **WHERE** user was (page location)
5. **HOW** tokens looked (expiry status)
With this data, we can implement a **targeted, permanent fix** instead of guessing at potential causes.
The system is production-safe, with minimal performance impact, and provides both user-facing feedback (alerts, banners) and developer tools (console logs, debug panel) to ensure we never miss another logout event.