How to Test Session Expiry and Refresh
Session expiry is the boundary between "logged in" and "logged out." Handling it badly means users get dumped to login mid-action, or stay logged in long after they should be out. Testing it requires
Session expiry is the boundary between "logged in" and "logged out." Handling it badly means users get dumped to login mid-action, or stay logged in long after they should be out. Testing it requires patience (sessions take time to expire) or careful mocking. This guide covers both approaches.
What to test
Active session
- Session valid during active use
- Token refresh happens silently before expiry
- User never sees unexpected logout during active use
Idle session
- Idle timeout enforced after configured period (e.g., 30 minutes for banking)
- Warning before timeout ("Session expires in 1 minute")
- "Stay logged in" option extends session
- Auto-logout if user does not respond
Background / foreground
- App backgrounded → foregrounded within limit → session still valid
- App backgrounded > limit → session expired on foreground
Explicit actions
- Logout immediately invalidates session
- "Log out all devices" invalidates all
- Password change invalidates current sessions (or keeps active — per policy)
- Account deletion invalidates all
Refresh flow
- Access token near expiry → silent refresh
- Refresh token valid → get new access token
- Refresh token expired / revoked → log user out cleanly
- Race: two requests simultaneously trigger refresh → only one refresh happens
Edge cases
- Clock skew between client and server → refresh still works within tolerance
- Network failure during refresh → retry or show auth error
- User logged in on multiple devices — refresh independent per device
- Session expired while in middle of form — data preserved if possible
- Server rotates secret → all tokens invalidated → users re-auth
Security
- Session timeout appropriate to sensitivity (5 min banking, 30 min normal)
- Cannot use expired token for new requests
- Refresh token not usable as access token
- Token revocation effective within acceptable window
- Session fixation prevented (new session on login)
UX
- Expiry not surprising — user warned or expected
- Re-login after expiry preserves user's intended destination (deep link)
- Error message clear ("Session expired, please log in")
- Not dumped to home screen without context
Testing approach
Manual
- Time-based: set idle timeout to 1 minute for testing, wait, verify expiry
- Server-side: force-expire session via admin panel, use app
- Password change on device B; verify device A session invalidated
Automated
- Mock time to test idle timeout without waiting
- Mock token expiry (expose a test endpoint that expires a specific session)
- Unit test the refresh interceptor
End-to-end
def test_token_refresh(page, api_mock):
page.goto("/dashboard")
# Simulate token near expiry
api_mock.expire_token_at(30) # 30 seconds from now
time.sleep(30)
# Trigger any authenticated action
page.click("text=Refresh data")
# Assert request succeeded (refresh happened silently)
expect(page.locator(".data")).to_be_visible()
Fix patterns
Silent refresh
Interceptor checks token expiry before each request. If near expiry, refresh first, then proceed.
Queue during refresh
All requests queue while refresh in progress. After refresh, all use new token.
Graceful expiry UI
If user-initiated action encounters expired session:
- Save the intended action
- Prompt for login
- After login, replay action
- Show success / error
Multi-device awareness
User logged in on N devices. Logout on device A should invalidate device A's token. If security-critical, option to invalidate all.
How SUSA tests session handling
SUSA's login phase establishes a session. Subsequent steps use it. If session expires mid-exploration, SUSA detects (401 responses, login screen appearance) and reports it as an issue or re-authenticates if configured.
susatest-agent test myapp.apk --steps 200 --re-auth-on-expiry
Can configure a short session for deliberate expiry test.
Common production bugs
- Refresh race condition: two refreshes happen simultaneously, second invalidates first → user logged out
- Clock skew between client and server causes false expiry — refresh rejected as "too early"
- Refresh token has same or longer life as access token — defeats the point
- Password change does not invalidate sessions — compromised session persists
- Silent refresh fires on every request (no caching) — performance hit
Session management is foundational. Get it right; users never think about auth. Get it wrong; they contact support.
Test Your App Autonomously
Upload your APK or URL. SUSA explores like 10 real users — finds bugs, accessibility violations, and security issues. No scripts.
Try SUSA Free