ZombieMutex
All writing
Engineering#Mobile Security#Authentication#Session Recovery#iOS#Android#Backend
November 6, 20264 min read

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.

By

Share

Send this post or copy the direct link.

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

EventStarting stateExpected result
Valid sign-inSigned outNew active session
Access expiryActiveAccess expired
Valid refreshAccess expiredRotate refresh credential; active
Refresh of revoked credentialRevokedRejected
Request recoverySigned out or expiredRecovery pending
Recovery proof acceptedRecovery pendingRotate credentials; create new active session
Step-up submitted with no challengeSigned outRejected
Session revocationAny usable stateRevoked

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.

text
POST /session/refresh
  1. Resolve refresh token record in a trusted store.
  2. Check expiry, revocation, and session binding.
  3. Atomically consume/rotate the presented token.
  4. Issue the next access credential and refresh credential.
  5. Record a privacy-safe outcome and correlation ID.
  6. Reject repeated use according to the session policy.

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:

  1. Which party initiated recovery and which account is affected?
  2. Which rate limits, abuse signals, or waiting periods apply?
  3. Which proof establishes account control without reusing a compromised credential?
  4. What old sessions and recovery tokens are invalidated after success?
  5. 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.

Related technical notes with overlapping topics.