Representative interview topic

Backend Interview: How Would You Secure OAuth 2.0 Pushed Authorization Requests?

BackendHard
Offer.cc Editorial TeamPublished Updated

Question

How would you introduce PAR for OAuth clients carrying fine-grained permissions and sensitive transaction context while protecting request_uri, redirect URIs, replay, PKCE, errors, and rollback?

Prompt and applicable context

You own an OAuth 2.0 authorization server. Mobile and enterprise web clients carry fine-grained scopes, resource indicators, and payment context, but the team does not want the full request in a browser URL. Design a Pushed Authorization Request (PAR) endpoint. Cover client authentication, request_uri generation and binding, expiry, one-time use, replay, redirect-URI validation, PKCE, errors, rate limits, and rollback.

This fits backend, identity-platform, and payments interviews. RFC 9126 defines PAR as a direct client-to-authorization-server push that returns a request_uri for a later browser authorization request. A strong answer also says PAR does not remove the authorization server’s need to validate the later request.

What the interviewer assesses

  • Drawing the boundary between the client, PAR endpoint, browser authorization endpoint, and token endpoint.
  • Separating client authentication, user authentication, request integrity, and token binding.
  • Handling request_uri guessing, swapping, replay, expiry, and redirect-URI attacks.
  • Putting PKCE, state, nonce, JAR, and PAR at the correct layers.
  • Making TTLs, idempotency, rate limits, audit, and canary rollout operational.

RFC 9700 summarizes current OAuth 2.0 security best practices. Amazon’s SDE II preparation material emphasizes reliability, accuracy, efficiency, scalability, and security in system design. The interview signal is turning those standards into running service boundaries.

Clarifying questions

  1. Is the client confidential or public? Can a mobile client protect a client secret, or must it use PKCE and platform binding?
  2. What sensitive data is in the request? Is a signed or encrypted JAR required, and which fields must be checked at PAR time?
  3. What are the request_uri lifetime, refresh allowance, per-client concurrency, and global rate-limit targets?
  4. Are redirect URIs fixed and pre-registered, or does an enterprise tenant need dynamic registration?
  5. Must every client send authorization parameters through PAR? How will legacy clients migrate?
  6. Should browser refresh, back navigation, cancellation, and network retry read the same request again?

30-second answer framework

The client sends an HTTPS POST to PAR, authenticates, and validates redirect URI, scope, resource, and PKCE parameters. The server creates a high-entropy, short-lived, client-bound request_uri and returns JSON. The browser then sends only client_id and request_uri to the authorization endpoint. That endpoint still revalidates the request, state/nonce, user consent, and PKCE before consuming the reference. Rate limits, audit, one-time-use policy, compatibility flags, and canary metrics control the rollout.

Step-by-step deep answer

1. Define the two-hop protocol

The first hop is a direct HTTPS POST from the client to PAR with an application/x-www-form-urlencoded body. It is the place to authenticate the client and validate client_id, response type, redirect URI, scope, resource indicators, state, nonce, and PKCE. RFC 9126 requires HTTPS for PAR and permits authorization-endpoint extensions.

The second hop is the user agent’s request to the authorization endpoint, normally carrying only client_id and request_uri. The authorization server retrieves the request saved in the first hop instead of trusting sensitive parameters re-submitted by the browser. This reduces query-string leakage, URL-size limits, and user-agent tampering.

2. Design request_uri storage

Generate request_uri with a cryptographically secure random value, never an incrementing ID or predictable business key. Store the complete request, client ID, creation time, expiry, consumption state, and hashed audit fields; bind the reference to the client that created it.

Keep the TTL short and return it as expires_in. An expired reference returns invalid_request; an unknown reference should not reveal whether it ever existed. One-time consumption is safest. If the product must support refresh, allow a short window with a read count, state/nonce binding, and a hard cap.

3. Validate the client and request

Authenticate the client at PAR using the token-endpoint rules, such as mTLS or private_key_jwt. Authentication proves which client submitted the request; it does not prove that a user signed in or consented to a scope. Validate registered redirect URIs, allowed scopes, resources, and response type at PAR, then repeat checks that require user context at authorization time.

If the client uses a signed Request Object, validate its signature, issuer, audience, expiry, and critical fields under JAR rules. PAR is the direct push and reference lifecycle; JAR is Request Object signing or encryption. They can compose, but neither replaces the other.

4. Put PKCE, state, and nonce in the right layer

PAR still carries code_challenge and its method; the token endpoint must verify code_verifier when redeeming the code. state binds the client session and mitigates CSRF. OIDC nonce binds the authentication request to the ID token. The presence of request_uri is not a reason to remove these controls or treat them as interchangeable.

5. Prevent swapping, replay, and open redirects

If an attacker replaces a request with one they obtained, scope or assurance level could change. Bind the reference to the client and require the authorization request’s client context to match. The client uses a unique state and PKCE; OIDC uses nonce. Match redirect URIs strictly so PAR cannot become an open-redirect entry point.

Return an explicit but non-enumerating error when a consumed reference is reused. Log client, reference hash, time, risk signals, and outcome to detect guessing and replay. Keep complete authorization requests, client assertions, and sensitive resource parameters out of ordinary access logs.

6. Design errors, limits, and availability

Invalid parameters, a mismatched redirect URI, or a bad signature use OAuth errors such as invalid_request. A wrong method can be 405, an oversized request 413, and a quota breach 429. Apply limits by client, tenant, IP, and global storage so an attacker cannot exhaust PAR storage.

When PAR is unavailable, whether migrated clients may fall back to a regular authorization request is a security policy decision. For payment or high-risk scopes, fail explicitly instead of silently weakening the flow. Migrate legacy clients through metadata, then gradually enforce require_pushed_authorization_requests.

7. Operate data lifetime and observability

Retain reference data only for its TTL, consumption, and necessary audit period. Use encrypted storage, access control, and minimization; do not copy sensitive authorization context into many caches. Track PAR success, validation failures, expiry, duplicate consumption, 429 rate, missing-reference rate at authorization, and PKCE failures at token exchange.

Canary regular and PAR flows side by side. Compare completion rate, first-page latency, mobile fallback, and security alerts. Automated tests should cover concurrent consumption, reference swapping, redirect-URI variants, browser refresh, oversized requests, and legacy-client compatibility.

High-quality sample answer

I would split the flow into two hops. The client calls PAR over HTTPS, authenticates, and validates redirect URI, scope, resource, response type, PKCE, state, and nonce. The server creates a high-entropy, short-lived, client-bound request_uri and returns only the reference and expires_in. The browser then calls the authorization endpoint with client ID and the reference; the server retrieves the original request and performs authorization, user-session, state/nonce, and PKCE checks again.

The reference stores the request, client binding, expiry, and consumption state, with one-time use by default. A refresh feature uses a short window, read cap, and audit. JAR handles Request Object signing or encryption; PAR handles direct push and reference lifetime. Strict redirect-URI matching prevents swapping and open redirects, while OAuth errors and per-client, IP, and tenant limits make abuse visible.

I would canary by client metadata while retaining the old flow, measuring completion, latency, expiry, duplicate use, 429s, and PKCE failures. High-risk clients do not silently fall back. A storage outage or replay spike stops rollout and reverses the enforcement flag. This reduces URL leakage and parameter tampering without changing OAuth’s user-consent and code-exchange boundaries.

Common mistakes

  • Saying only “put the parameters in POST” without client authentication, reference binding, or expiry.
  • Treating request_uri as an access token or making it a guessable database ID.
  • Assuming PAR automatically prevents replay and omitting one-time use, TTL, state, nonce, or PKCE.
  • Mixing PAR, JAR, and PKCE without explaining each protection boundary.
  • Validating redirect URI only at PAR and trusting browser parameters at authorization time.
  • Ignoring 413, 429, concurrent consumption, cache leakage, and sensitive logs.
  • Falling back unconditionally to regular authorization when PAR fails, widening high-risk scope exposure.

Follow-up questions and responses

How long should a request_uri live?

Use a short TTL based on risk and return expires_in. Delete or invalidate it after expiry or consumption; retain only minimal hash, client, and outcome fields for audit.

Can a browser refresh reuse the reference?

Default to one-time use. If refresh is required, use a short window, a read cap, and state, nonce, and client-session binding while monitoring duplicate reads. Do not allow unlimited reuse.

Why is PKCE still needed after client authentication?

Client authentication identifies the client that submitted the request. PKCE binds code redemption to the requester who holds the verifier. It remains important for public and mobile clients if an authorization code is intercepted.

Can PAR replace JAR?

No. PAR addresses direct push, browser-parameter hiding, and reference lifetime. JAR addresses Request Object signing or encryption. Combine them when stronger integrity or non-repudiation is required.

How should legacy clients migrate?

Enable PAR by authorization-server metadata and client policy, observe support, then enforce require_pushed_authorization_requests for selected clients. Keep a bounded, audited legacy path instead of switching the whole ecosystem blindly.

What if the PAR endpoint receives a flood?

Limit by client, tenant, IP, and global resources; cap body size and storage TTL; return 429; and monitor failure rate and storage watermarks. High-risk clients must not silently downgrade because of traffic pressure.

Public sources

Related questions