HTTP API Authentication Interview Questions

Reviewed by Mark Dickie · Last updated

API authentication is the process of verifying the identity of a client calling an HTTP API and determining what resources that client is allowed to access. For interviews, you need to know how the common schemes differ (Basic auth, API keys, bearer tokens, OAuth 2.0) and when each one fits. You should be able to explain JWT structure (header, payload, signature), how token expiration and refresh flows work, and the trade-offs between stateless JWTs and server-side sessions. Interviewers also test whether you understand how to sign requests with HMAC and why putting secrets in query strings or client-side code is a bad idea.

SchemeHow it worksWhat to know for interviews
Basic AuthBase64-encoded username:password in Authorization headerNever use over plain HTTP; credentials sent every request; no token invalidation
API KeyStatic key sent in header or query parameterSimple but no fine-grained permissions; key rotation is manual; query params leak in server logs
Bearer TokenToken in Authorization: Bearer <token> headerStateless if JWT; server validates signature; token has expiry; cannot be revoked without a blocklist
OAuth 2.0Delegated authorization via flows like authorization code or client credentialsCovers the most ground in interviews; know grant types, redirect URI validation, PKCE for public clients
HMAC SigningRequest signed with a shared secret using HMAC-SHA256Signature covers method, path, headers, and body; prevents tampering; secret never sent over the wire

What does an API authentication interview test?

Most interviews focus on a few recurring themes:

  1. Picking the right auth scheme for a given scenario (server-to-server vs. browser app vs. mobile app)
  2. Explaining JWT anatomy: three base64url-encoded parts separated by dots, and what the alg and exp claims mean
  3. Describing an OAuth 2.0 flow end-to-end, including the authorization code exchange and token refresh
  4. Identifying security mistakes: storing secrets in frontend JavaScript, sending credentials over plain HTTP, or accepting alg: none JWTs
  5. Discussing token storage trade-offs: localStorage vs. httpOnly cookies, and what CSRF and XSS exposure each one introduces

How should I prepare for API auth questions?

Know the HTTP status codes tied to auth failures. A 401 Unauthorized means the client is not authenticated; a 403 Forbidden means the client is authenticated but lacks permission. Understand the difference between authentication (who are you?) and authorization (what can you do?), because interviewers use those terms to probe whether you conflate them. Be ready to whiteboard a token exchange: the client requests a token, the server validates credentials and issues a signed token, and the client includes that token in subsequent calls until it expires.

Key facts

  • Tarmac has 40 HTTP & APIs interview questions on this topic, 25 of them on this page, at difficulty 1–5 of 5.
  • Tarmac tracked 4,906 job postings asking for HTTP & APIs in August 2026.
  • Roles asking for HTTP & APIs advertise a median base salary of US$167,500, across 854 job postings as of August 2026.
  • Tarmac last reviewed these HTTP & APIs interview questions on 31 August 2026.

At a glance

Questions25 shown · 40 in the bank
Difficulty1–5 of 5
FormatsMultiple choice, True / false, Fill in the blank, Find the bug, Flashcard, Multiple answer, Short answer, Ordering

What you'll review

  1. api auth
  2. oauth basics

Practice questions

Try one before you open the answer. Pick an option and press Check; it's marked on the spot.

HTTP & APIs/api-auth

An API client sends a request without the required Authorization header. Which HTTP status code should the server return to indicate that authentication is required?#

Options

Show answer

The server should return 401 Unauthorized. HTTP 401 indicates that authentication is required or the provided credentials are invalid, and it should be paired with a WWW-Authenticate header. 403 Forbidden, by contrast, means the client is authenticated but not permitted to access the resource.

Why:

HTTP 401 Unauthorized is specifically used when a request lacks valid authentication credentials or the provided credentials are invalid. RFC 7235 specifies that the response must include a WWW-Authenticate header describing how to authenticate. 400 is for malformed requests generally, 403 is for when the client IS authenticated but lacks permission, and 500 indicates a server-side error.

HTTP & APIs/api-auth

In the OAuth 2.0 Bearer token scheme, the access token is sent in the Authorization header using the format Bearer <token>.#

Options

Show answer

True. RFC 6750 specifies that a Bearer access token is sent in the Authorization header using the format Bearer <token>. This is the standard and recommended method for transmitting OAuth 2.0 access tokens in HTTP requests.

Why:

RFC 6750 defines the OAuth 2.0 Bearer Token usage. The standard method for sending a bearer access token is via the Authorization header with the scheme Bearer followed by a space and the token value, e.g., Authorization: Bearer mF_9.B5f-4.1JqM. This is the recommended approach over alternatives like the request body or URI query parameter.

HTTP & APIs/api-auth

In HTTP API auth, a _____ status means the client did not provide valid credentials, while a _____ status means the client authenticated successfully but is not permitted to perform the requested action.#

Show answer

In HTTP API auth, a 401 Unauthorized status means the client did not provide valid credentials, while a 403 Forbidden status means the client authenticated successfully but is not permitted to perform the requested action.

Why:

401 Unauthorized is returned when authentication credentials are missing or invalid. 403 Forbidden is returned when the server recognizes the authenticated identity but that identity does not have permission to access or perform the operation on the resource. This distinction is defined in RFC 7231/9110.

HTTP & APIs/api-auth

In the OAuth 2.0 authorization code flow, an application receives a short-lived authorization code via a redirect. Which grant_type value must the application send in the back-channel token request to the authorization server to exchange that code for an access token?#

Options

Show answer

Use grant_type=authorization_code to exchange the code for an access token in the back-channel request. The client_credentials grant is for machine-to-machine calls, implicit returns a token without a code exchange, and the password grant sends raw user credentials—neither fits the authorization code flow.

Why:

In the authorization code flow, the back-channel token request must include grant_type=authorization_code along with the code and client credentials. client_credentials is for machine-to-machine access without a user, implicit (deprecated) returns a token directly in the front channel without a code exchange, and password (deprecated) sends raw user credentials directly.

HTTP & APIs/api-auth

A signed JSON Web Token (JWT) consists of three Base64Url-encoded segments separated by periods. From left to right these segments are the _____, the _____, and the _____.#

Show answer

A signed JSON Web Token (JWT) consists of three Base64Url-encoded segments separated by periods. From left to right these segments are the header, the payload, and the signature.

Why:

A JWT is structured as header.payload.signature. The header (also called the JOSE header) declares the token type and signing algorithm. The payload (also called the claims set) contains the assertions such as iss, sub, and exp. The signature is the cryptographic MAC over the first two segments using the algorithm named in the header, which the receiver verifies to confirm integrity.

HTTP & APIs/api-auth

The following Python function is supposed to extract a Bearer token from the incoming HTTP request, but it always returns None even when the client sends a valid Authorization: Bearer <token> header. Which line contains the bug?#

def extract_bearer_token(request):
    auth = request.headers.get("Authentication")
    if auth and auth.startswith("Bearer "):
        return auth[7:]
    return None
Show answer

The bug is on line 2.

Why:

Line 2 reads the header named "Authentication", but the standard HTTP header for bearer credentials is Authorization. Since no Authentication header is sent, auth is None, the if guard fails, and the function returns None. Changing the header name to "Authorization" fixes the extraction.

HTTP & APIs/api-auth

What is the OAuth 2.0 client credentials grant, and when do you use it?#

Show answer

A grant where the client authenticates to the token endpoint with its own credentials (client ID + secret, or a certificate) and receives an access token representing itself — there's no resource owner, no browser redirect, and no consent screen. Use it for machine-to-machine calls where a service needs to call another service's API on its own behalf, not a user's — e.g. a nightly batch job or a backend microservice calling another backend API.

Why:

Because there's no user involved, the client credentials grant collapses OAuth's multi-step redirect dance into a single request/response: the client presents its credentials directly and gets a token back. RFC 6749 §4.4 restricts it to confidential clients — ones that can hold a secret, i.e. server-side services, never a browser app or mobile app — and says a refresh token normally shouldn't even be issued, since the client can just re-authenticate with its own credentials to get a new access token whenever it needs one.

HTTP & APIs/api-auth

A single-page application (SPA) running in the browser needs to obtain an OAuth 2.0 access token to call a backend API on behalf of a signed-in user. The SPA cannot securely store a client secret. Which OAuth 2.0 flow is the current recommended approach for this scenario?#

Options

Show answer

Use the Authorization Code flow with PKCE. It is the current OAuth 2.0 Security BCP recommendation for public clients like SPAs that cannot store a client secret. PKCE replaces the static secret with a per-request code challenge/verifier pair, blocking authorization-code interception attacks. The Implicit grant is deprecated, Password Credentials is deprecated, and Client Credentials is for machine-to-machine, not user-delegated, access.

Why:

The Authorization Code flow with PKCE is the current OAuth 2.0 Security Best Current Practice (RFC 9700) recommendation for SPAs, native apps, and other public clients that cannot keep a client secret confidential. PKCE replaces the client secret with a dynamically generated code verifier/challenge pair, preventing an attacker from intercepting the authorization code and exchanging it for a token. The Implicit grant is deprecated because it exposes tokens in the URL fragment and offers no client authentication. The Resource Owner Password Credentials grant is deprecated because it requires the app to handle user passwords directly. The Client Credentials grant is for machine-to-machine communication where the client is acting on its own behalf, not on behalf of a user.

HTTP & APIs/api-auth

When sending an OAuth 2.0 Bearer access token in an HTTP request, the token is placed in the _____ request header using the scheme Bearer <token>. According to RFC 6750, this header name is case-insensitive but the scheme name (Bearer) is case-sensitive and must be followed by exactly one space and the token value.#

Show answer

When sending an OAuth 2.0 Bearer access token in an HTTP request, the token is placed in the **Authorization** request header using the scheme Bearer <token>. According to RFC 6750, this header name is case-insensitive but the scheme name (Bearer) is case-sensitive and must be followed by exactly one space and the token value.

Why:

RFC 6750 defines the standard method for transmitting OAuth 2.0 bearer tokens: the client places the access token in the Authorization header using the Bearer authentication scheme, formatted as Authorization: Bearer <access_token>. While HTTP header names are case-insensitive per RFC 7230, the Bearer scheme name itself is case-sensitive per RFC 6750. This is the preferred and most secure of the three methods defined in RFC 6750 (the others being URI query parameter and form-encoded body parameter, both of which have security drawbacks such as logging exposure).

HTTP & APIs/api-auth

In a signed JSON Web Token (JWT), the middle segment (the payload, between the two periods) is cryptographically encrypted, so an attacker who intercepts the token cannot read its claims.#

Options

Show answer

False. A signed JWT's payload is only Base64URL-encoded, not encrypted. Anyone who intercepts the token can decode it and read every claim. The signature provides integrity (proof of issuer and tamper resistance) but not confidentiality. To hide claim contents you need an encrypted JWT (JWE), a separate standard from the signed JWT (JWS) used by most OAuth 2.0 access tokens.

Why:

A signed JWT (JWS) has three Base64URL-encoded segments: header, payload, and signature. The payload segment is only Base64URL-encoded, NOT encrypted. Anyone who obtains the token can Base64URL-decode the payload and read all claims such as sub, exp, and roles. The signature segment only provides integrity — it proves the token was issued by someone holding the signing key and was not tampered with. To keep claims confidential, you must use an encrypted JWT (JWE), which is a different and separate specification (RFC 8317) from a signed JWT (JWS, RFC 7515). Most OAuth 2.0 access tokens formatted as JWTs are JWS, not JWE, so their payloads are readable by design.

HTTP & APIs/api-auth

A nightly batch job needs to call your internal billing API. There is no end user involved — the job authenticates as itself using a client ID and client secret issued to the service. Which OAuth 2.0 grant type is this?#

Options

Show answer

A nightly batch job with no end user involved is the client credentials grant. It exists for exactly this case: the client authenticates directly to the token endpoint with its own client ID and secret and receives an access token representing the service itself, with no resource owner, browser redirect, or consent screen involved. Authorization code and implicit grants both require a user to authenticate and consent through a browser; resource owner password credentials still uses an actual end user's password rather than a service identity.

Why:

The client credentials grant (RFC 6749 §4.4) is for exactly this case: "the client is requesting access to the protected resources under its control" with no resource owner involved at all. The client authenticates directly to the token endpoint with its own credentials and receives an access token representing the service itself, not a user — a single request/response exchange with no redirect, no consent screen, and, per the RFC, normally no refresh token, since the client can simply request a new access token with its credentials whenever needed. (a) requires a resource owner authenticating and consenting through a browser redirect — there's no user here. (c) the implicit grant is also user/browser-based — it returns a token via a redirect fragment, which doesn't fit a headless batch job either. (d) still takes an actual end user's password as the grant, just without a redirect; that's a person's credentials, not a service identity.

HTTP & APIs/api-auth

To prevent session fixation attacks, an application should assign a brand-new session ID at the moment a user authenticates, rather than continuing to use whatever session ID (if any) was already active before login.#

Options

Show answer

True. To prevent session fixation, an application should assign a brand-new session ID the moment a user authenticates, rather than reusing whatever ID was already active. In session fixation, an attacker gets a victim to start a session under an ID the attacker already knows and waits for them to log in under it; if the app keeps that ID across authentication, the attacker's known ID becomes a valid authenticated session. Regenerating the session ID at login invalidates the ID the attacker was tracking, so the attacker is left holding a dead session.

Why:

In session fixation, an attacker gets a victim to start a session with an ID the attacker already knows — for example by sending a link containing a session identifier, or because a pre-login session cookie was already set — then waits for the victim to log in under that same ID. If the application keeps using the pre-existing session ID across the authentication boundary, the attacker's known ID becomes a valid, authenticated session the moment the victim logs in, and the attacker can use it directly. OWASP's Session Management Cheat Sheet states the session ID "must be renewed or regenerated by the web application after any privilege level change," and singles out authentication as the most important trigger. Regenerating the ID at login invalidates whatever ID the attacker was tracking, so the attacker is left holding a dead session while the real user gets a fresh one only they know.

HTTP & APIs/api-auth/oauth-basics

An OAuth 2.0 authorization server issues tokens with the following JWT payload:#

Options

Show answer

The resource server must reject the token with HTTP 401 and WWW-Authenticate: Bearer error="invalid_token" because the exp claim has elapsed. RFC 7519 §4.1.4 states JWT processors MUST reject tokens where the current time is at or after exp. RFC 6750 §3.1 mandates the 401 + invalid_token response. There is no mandatory default clock-skew tolerance defined by the spec.

Why:

RFC 7519 §4.1.4 mandates that JWT processors MUST reject tokens where the current time is at or after the exp value. RFC 6750 §3.1 specifies that an expired token is an invalid_token error and the resource server MUST respond with HTTP 401 and a WWW-Authenticate header carrying error="invalid_token". The azp claim (authorized party) is checked by the client, not required by the resource server for basic validation. The aud as a URI is perfectly valid per RFC 7519 §4.1.3. RFC 7519 does acknowledge that implementors may allow small clock skew, but it is not a default tolerance, and 'MUST reject' takes precedence — accepting is never the correct answer once past exp.

HTTP & APIs/api-auth

Your service verifies incoming JWTs by checking the cryptographic signature and rejects the request if it fails. A penetration test shows it will still accept a token that is well-formed, correctly signed by your own key, but was issued for a completely different downstream service and expired ten minutes ago. What is missing from the verification?#

Options

Show answer

The missing step is validating the registered claims — exp, and where present nbf, iss, and aud — not just the signature. A valid signature only proves the token was issued by the key holder and hasn't been tampered with; it says nothing about whether the token is still within its validity window or intended for this service. RFC 7519 requires checking exp (with a small clock-skew allowance), nbf, that iss matches the expected issuer, and that this service is named in aud. A larger key size or a different signing algorithm would not have caught an expired, wrong-audience token.

Why:

A signature only proves the token was issued by the holder of the signing key and hasn't been tampered with (RFC 7519 §4.1) — it says nothing about whether the token is still within its validity window or intended for this service. Verification must also check exp (MUST NOT be accepted at or after this time, with a small clock-skew tolerance permitted), nbf if present, that iss matches the expected issuer, and that aud names this service — RFC 7519 says a recipient not identified in aud should reject the JWT. Skipping this is exactly how the prompt's bug happens: a legitimately-issued token for a different service gets replayed here, or an expired token close to real time keeps being honored. (c) and (d) touch signing-algorithm strength, not claims validation, and neither would have caught an expired, wrong-audience token.

HTTP & APIs/api-auth

A mobile app implements OAuth 2.0's authorization code flow. It cannot safely store a client_secret inside the compiled app binary, and on some platforms a malicious app can register the same custom URI scheme used for the OAuth redirect and intercept the returned authorization code. What does adding PKCE (Proof Key for Code Exchange) actually fix here?#

Options

Show answer

PKCE ties the authorization request to the token exchange with a client-generated secret — the code_verifier and its derived code_challenge — so an attacker who intercepts the authorization code still can't redeem it without the verifier. Public clients like native and mobile apps can't hold a client_secret the way a confidential server-side client can, so PKCE closes the interception gap without one: the client sends a code_challenge up front, and the token endpoint later requires the matching code_verifier before it will exchange the code. It is not encryption of the code, not a switch to the client credentials grant, and not simply a shorter code lifetime.

Why:

RFC 7636 exists because "OAuth 2.0 public clients utilizing the Authorization Code Grant are susceptible to the authorization code interception attack" — public clients like native/mobile apps can't hold a secret the way a confidential server-side client can, so anyone who intercepts the code (e.g. a malicious app claiming the same custom URI scheme) could otherwise redeem it directly. PKCE closes that gap without a shared secret: before redirecting, the client generates a random code_verifier, derives a code_challenge from it (normally SHA-256, method S256), and sends the challenge with the authorization request. The token endpoint later requires the original code_verifier, checks it against the stored challenge, and rejects the exchange if it doesn't match. An attacker who only has the intercepted code — not the verifier, which never left the legitimate app until the token request — cannot complete the exchange. (a) mischaracterizes PKCE as encryption; it's a proof-of-possession check, not confidentiality. (c) picks the wrong grant type — client credentials has no user and no redirect at all, so it can't drive a user-delegated mobile flow. (d) is a real complementary practice but is not what PKCE itself does.

HTTP & APIs/api-auth

Your API authenticates state-changing requests (e.g. POST /transfer) using a session cookie the browser sends automatically. Select all of the following that are genuine, effective mitigations against CSRF for this design.#

Options

Pick every one that applies.

Show answer

SameSite=Strict/Lax and a per-session CSRF token are genuine CSRF mitigations; HttpOnly and JWT key rotation are not. CSRF works because browsers attach cookies automatically to any request to the target origin regardless of which page triggered it. SameSite withholds the cookie on the cross-site requests it covers, and a CSRF token is unpredictable and unreadable by the attacker's page, so a forged request is missing it. HttpOnly only stops JavaScript from reading the cookie — an XSS defense — and does nothing to stop the browser's automatic cross-site attachment, so it does not stop CSRF. Signing-key rotation is unrelated to cookie-based CSRF exposure.

Why:

CSRF exploits the fact that browsers attach cookies automatically to any request to the target origin, regardless of which page triggered it. SameSite=Strict/Lax (a) directly targets that: the browser withholds the cookie on the cross-site requests each setting covers, so a forged cross-site POST from an attacker's page arrives with no session cookie at all. A CSRF token (b) works differently but is equally effective: the attacker's page can't read or set a value it never received — the same-origin policy blocks reading responses, and a synchronizer or signed double-submit token is unpredictable and bound to the user's session — so a forged request is missing it or carries the wrong one and gets rejected. HttpOnly (c) is a real, valuable defense but against a different threat: it stops injected JavaScript (XSS) from reading and exfiltrating the cookie. It does nothing to stop the browser's normal, automatic attachment of the cookie to a cross-site request, which is the entire CSRF mechanism, so relying on HttpOnly alone leaves CSRF wide open. Rotating a JWT signing key (d) is unrelated to cookie-based session auth's CSRF exposure entirely — it limits the blast radius of a compromised key, not forged requests riding a legitimately ambient cookie.

HTTP & APIs/api-auth

With refresh token rotation, if a refresh token that was already exchanged (and should therefore be invalid) is presented again, the authorization server should treat this as a signal of possible token theft and revoke the entire family of tokens descended from it — not just silently reject that one request.#

Options

Show answer

True. Under refresh token rotation, each refresh token is used exactly once — using it issues a new one and invalidates the old. A second presentation of an already-invalidated token means someone besides the legitimate holder has a copy, so RFC 9700 directs the server to treat that reuse as a compromise signal and revoke the whole token family descended from a common ancestor, not just reject the one reused request. Revoking only the single request would leave an attacker holding a later rotated token still able to use it undetected.

Why:

Rotation means every use of a refresh token issues a new one and invalidates the one just used, so under normal operation each refresh token is presented exactly once. A second presentation of an already-invalidated token means either the legitimate client and an attacker both have a copy, or the attacker got there first — in either case, something is compromised. RFC 9700 (OAuth 2.0 Security Best Current Practice, §4.14) directs that this reuse be treated as a compromise signal and that the server revoke not just the reused token but the whole token family descended from a common ancestor, so a token an attacker is holding — rotated or not — stops working, forcing re-authentication. Silently rejecting just the one reused request and leaving the rest of that lineage valid would let an attacker who already rotated forward once keep using their newer token undetected.

HTTP & APIs/api-auth

A teammate's login handler has a session-fixation bug: an attacker who gets a victim to start a session before login can reuse that same session as an authenticated one after the victim signs in. Which line is responsible?#

1| app.post("/login", (req, res) => {
2|   const { username, password } = req.body;
3|   if (!verifyCredentials(username, password)) {
4|     return res.status(401).send("Invalid credentials");
5|   }
6|   req.session.userId = getUserId(username);
7|   req.session.authenticated = true;
8|   res.send("Logged in");
9| });

Options

Show answer

Line 6 — it writes into the pre-existing req.session instead of first regenerating it to get a fresh session ID

Why:

Nothing in this handler ever issues a new session ID at the point of authentication — it just writes userId and authenticated onto whatever session (and therefore whatever session ID) the request already carried, which could be one the attacker set on the victim's browser before login, whether via a fixation link or simply an existing pre-login cookie the attacker also holds. Once the victim authenticates, that same, attacker-known session ID becomes a valid authenticated session, and the attacker can use it directly without ever learning the victim's password. The fix is to regenerate the session — issue a brand-new session ID, migrating over only the data you intend to keep — right after credentials are verified and before marking the session authenticated, e.g. req.session.regenerate(...) in Express, or the framework's equivalent. (b) describes a real but different timing-attack concern about comparing credentials, unrelated to session identity. (c) a plain-text response body has no bearing on which session ID gets used. (d) a missing null check would cause a crash on malformed input, not a session-fixation vulnerability — a different bug class entirely.

HTTP & APIs/api-auth

Explain how PKCE works mechanically (code_verifier and code_challenge), and specifically what class of attack it defends against, and for which kind of OAuth client.#

Show answer

PKCE protects public clients — apps like SPAs and native/mobile apps that can't safely hold a client_secret — against authorization code interception. Before redirecting the user to the authorization server, the client generates a random code_verifier and derives a code_challenge from it (typically SHA-256, method S256), sending the challenge along with the authorization request. The authorization server stores the challenge against the code it issues. When the client later calls the token endpoint to exchange the code for tokens, it must also send the original code_verifier; the server re-derives the challenge from it and only issues tokens if it matches what was stored. An attacker who intercepts just the redirect-borne authorization code doesn't have the verifier — which never left the legitimate client until the token request — so they can't complete the exchange even with a valid code in hand.

Why:

PKCE (RFC 7636) exists because OAuth 2.0 public clients using the authorization code grant are "susceptible to the authorization code interception attack" — without a client secret to prove identity at the token endpoint, anyone who intercepts the code can redeem it. The verifier/challenge pair acts as a one-time, client-generated substitute for a secret: the challenge (sent up front) is a one-way function of the verifier (sent only at the end), so binding the code to the challenge lets the token endpoint confirm the same client that started the flow is the one finishing it, without ever transmitting a reusable shared secret. This matters most for mobile/native apps, where OS-level redirect interception is a real risk, and for SPAs, whose JS is fully inspectable so no secret is truly private — current OAuth security guidance recommends PKCE for all authorization-code clients, confidential or public, as defense in depth.

HTTP & APIs/api-auth

Explain why production OAuth systems rotate refresh tokens on every use, and what a correctly-implemented authorization server should do if it sees a refresh token used a second time.#

Show answer

Rotation means every time a refresh token is redeemed for a new access token, the server also issues a brand-new refresh token and invalidates the one just used, so each refresh token is only ever valid for a single use. This turns a stolen refresh token into a detectable event instead of silent, indefinite access: if an attacker copies a refresh token and later exchanges it, and the legitimate client also tries to use its own now-superseded copy (or vice versa), the server sees a token being reused after it was already exchanged. A correctly-implemented server treats that reuse as evidence of compromise and revokes the entire family of tokens descended from that lineage, not just the one reused token, forcing everyone holding any token in that chain, attacker included, to re-authenticate. Without rotation, a leaked long-lived refresh token just keeps working forever with no way to tell legitimate use from theft.

Why:

This is RFC 9700's (OAuth 2.0 Security Best Current Practice) guidance in §4.14: rotating refresh tokens converts them from a long-lived bearer secret into a chain where only the most recent link is valid, and reuse of an earlier link is a reliable compromise signal precisely because legitimate clients never present the same refresh token twice. Revoking the whole family, not just the reused token, matters because an attacker who already rotated forward once — using the stolen token before the legitimate client did — is now holding a different, currently-valid refresh token; revoking only the specific token that got reused would leave that attacker's newer token untouched.

HTTP & APIs/api-auth

Order the steps of the OAuth 2.0 authorization code flow with PKCE, from the client preparing the request to it finally calling the resource API.#

Put these in order

Show answer

The PKCE-augmented authorization code flow runs in this order:

  1. The client generates a random code_verifier and derives a code_challenge from it.
  2. The client redirects the user to the authorization endpoint with the code_challenge and its method.
  3. The user authenticates and grants consent.
  4. The authorization server redirects back with a short-lived authorization code, having stored the code_challenge against it.
  5. The client calls the token endpoint with the code and the original code_verifier.
  6. The authorization server re-derives the challenge from the verifier and checks it matches before issuing tokens.
  7. The client calls the resource server's API with the access token.

PKCE only adds the challenge (sent up front) and the verifier (sent only at the final exchange) — the verifier never appears in the front-channel redirect, which is what protects it from interception.

Why:

PKCE inserts exactly two things into the ordinary authorization-code flow: a challenge generated and sent before the redirect, and a verifier sent and checked at the token exchange — everything else (the redirect to the authorization server, user consent, the code coming back, the client finally calling the resource API) is unchanged from the plain flow. The ordering is what makes it secure: the verifier is only ever transmitted once, at the very end, over the trusted token-endpoint channel — never in the front-channel redirect where it, like the authorization code itself, could be intercepted. If a copy of the code leaks from the redirect step, whoever has it still lacks the verifier generated in step one and held by the legitimate client, so the exchange fails for them.

HTTP & APIs/api-auth

You're building a single-page app (SPA) that runs entirely in the browser and needs to call a third-party API on behalf of the signed-in user (e.g. read their calendar from another provider). The SPA has no backend of its own. Which approach is appropriate?#

Options

Show answer

Use authorization code flow with PKCE: the SPA redirects to the third party's consent screen and exchanges the returned code for a token itself, using a code_verifier instead of a client_secret. As a public client, the SPA can't hold a secret, and it needs the user's actual consent to access their third-party data — client credentials only authenticates the app itself, with no user involved, so it can't act on the user's behalf. Never embed a client_secret in browser JavaScript, never forward the user's real password to a third party, and avoid the implicit grant, which exposes the access token in the redirect URI with no code-exchange step.

Why:

The SPA is a public client (its JS bundle is entirely inspectable, so it cannot hold a secret) that needs delegated, user-consented access to another service — that's precisely the authorization code flow's use case, and PKCE is what makes it safe for a client that can't hold a client_secret: the browser redirects to the third party's consent screen, gets a code back, and the SPA exchanges that code with a code_verifier instead of a secret. (b) is wrong on two counts: client credentials has no user consent at all (it authenticates the app, not the user, so it can't act "on behalf of the signed-in user"), and any secret shipped in browser JavaScript is trivially extractable — never embed one client-side. (c) is the password anti-pattern OAuth was invented to replace: it hands your app the user's actual third-party password, far broader access than the API scope you need. (d) — the implicit grant returns the access token directly in the redirect URI fragment with no code-exchange step, exposing it to browser history and referrer leakage; current OAuth security guidance recommends authorization code with PKCE over implicit for browser-based apps specifically because of this exposure.

HTTP & APIs/api-auth

This server-side JWT verification function has a critical flaw that lets an attacker forge a token that passes verification without ever knowing the server's private signing key. Which line is responsible?#

1| function verifyToken(token, publicKey) {
2|   const [headerB64, payloadB64, signatureB64] = token.split(".");
3|   const header = JSON.parse(base64UrlDecode(headerB64));
4|   const payload = JSON.parse(base64UrlDecode(payloadB64));
5|   const signedData = headerB64 + "." + payloadB64;
6|   const valid = crypto.verify(header.alg, signedData, signatureB64, publicKey);
7|   if (!valid) throw new Error("invalid signature");
8|   return payload;
9| }

Options

Show answer

Line 6 — it verifies using the algorithm named in the token's own header instead of a fixed algorithm the server expects

Why:

The function lets the token itself dictate which algorithm verifies it (header.alg) instead of the server hardcoding the one algorithm it actually issues tokens with. This is the classic JWT "algorithm confusion" flaw: if the server expects asymmetric RS256 (signed with a private key, verified with a public key) but the verification code will run whatever crypto.verify supports for header.alg, an attacker can set alg to HS256 (a symmetric HMAC algorithm) and sign the token using the server's own public key as the HMAC secret — which is, by definition, not secret at all, since it's meant to be shared. Many JWT libraries' generic "verify with whatever alg is in the header" entry point makes this mistake easy to write; some implementations go further and also accept alg: "none", which has no signature at all, so any payload is accepted outright. The fix is to never let the token choose its own verification algorithm: pin the expected algorithm (e.g. always verify as RS256 with the known public key) at the call site, and reject any token whose header doesn't match. (b) parsing before verifying is standard practice (you need the header to know things like kid) and by itself introduces no forgery path. (c) an error message describing only "invalid signature" leaks essentially no attacker-useful information — a real but minor hardening nitpick, not what enables forgery. (d) a malformed token would just throw during parsing before reaching a security-relevant check — a crash on bad input is a different bug class from accepting a forged-but-well-formed token as valid.

Sources

The official documentation these questions are checked against:

Related interview questions

Job market

See http-apis salaries and hiring demand from live job postings.

The other 15 questions

This page shows 25 and marks what you pick. That's as far as a page can go. A free account opens the other 15 and keeps every answer. What you miss comes back until it's right: after a day, then at longer gaps.

Start with this topic

Free · the whole bank · 100 marked answers per 30 days · written feedback on the paid plan

What moved, monthly

One email a month when the bulletin comes out: what moved in the markets we track, and the new question topics we published. Confirm your address to join. Unsubscribe any time.