Auth Tokens & Idempotency Quiz

DATA › Networking

Which HTTP methods are idempotent by contract?

Answer: GET, PUT and DELETE, but not POST

PUT replaces state (same result however many times) and DELETE removes it; both are defined idempotent. POST 'creates/processes' and may repeat its effect, which is exactly why payment APIs bolt idempotency keys onto POST.

A payment request times out and the app retries it. Why is an idempotency key required for this to be safe?

Answer: The server may have executed the first request even though the response was lost

A timeout is ambiguous: the request may have failed OR succeeded with a lost response. With the same idempotency key, the server recognises the retry and returns the stored outcome instead of charging twice.

What is the race in this token accessor when called from many coroutines?

Answer: Several callers see expired == true and all start their own refresh

Check-then-act without exclusion: every concurrent caller can pass the expiry check before any refresh completes, firing N refreshes with all the rotation and rate-limit fallout that brings.

Which change makes that accessor single-flight?

Answer: Guard with a Mutex and re-check expiry inside withLock before refreshing

The Mutex serialises the critical section across coroutines without blocking threads, and the inner re-check turns the queued callers into no-ops. @Synchronized doesn't work across suspension points, and volatile fixes visibility, not the check-then-act race.

An alternative single-flight shape caches a Deferred of the in-flight refresh. What property does this give?

Answer: Every concurrent caller awaits the same refresh and gets the same result

The first caller creates the Deferred (in a scope the manager owns); everyone else finds it and awaits it. One network call, one result fanned out to all, and the field is cleared when it completes.

Why is refreshing exactly once for N callers described as making the refresh idempotent in effect?

Answer: N concurrent requests for a valid token produce one refresh and one shared result, same as a single request

That is the interview's connecting thread: single-flight gives 'many invocations, the effect of one'. Whether achieved client-side (Mutex/Deferred) or server-side (idempotency keys), repeated triggers must not multiply the side effect.

The refresh call itself fails with a 401 (refresh token rejected). What is the correct handling?

Answer: Clear the session and route to re-authentication; don't loop retrying

A rejected refresh token means the session is dead (revoked, rotated-and-reused, or expired). Retrying can never succeed and may trigger reuse detection to lock the account further. Fail cleanly: wipe state, surface logged-out, and let the user authenticate.

In OkHttp, which mechanism is designed to react to a 401 by supplying credentials for a retry?

Answer: An Authenticator installed on the client

Authenticator is invoked exactly when a request got a 401: refresh there (synchronously, single-flight!) and return a new request with the fresh header, or null to give up. Interceptors are the right place to ATTACH the current token, not to handle challenges.

A caller awaiting the shared refresh is cancelled (its screen closed). What should the design guarantee?

Answer: The refresh continues in the manager's own scope and other callers still complete

Run the refresh in a scope the token manager owns (or guard the final write with NonCancellable) so one caller's lifecycle doesn't abort work others depend on. withLock releases the Mutex via finally even on cancellation, so no lock is leaked.

How does refresh-token ROTATION limit the damage of a stolen refresh token?

Answer: Replay of the old token is detected as reuse and the whole token family is revoked

Every refresh invalidates the previous refresh token. If the attacker uses the stolen (now old) token, or the legitimate client does after the attacker, the server sees an invalidated token being replayed, flags compromise, and revokes all descendants.

Which storage is acceptable for a refresh token on Android?

Answer: Keystore-backed encrypted storage (e.g. encrypted DataStore/preferences)

The sandbox is not a boundary on rooted devices or in backups, and anything in BuildConfig/URLs leaks via the APK, logs and analytics. Encrypt at rest with a Keystore key; keep the hot copy in memory; mask Authorization headers in logging.

Token and expiry are stored as two separate vars, updated one after the other. What consistency bug can readers hit, and what is the fix?

Answer: A reader can see a new token with the old expiry (a torn pair); store one immutable value swapped atomically

Between the two writes any reader observes a mismatched pair and may judge a fresh token expired (or worse, vice versa). Group the state into one immutable data class behind a single @Volatile reference or StateFlow so every reader sees a consistent snapshot.

What is the security benefit of idempotency keys beyond correctness?

Answer: A replayed/duplicated request cannot multiply its side effect, blunting replay attacks and double-submits

Whether the duplicate comes from a network retry, a double-tap, or an attacker replaying a captured request, the server executes the effect once and returns the recorded result. Keys must be unguessable and scoped to the authenticated user so one user's key can't collide with another's.

Which logging setup is safe in a networking stack that carries bearer tokens?

Answer: Redact the Authorization header and log bodies only in debug builds

Tokens in logs are credentials at rest in the wrong place: they flow into crash reports, analytics and support tickets. OkHttp's HttpLoggingInterceptor supports redactHeader("Authorization"); Base64 is encoding, not protection.

Back to Auth Tokens & Idempotency