Why does the authenticator code change every 30 seconds instead of on every login?
Because TOTP's counter is the Unix time divided by the configured period, 30 seconds by default under RFC 6238; every time that counter's integer value changes, the HMAC computed over it changes, and the truncated code changes along with it, regardless of whether a login attempt is happening at that moment.
Is the base32 TOTP secret the same thing as the QR code?
Not exactly: the base32 secret (like `JBSWY3DPEHPK3PXP`) is just the raw data used in the HMAC calculation; the QR code encodes that secret inside a full `otpauth://` URI, which also carries the account name, the issuer, the algorithm, the digit count and the period, everything the app needs to generate matching codes.
What happens if the server's clock is behind?
Since TOTP exchanges no messages to stay in sync, it depends entirely on both clocks matching within a tolerance window the verifier accepts, usually one or two 30-second periods forward and back; a larger drift makes even the user's correct code get rejected, a clock-synchronization problem, not a flaw in the algorithm itself.
Is TOTP as secure as a physical FIDO2 security key?
Not at the same level: TOTP is symmetric, the server stores the same secret used to generate the code, so whoever steals that database can generate valid codes forever. A FIDO2/WebAuthn key uses an asymmetric key pair, and the private key never leaves the physical device or gets stored by the server, which removes that specific risk.