Mobile session recovery as an explicit state machine
Model sign-in, refresh rotation, expiry, step-up recovery, and revocation without creating a hidden authentication bypass.
Authentication features usually look simple as screens: sign in, stay signed in, reset password, and return to the app. The dangerous parts are the transitions between those screens. An expired access token does not mean the same thing as a revoked session. A recovery message arriving does not mean a user is authenticated.
Representing those conditions as explicit states makes missing checks easier to find. The session recovery simulator demonstrates the idea with synthetic events. It is not an authentication server.
Separate identity proof from session state
A minimal conceptual state model might contain:
- Signed out: No usable session credentials are accepted.
- Active: The server has an unrevoked session with a valid access credential.
- Access expired: The access token is no longer usable; a separately protected refresh credential might still be valid.
- Recovery pending: The caller is proving account control through an independent challenge, not yet receiving protected access.
- Revoked: The server invalidated session credentials; an old refresh credential must not restore the session.
Real systems often need more states: password change required, rate-limited, suspected account takeover, step-up pending, device migration, or disabled account. Start with the smallest model that captures actual authorization differences.
Draw transitions before writing view logic
| Event | Starting state | Expected result |
|---|---|---|
| Valid sign-in | Signed out | New active session |
| Access expiry | Active | Access expired |
| Valid refresh | Access expired | Rotate refresh credential; active |
| Refresh of revoked credential | Revoked | Rejected |
| Request recovery | Signed out or expired | Recovery pending |
| Recovery proof accepted | Recovery pending | Rotate credentials; create new active session |
| Step-up submitted with no challenge | Signed out | Rejected |
| Session revocation | Any usable state | Revoked |
Notice what is missing: there is no transition that turns a UI success message, a local biometric check, or a raw device-integrity hint into an authenticated session.
Keep the policy on the backend
A client-side state enum helps UX and telemetry. It must not be the authorization authority. The backend must establish whether a refresh credential is still valid, whether it has already been rotated, whether a recovery challenge expired, and whether an account is disabled or on hold.
For refresh rotation, concurrent callers require particular attention. Two requests can present the same refresh token nearly simultaneously. A naive lookup, validate, then update sequence can issue two successor credentials unless the rotate-and-consume operation is atomic.
That sequence is an architectural checklist, not deployable auth code. Token hashing, key management, binding, device theft, concurrency semantics, and abuse limits require additional design.
Recovery cannot be a universal fallback
Suppose an attacker controls a device but not the user's primary credentials. A support-driven recovery path that skips normal step-up can become more attractive than attacking login. The risk is highest when a system treats “the app is on a recognized device” as if it were account ownership.
A safer review looks at independent evidence:
- Which party initiated recovery and which account is affected?
- Which rate limits, abuse signals, or waiting periods apply?
- Which proof establishes account control without reusing a compromised credential?
- What old sessions and recovery tokens are invalidated after success?
- Which events are visible to the account owner and incident response?
Never assume recovery is secure merely because it has more screens.
Failure-driven test matrix
Try the authentication state machine with this sequence: sign in, expire access, refresh, revoke, then attempt refresh again. The final refresh should fail. Next request recovery, complete the modeled step-up, and verify a new active state is possible only through the recovery path.
Add production-specific tests for concurrent refresh, stolen refresh tokens, reused recovery codes, disconnected clients, disabled accounts, clock skew, token rotation races, and service outages. The UI can remain useful through a temporary failure while refusing unauthorized access.
Operational takeaway: Record why a transition was allowed or denied without recording passwords, one-time codes, raw tokens, or recovery secrets. An explicit state makes a failure observable; it does not replace backend verification.
Editorial note: Educational state-machine reference prepared with AI assistance. The simulator does not authenticate users or issue tokens.
Continue exploring
Related technical notes with overlapping topics.