Interview Bank · Lesson 8

Authentication & authorization — interview questions

49 questions · Core / Senior / Staff · pairs with Lesson 8

API authAuthNAuthZStatus codesHTTP methods

Published

Use these as active recall, not reading. Read the question, answer it out loud or in writing from memory first, and only then reveal the model answer to grade yourself. Recognizing an answer ≠ being able to produce it under pressure. Filter by difficulty; reveal-all only for a final review pass.

Define authentication vs authorization.

Authentication (AuthN) answers "who are you?" — proving identity from a credential. Authorization (AuthZ) answers "what may you do?" — deciding whether a known principal may perform this action on this resource. AuthN happens first; AuthZ runs on every protected operation after.

Which status code maps to a missing/invalid credential vs an authenticated-but-not-permitted request?

Missing or invalid credential → 401 Unauthorized (a historical misnomer; it really means unauthenticated). Known principal who isn't permitted → 403 Forbidden.

Give the one-line senior framing of 401 vs 403.

401 = come back with valid identity; 403 = your identity is fine, the answer is still no. A single request can pass AuthN and fail AuthZ on the very next line — and the most expensive bugs live in that gap.

What is an API key and where is it appropriate?

A shared secret sent in a header (e.g. X-API-Key or Authorization). Fine for simple server-to-server identification of a calling app, and for low-stakes public read APIs with quotas. It identifies the app, not an end user.

What are the weaknesses of API keys?

They're coarse (hard to scope to one action), hard to rotate (often hardcoded by integrators), long-lived, and easy to leak. They prove possession, not identity-with-permissions. Mitigations: scope keys, support multiple active keys to enable rotation, set quotas, and treat them as secrets.

What is a bearer token, its core risk, and how do you contain it?

"Whoever bears it is granted access," sent as Authorization: Bearer <token> (header, never a query string). Core risk: it's not bound to the sender — anyone who steals one can replay it. Containment: TLS everywhere, short expiry, keep it out of logs/URLs, and for high security use sender-constrained tokens (DPoP or mTLS-bound) so a stolen token can't be replayed from another client.

Is OAuth2 an authentication protocol?

No. OAuth 2.0 is a delegated authorization framework: it lets a user grant an app scoped access to their resources without sharing a password. Treating an OAuth access token as proof of identity is a classic gotcha — the access token is for the resource server, not for identifying the user to the app.

What does OIDC add on top of OAuth2?

An identity layer. OIDC standardizes an id_token (a JWT about the user with claims like sub, email) plus a /userinfo endpoint, so you can actually authenticate. Rule of thumb: OAuth2 = authorization; OIDC = authentication. "Sign in with Google" is OIDC.

What is mTLS and when do you reach for it?

Mutual TLS: both ends present X.509 certificates, so each authenticates the other at the transport layer. Reach for it in service-to-service traffic (internal mesh, partner backends) where you want strong, replay-resistant identity without bearer secrets. Cost: certificate lifecycle/rotation and PKI overhead; rarely used for public browser clients.

mTLS vs signed JWTs for service-to-service auth — how do you choose?

mTLS authenticates the connection at the transport layer — strong, sender-bound, great inside a service mesh, but it tells you which service connected, not on whose behalf. Signed JWTs carry application-level claims (caller, scopes, end-user context, audience) and survive proxy hops, but are bearer by default (replayable if leaked). Mature systems combine them: mTLS for channel identity + a JWT for fine-grained, propagated authZ context.

Which grant for a user signing in via web/mobile/SPA?

Authorization Code + PKCE. The user authenticates at the authorization server, the app receives a short-lived code, then exchanges it for tokens. PKCE is now recommended for all Authorization Code clients, public and confidential.

What problem does PKCE solve, and how?

It protects public clients (SPAs, mobile apps that can't keep a secret) against authorization-code interception. The client generates a random code_verifier, sends its hash (code_challenge) on the auth request, and must present the original verifier at token exchange. An intercepted code is useless without the verifier, binding the code to the requester.

Which grant for machine-to-machine with no user?

Client Credentials. The service authenticates as itself with its own client id/secret (or a client cert) to get an access token. The right grant for partner backends, cron jobs, and internal services — there is no resource owner to delegate from.

Why are the Implicit and Password grants deprecated — what replaces them?

Implicit returned the token in the redirect URL fragment (no code exchange, no refresh, exposed to history/scripts). Resource-Owner Password makes the app handle the user's actual password — defeating delegation, blocking MFA/SSO. Both are superseded by Authorization Code + PKCE, even for SPAs. Naming either as a default is a red flag.

What are scopes?

Coarse, named permissions attached to a token (e.g. orders:read, orders:write) describing what the token is allowed to do. They implement least privilege: an app only requests the scopes it needs, and the resource server enforces them.

What is the aud (audience) claim and why must a resource server check it?

aud names the intended recipient(s) of the token. A resource server must verify the token was issued for it — otherwise a token minted for service A could be replayed against service B (the "confused deputy"). Audience-checking is what stops one service's token from being a skeleton key for another.

Access token vs refresh token — what's the difference?

The access token is short-lived and sent on every API call to prove authorization. The refresh token is long-lived, kept private, and used only against the auth server to mint new access tokens without re-prompting the user. Splitting them limits the blast radius of a leaked access token.

What is refresh-token rotation and what does it detect?

Each time a refresh token is used, the auth server issues a new one and invalidates the old. If an already-used refresh token is presented again, that signals theft (two parties hold the same token) — the server revokes the whole token family and forces re-auth. Rotation turns a stolen refresh token into a detectable, single-use event.

Where should a SPA store tokens, and why is that contentious?

localStorage is XSS-exploitable; in-memory loses tokens on refresh. The modern recommendation is the Backend-For-Frontend (BFF) pattern: tokens live server-side, the browser holds only a HttpOnly+Secure+SameSite session cookie, and the BFF attaches the access token to upstream calls. This keeps tokens out of JS entirely, trading a little architecture for a much smaller XSS blast radius.

What are the three parts of a JWT?

header.payload.signature — three base64url segments joined by dots. The header names the algorithm, the payload carries claims (sub, iss, aud, exp…), and the signature lets any holder of the key verify integrity and authenticity.

Is a JWT payload encrypted?

No. A standard (JWS) JWT payload is base64url-encoded, not encrypted — anyone can decode and read it. The signature guarantees it wasn't tampered with, not that it's secret. So never put secrets or PII you don't want exposed in a JWT. (JWE exists for encryption but is rarely used for API tokens.)

Why do stateless JWTs fit REST so naturally?

Because the constraint of statelessness says each request carries everything the server needs. A signed JWT is self-contained: any node verifies it locally with the public key — no session lookup, no shared store on the hot path. That's exactly the horizontal-scaling story REST wants.

What is the chief weakness of a JWT versus a server-side session?

Revocation. A self-contained token is valid until exp, no matter what — there's no row to delete. A server-side session is the opposite: trivial to revoke (delete it), but stateful (needs a shared store or sticky sessions). The design tension is pure statelessness vs instant logout.

How do you handle JWT revocation honestly?

Combine: short-lived access tokens (minutes) to shrink the blast radius, rotating refresh tokens for continuity, and a denylist or token introspection for emergency "log this device out now." Short expiry handles the common case cheaply; the denylist handles the urgent case at the cost of reintroducing a little state.

List the checks of a proper JWT validation.

(1) Verify the signature with the correct key. (2) Pin the algorithm — reject alg: none and algorithm-confusion. (3) Check iss (issuer), aud (audience is you), and exp/nbf (time window). Optionally check sub, scopes, and a denylist. A JWT you don't fully validate is just an attacker-supplied JSON blob.

Explain the alg: none and algorithm-confusion attacks.

alg: none: attacker sets the header algorithm to none and strips the signature; a naive verifier that trusts the header accepts an unsigned token. Algorithm confusion (RS256→HS256): the server expects an RSA-signed token (verify with the public key) but the attacker sends an HS256 token signed with that public key as the HMAC secret; a library that picks the algorithm from the header validates it. Defense: pin the expected algorithm(s) server-side and never let the token's header choose how it's verified.

Symmetric (HS256) vs asymmetric (RS256/ES256) signing — when each?

HS256 uses one shared secret to sign and verify — simple and fast, but everyone who can verify can also forge, so it only suits a single trust domain. RS256/ES256 sign with a private key and verify with a public key (published via JWKS), so many resource servers can validate without holding signing power. For multi-service / third-party validation, use asymmetric and rotate keys via the JWKS kid.

RBAC vs ABAC — what's the difference?

RBAC assigns permissions by role (admin, editor) — simple, coarse: "editors can publish." ABAC decides from attributes of subject/resource/environment (department, region, time, owner) — fine-grained and dynamic: "the owner can edit during business hours." RBAC is easier to reason about; ABAC scales to nuanced policy at the cost of complexity.

What is object-level authorization and why is it the one that bites?

It verifies the authenticated principal may act on this specific object, not merely that they're authenticated or role-permitted. It bites because RBAC/ABAC answer "can this kind of user do this kind of thing" but not "does this user own this record." Skipping it is OWASP API #1 — BOLA — the most common API breach.

What is function-level authorization (and BFLA)?

Function-level authZ checks whether a principal may invoke a particular operation/endpoint at all (e.g. an admin-only DELETE). BFLA — Broken Function Level Authorization (OWASP API5) is when a regular user reaches admin or privileged functions just by guessing the route or swapping the HTTP method, because the check is missing. Defense: deny by default and enforce role/permission per function, including non-GET methods.

How do you keep authorization consistent across dozens of microservices?

Externalize policy: a centralized decision model (e.g. a policy engine / OPA-style PDP, or a relationship-based system à la Google Zanzibar for object-level checks) with services as enforcement points. Propagate identity and scopes via signed tokens, version policies, and test them. The anti-pattern is ad-hoc if checks copy-pasted per service — they drift, and one missed check is a BOLA.

What is the #1 risk on the OWASP API Top 10 (2023)?

API1: BOLA — Broken Object Level Authorization. The endpoint confirms you're authenticated (and maybe role-permitted) but never checks you own the specific object, so changing an id returns someone else's data. It's #1 because it's pervasive and high-impact.

Walk through a BOLA on GET /accounts/{id} and the fix.

Attacker is authenticated as user A, calls GET /accounts/123 (their own), then changes it to GET /accounts/124 and reads user B's account — because the handler only checked authentication. Fix: on every object access, verify the authenticated principal is authorized for that exact object (tenant/owner check), enforced at the data layer. Unguessable ids are not authorization — that's obscurity.

What is BOPLA (API3) and a concrete example?

Broken Object Property Level Authorization — the caller may access the object but not all its properties. Two faces: excessive data exposure (returning fields like passwordHash or another user's PII) and mass assignment (letting a client set fields they shouldn't, e.g. "isAdmin": true via the update body). Fix: explicit allow-lists for readable and writable fields; never bind request bodies straight onto your model.

What is Broken Authentication (API2)?

Weaknesses in the auth mechanism itself: weak/missing token validation, accepting alg: none, no rate limit on login (credential stuffing/brute force), tokens that don't expire, weak password reset, or secrets in code. Defenses: strong token validation, login rate limiting + lockout/MFA, short token lifetimes, and standard, well-tested auth libraries.

What is Unrestricted Resource Consumption (API4) and how do you defend it?

No limits on the resources a request can consume → DoS or runaway cost (huge page sizes, expensive queries, unbounded uploads, fan-out). Defenses: rate limiting (429 + Retry-After), pagination caps, payload-size and timeout limits, query-complexity limits, and quotas per principal. It also covers financial DoS in pay-per-use backends.

What is SSRF (API7) in an API context?

Server-Side Request Forgery: the API fetches a user-supplied URL (webhook, image-from-URL, importer) and the attacker points it at internal targets — cloud metadata (169.254.169.254), internal services, localhost. Defenses: allow-list destinations, block private/link-local ranges, resolve-then-validate to defeat DNS rebinding, disable redirects to internal hosts, and isolate the egress.

Why did the 2023 OWASP API list elevate authorization risks to the top?

Because real-world API breaches are dominated by authorization flaws, not injection. APIs expose object identifiers directly and are hit programmatically, so missing per-object/per-property/per-function checks (BOLA, BOPLA, BFLA) leak data at scale. The list reflects that authZ — especially object-level — is where APIs actually fail, which is why interviewers hammer it.

Why is TLS non-negotiable for token-based auth?

A bearer token over plaintext can be sniffed and replayed — game over. TLS encrypts the channel so credentials and tokens can't be read in transit. No exceptions, including internal hops (assume the network is hostile / zero-trust).

What do the HttpOnly, Secure, and SameSite cookie flags do?

HttpOnly: JavaScript can't read it (mitigates token theft via XSS). Secure: sent only over HTTPS. SameSite (Lax/Strict): restricts sending on cross-site requests (mitigates CSRF). Together they keep a session cookie out of scripts and off cross-site requests.

XSS vs CSRF — which cookie flag addresses which, and what's the residual risk?

XSS (malicious script reads the token) → HttpOnly stops JS access. CSRF (browser auto-sends the cookie on a forged cross-site request) → SameSite (plus CSRF tokens / origin checks). Residual: SameSite=Lax still allows top-level GET navigations, and a successful XSS can act as the user even with HttpOnly — so you still need to prevent XSS, not just contain it.

What does least-privilege scoping look like in practice?

Grant each credential only the scopes/permissions it actually needs — a read-only integration gets orders:read, not orders:write. It shrinks what a leaked token can do. The default request should be the narrowest set, widened only on demonstrated need.

Design auth for a multi-tenant SaaS used by end users and partner backends.

Standardize on OAuth2. End users (web/mobile/SPA): Authorization Code + PKCE with OIDC for identity. Partner backends: Client Credentials (or mTLS), no user in the loop. Issue short-lived signed JWT access tokens + rotating refresh tokens. Validate signature/alg/iss/aud/exp on every node (stateless, scales out). Scopes give coarse authZ; the critical part is object-level tenant checks to shut down BOLA. Revocation via short expiry + denylist/introspection. Hygiene: TLS everywhere, tokens only in the Authorization header, secrets rotated, mTLS between services.

Concretely, how do you prevent BOLA on /accounts/{id} in a multi-tenant app?

Derive the tenant/owner from the authenticated token, never from the request, and scope the query: WHERE id = :id AND tenant_id = :tokenTenant — so a foreign id simply returns nothing. Enforce it in a shared data-access layer so no endpoint can forget. Add tests that try cross-tenant access. Random ids are a speed bump, not the control.

A token has leaked. Walk through your response.

Immediate: revoke it — add to the denylist / kill the session, and if it's a refresh token, revoke the whole token family (rotation makes prior tokens invalid). If a signing key leaked, rotate the key (JWKS kid) so all tokens signed by it stop verifying. Then: force re-auth for affected principals, rotate any related secrets, audit logs for the token's use, and tighten the leak source (logs/URLs/storage). Short expiry is why this is survivable.

Public API: API keys or OAuth2?

Depends on stakes. API keys for simple, read-mostly public data with quotas — low friction for developers. OAuth2 once you have user data, write actions, or per-user delegation: you need scopes, short-lived tokens, and revocation. A common path is keys for app identification plus OAuth for user-scoped actions. The senior move is naming the trade-off (developer friction vs granularity/revocability), not picking dogmatically.

Design single sign-on (SSO) across several of your own apps.

Central identity provider speaking OIDC (or SAML for enterprise). Each app is an OIDC relying party using Authorization Code + PKCE; the IdP holds the session and issues per-app tokens with the right aud and scopes. Benefits: one login, central MFA/policy, central revocation. Watch-outs: single sign-out is genuinely hard, the IdP is now a critical dependency (HA + careful key rotation), and don't share one access token across apps — mint per-audience tokens.

An interviewer says "we'll just use unguessable UUIDs instead of authorization checks." Respond.

Push back: that's security through obscurity, not authorization. IDs leak — in logs, Referer headers, shared links, error messages, prior responses, and the wire. A UUID raises the guessing cost but does nothing once an id is known, and gives zero protection against an authenticated insider. UUIDs are fine for non-enumerability, but you still do the per-object owner/tenant check on every access.

How do you propagate end-user identity through a chain of microservices safely?

Don't let downstream services blindly trust a header. Options: forward the original signed access token (each hop re-validates signature + aud), or use token exchange (RFC 8693) to mint a downstream-audience token, all over mTLS so the channel identity is also proven. Each service enforces its own authZ — never assume the caller already checked. The anti-pattern is a plaintext X-User-Id header any internal caller can forge.

Design-round framework — drive any API auth prompt through these, out loud:
  1. Clarify actors & trust boundaries: end users (web/mobile/SPA) vs machine clients (partner backends, services), single- vs multi-tenant, regulatory constraints.
  2. Authentication: pick grants per actor — Authorization Code + PKCE (+ OIDC for identity) for users, Client Credentials or mTLS for machines.
  3. Token strategy: short-lived signed JWT access tokens + rotating refresh tokens; choose HS256 (one domain) vs RS256/ES256 + JWKS (multi-service); pin alg, check iss/aud/exp.
  4. Authorization layers: coarse scopes + role/attribute checks, then the one that bites — per-object owner/tenant checks to shut down BOLA — enforced in a shared data layer, deny-by-default per function.
  5. Revocation & key lifecycle: short expiry + denylist/introspection for emergency logout; JWKS kid rotation for signing keys.
  6. Transport & hygiene: TLS everywhere (incl. internal hops), tokens only in the Authorization header, secrets rotated, sensitive fields never in a JWT or logs.
  7. Threat model & observability: walk the OWASP API Top 10 (BOLA/BOPLA/BFLA, broken auth, SSRF); audit-log auth decisions; test cross-tenant and privilege-escalation paths.
Design end-to-end authentication and authorization for a multi-tenant SaaS API serving both end users and partner backends.

A strong answer covers: separate the actors first — interactive users vs machine-to-machine partners have different grants → standardize on OAuth2: end users via Authorization Code + PKCE with OIDC for identity (an id_token with sub/email), partner backends via Client Credentials or mTLS with no user in the loop → issue short-lived signed JWT access tokens + rotating refresh tokens; sign with RS256/ES256 and publish a JWKS so every resource server validates locally without holding signing power — preserving REST statelessness → on every request, validate signature, pin the algorithm (reject alg: none/confusion), and check iss, aud (audience is you, to stop confused-deputy replay), and exp/nbf → authorization in layers: scopes for coarse "what the token may do," role/attribute checks, and critically the per-object tenant/owner check derived from the token (WHERE id=:id AND tenant_id=:tokenTenant) enforced in a shared data layer so no endpoint can forget — this is what shuts down BOLA, the #1 OWASP API risk → revocation: short expiry for the common case + a denylist/introspection for emergency logout, family revocation on refresh-token reuse, JWKS kid rotation if a key leaks → hygiene: TLS everywhere including internal hops, tokens only in headers, secrets rotated, no PII in the JWT → name the trade-off: stateless JWTs scale out but make instant revocation hard, which the short-expiry + denylist combo resolves at the cost of a little state.

Design the authorization model and OWASP-API-Top-10 defenses for a public REST API exposing per-user resources by id.

A strong answer covers: start from the threat model — APIs expose object ids directly and are hit programmatically, so authorization (not injection) is the dominant breach class → authenticate with bearer tokens over TLS, but treat authentication as table stakes; the work is authorization → defend API1 BOLA with a per-object owner/tenant check on every access, derived from the token, in a shared data-access layer; make clear that unguessable UUIDs are obscurity, not a control → defend API3 BOPLA with explicit allow-lists for readable and writable fields — never bind request bodies straight onto the model (mass assignment like "isAdmin":true) and never over-return (passwordHash, others' PII) → defend API5 BFLA by denying by default and enforcing role/permission per function including non-GET methods, so a user can't reach admin routes by guessing a path or swapping a verb → defend API4 Unrestricted Resource Consumption with rate limiting (429 + Retry-After), pagination caps, payload-size/timeout/query-complexity limits, and per-principal quotas (also stops financial DoS) → defend API2 Broken Authentication with strict token validation (pin alg, check iss/aud/exp), login rate limiting + MFA, short token lifetimes → if the API fetches user-supplied URLs, defend API7 SSRF by allow-listing destinations, blocking private/link-local ranges, and resolve-then-validate against DNS rebinding → cross-cutting: return safe errors (no stack traces / SQL), use 404 over 403 where existence itself is sensitive, audit-log authorization decisions, and add automated cross-tenant / privilege-escalation tests → name the trade-off: centralizing policy (a PDP / Zanzibar-style relationship model) keeps checks consistent across services but adds a dependency vs fast-but-drift-prone per-service if checks.