Back to Blog

Browser-based OAuth in 2026: choose the architecture before exposing tokens to a SPA

September 20, 2026 9 min read
Browser-based OAuth in 2026: choose the architecture before exposing tokens to a SPA
Last updated:

A backend-for-frontend (BFF) is justified when keeping OAuth tokens out of browser JavaScript provides enough protection to warrant operating a backend and proxying API traffic. The decision depends on the consequences of token theft, direct API requirements, proxy feasibility, and operating cost. Authorization code with PKCE is a separate protocol choice: a BFF uses it too.

Published in August 2026, RFC 10017 establishes IETF Best Current Practice for browser-based OAuth applications. It compares three architectures with different token-exposure boundaries. Start with those boundaries, then verify the protocol and session controls.

Security review decision flow

Choose where tokens can be used

Architecture Values available to application JavaScript Browser-managed session Main remaining concern
BFF No OAuth tokens An HttpOnly cookie that JavaScript cannot read Injected code can still make authenticated requests through the BFF
Token-mediating backend Access tokens; refresh tokens remain under backend control An HttpOnly backend-session cookie Injected code can steal access tokens or request them from the backend
Browser-based OAuth client Access tokens and, if issued, refresh tokens; direct readability depends on storage isolation No backend-session cookie is inherent to this pattern Malicious code can abuse the client and obtain tokens

These distinctions follow RFC 10017 §6. An HttpOnly cookie is stored and sent by the browser, but its value is unavailable to JavaScript.

A BFF acts as a confidential OAuth client, manages tokens through a cookie-based session, and attaches access tokens when forwarding requests to resource servers. This article selects server-side session and token storage for its BFF example. That is an implementation choice, not the definition of a BFF: the RFC also permits client-side cookie sessions and recommends encrypting cookie contents when they contain access tokens. RFC 10017 §6.1.2.3, §6.1.3.2

RFC 10017 strongly recommends a BFF for business applications, sensitive applications, and applications handling personal data. Assess that recommendation against four practical questions:

  • What data and actions would a stolen token expose, and for how long?
  • Must the browser call a resource server directly?
  • Can the backend safely proxy the required traffic?
  • Can the team operate the proxy and session infrastructure reliably?

Proxy feasibility alone does not justify the architecture. It establishes that a BFF is possible; the exposure reduction and operating requirements determine whether it is appropriate. Consider a token-mediating backend only when use cases or system requirements prevent a full BFF. RFC 10017 §6.1.4.3, §6.2.4.4

For example, consider a hypothetical staff portal that edits customer records through a small set of APIs. If the team already operates a backend and has no direct-call requirement, a BFF is a defensible choice. If a required browser integration must call a resource server directly, a token-mediating backend may be the workable compromise, with access-token exposure explicitly accepted. A browser-only client requires its own documented risk decision; avoiding backend operations does not reduce the consequences of malicious browser code.

Verify redirects and callback protection

Use authorization code with PKCE as the baseline for the designs discussed here. Browser-based public clients must use PKCE, and their authorization server must enforce it. RFC 9700 also recommends PKCE for confidential clients. Use S256 to avoid exposing the verifier in the authorization request. RFC 10017 §6.3.2.1, RFC 9700 §2.1.1

Check Evidence and responsible component
Exact redirect matching The authorization server rejects unregistered mutations of the requested redirect_uri, including changed hosts, paths, and query strings. Test against the registered URI set. RFC 9700 §2.1
Callback CSRF protection The client demonstrates its chosen protection: PKCE with verified server support and transaction binding; correctly validated OIDC nonce; or a one-time state token securely bound to the initiating browser. RFC 9700 §2.1
Issuer binding A client supporting multiple authorization servers binds the expected issuer to the transaction and checks the response using issuer identification or the specified distinct-redirect-URI defense. RFC 9700 §4.4.2
Callback handling The application processes the authorization response, removes sensitive response data from the visible URL promptly, and prevents disclosure through page scripts, referrers, or telemetry. RFC 9700 §4.2, §4.3

Exact matching applies to the redirect_uri submitted in the authorization request. It does not prohibit the authorization server from adding legitimate response parameters such as code and state to the callback. The localhost-port exception for native applications is not permission to use wildcard callbacks in a SPA. Clients and authorization servers must also avoid open redirectors. RFC 9700 §2.1

state is not universally mandatory when another permitted CSRF defense is correctly implemented. An organization may require it in addition to PKCE, but that is a stricter organizational policy. If state carries application data, protect any integrity-sensitive contents against tampering and swapping. RFC 9700 §4.7.1

Test four separate PKCE properties

A library name is not test evidence. In staging, distinguish the client's generation behavior from the authorization server's validation behavior.

Property Evidence to retain
Client randomness and freshness Review the client's cryptographic random generation and verify that separate authorization attempts create fresh verifiers. Each verifier must meet the RFC's syntax and length requirements. Bind transaction material to the initiating browser. RFC 7636 §4.1, RFC 9700 §2.1.1
Server challenge matching For a code issued with an S256 challenge, show that the token endpoint rejects a missing or mismatched verifier and accepts the correct verifier for a valid, unused code. RFC 7636 §4.4, §4.6
Authorization-code replay rejection After a successful exchange, show that the authorization server rejects another exchange of the same code, including with the correct verifier. RFC 9700 §4.5
Downgrade rejection Verify server enforcement for the configured client and the absence of client fallback. A token request containing a verifier must not succeed for a code issued without a challenge; requiring PKCE at authorization prevents that code from being issued. RFC 9700 §4.8.2

A client that reuses a verifier across new authorization requests fails the freshness requirement. The server does not necessarily reject a newly issued code merely because its matching verifier appeared in an earlier transaction. That is different from reusing an authorization code.

For these implementation examples, require S256 and reject fallback to plain as an explicit release policy. RFC 7636 requires clients capable of S256 to use it; RFC 9700 recommends challenge methods that do not reveal the verifier. Do not describe a server's general support for plain as proof of a downgrade in the tested transaction. RFC 7636 §4.2, RFC 9700 §2.1.1

Verify refresh protection and expiry together

For refresh tokens issued to browser-based public clients, rotation or sender constraint is only part of the requirement. The authorization server must also impose a maximum lifetime or expiration after inactivity. If the original token has a preestablished expiration time, rotation must not move the replacement token's expiry beyond that deadline. RFC 10017 §6.3.2.3

Check Authorization-server evidence
Rotation, if selected Every refresh replaces and invalidates the previous token. Reuse of an invalidated token is detected and causes revocation of the active refresh token associated with that grant. RFC 9700 §4.14.2
Sender constraint, if selected A refresh attempt without the required proof, or using the wrong key, fails. RFC 9700 §4.14.2
Expiration Refresh fails after the configured maximum lifetime or inactivity period, according to the chosen policy. RFC 10017 §6.3.2.3
Rotation deadline Where an original expiry is fixed, successive replacements cannot refresh beyond it. RFC 10017 §6.3.2.3

Deleting a stored token is local cleanup. It does not invalidate a stolen copy or replace server-side rotation, sender constraint, expiration, or revocation. For a BFF or token-mediating backend acting as a confidential client, verify client authentication and refresh-token binding to that client; do not automatically apply every public-client rule to it. RFC 9700 §4.14

Assess storage and XSS by architecture

Browser storage is not subject to a universal ban. Persistent storage, such as localStorage or IndexedDB, and in-memory handling have different lifetimes and exposure properties. Isolation in a Web Worker can limit direct access to existing tokens, but storage isolation alone cannot prevent malicious code from obtaining new tokens through the application's available flows. RFC 10017 §8

Apply architecture-specific acceptance criteria:

  • The server-storage BFF selected here: no OAuth tokens in browser responses, JavaScript memory, or browser storage. The browser retains only the application session cookie.
  • Token-mediating backend: access tokens in the browser are intentional; refresh tokens must remain under backend control and unavailable to browser JavaScript.
  • Browser-only client: document which tokens exist in memory or persistent storage, who can access them, how long they remain, and what happens on reload and logout. A ban on persistent token storage is an optional organizational policy, not a universal RFC requirement.

In every architecture, treat unintended disclosure through URLs, logs, analytics, error reports, or referrers as a defect. An authorization code legitimately arriving at a callback is different from an access token being placed in a URL. URL cleanup also cannot erase data that has already reached a log or analytics service.

For the BFF, require Secure and HttpOnly cookies. Follow the RFC's recommendations for SameSite=Strict, Path=/, no Domain attribute, and an HTTP-set cookie prefix such as __Host-Http-; document justified deviations from recommendations. Path=/ covers the whole host rather than a narrow application path. RFC 10017 §6.1.3.2

Implement and test an appropriate CSRF defense for cookie-authenticated endpoints. Account for other applications on the same site when evaluating SameSite protection. RFC 10017 §6.1.3.3

Restrict proxy destinations and permitted paths so client input cannot send a user's token to an unapproved host or route. Validate destination hosts against an explicit allowlist of approved resource servers; a dynamically configurable proxy must allow only explicitly permitted hosts and paths. RFC 10017 §6.1.3.6

A BFF reduces token extraction, but injected code can still act through the active session. Keep XSS prevention, script controls, and least privilege in the design. RFC 10017 §5

Release decisions and retained evidence

Use the checks above as a proposed release policy. Block release for failed redirect matching, ineffective callback protection, failed PKCE checks, missing applicable refresh protections, unapproved proxy destinations, unintended credential disclosure, or token exposure that contradicts the selected architecture. Intentional browser token handling is not automatically a release blocker.

Keep a data-flow inventory and sanitized test results in your own architecture records, release record, or issue tracker. Record token locations, cookie settings, redirect URIs, issuers, resource servers, scopes, expiry policies, and logout and revocation behavior. Include explicit token exposure, residual XSS impact, remediation owners, and retest results. Retain evidence without retaining reusable credentials.

For further reading on explaining business impact and remediation priorities, see Reporting and risk. That page explains an assessment approach; your own records hold the release evidence.

These checks validate defined boundaries and observed behavior. They do not establish that the application, identity provider, browser, or dependencies are free of vulnerabilities.

FAQ

Does PKCE make a browser-only SPA equivalent to a BFF? No. PKCE protects the authorization-code exchange. The architecture determines whether the application receives tokens and which actions malicious browser code can perform.

Does an HttpOnly cookie reach JavaScript? Its value does not. The browser can still attach it to requests initiated by JavaScript, including injected code.

Must every browser client avoid persistent storage? No universal prohibition applies. Document the selected storage tradeoffs, enforce applicable refresh protections, and identify any stricter organizational policy.

Further reading

Vid Grosek

Vid Grosek

Ethical Hacker & Penetration Tester

I help Slovenian companies discover security vulnerabilities before attackers do. With 18+ years of experience in cybersecurity.

All Posts

Comments

No comments yet. Be the first!

Leave a Comment

Enjoyed this article?

Subscribe to the newsletter for monthly security insights.

Subscribe