Auth and secrets, as far as the round goes

Check the password once, because checking it is slow on purpose. Hand back a token signed with a key only the servers hold, and every call after that proves who it is with one keyed hash.

Problems worked on this page, and more to practise

Should I use server sessions or stateless tokens like JWTs?

Either works at interview scale, and the choice turns on revocation. A session id points at a row in a shared store, so logout and bans take effect on the next request, and each request pays for one store lookup, about a millisecond. A signed token carries its own claims, so there is no lookup, but a token cannot be taken back until it expires unless you keep a revoked list. I would say that trade out loud, then pick tokens with a short lifetime for an API that many services verify, and sessions for a web app with one backend.

Why not check the password on every request?

Because a password hash is slow on purpose. PBKDF2 at 600,000 rounds, the current OWASP figure, takes a sizeable fraction of a second of one core, so that someone who steals the hashes cannot guess quickly. At thousands of calls a second that is thousands of cores. Hash once at login, then let a signature check, a few microseconds, carry every call after it.

How do you log a user out if tokens are stateless?

Keep a revoked list of token ids that every server checks after the signature. It stays small because an entry only has to live until its token would have expired anyway. To log someone out everywhere at once, keep one cutoff time per user and refuse any token issued before it. Short lifetimes shrink the window in which a stolen token works.

Why compare signatures with hmac.compare_digest and not ==?

A plain == stops at the first character that differs, so the time it takes leaks how much of a guess was right. An attacker who can time many attempts can recover a valid signature one character at a time. compare_digest takes the same time wherever the difference is. The output is identical, which is why tests never catch the mistake.

How do you rotate a signing key without logging everyone out?

Give every key an id, put the id inside the signed token, and keep two keys live at once. The new key signs from the rotation onward; the old key only verifies, until a grace window longer than the longest token lifetime has passed. Swapping the secret in place instead refuses every token already issued, and the resulting wave of logins lands on the slow password hash all at once.