Skip to content

The user may not notice the difference.

They click Sign in. They land on the same product screen. They load profile data and call an API. From the front of the house, a direct-token SPA and a Backend-for-Frontend can look almost identical.

The security boundary is not identical.

In a direct SPA, the browser application becomes the OAuth token holder. In a BFF architecture, the browser has an application session and the backend owns the OAuth tokens. That difference matters most when something goes wrong inside the browser origin: a cross-site scripting bug, a compromised dependency, an unsafe tag, or any other path that lets unwanted JavaScript run where the application runs.

The ProAuth v3 preview includes BFF support for this exact boundary change. The goal is not to pretend browser risk disappears. It is to reduce token exposure by keeping access and refresh tokens out of browser JavaScript.

The direct SPA demo makes the problem visible

The direct SPA in the conference demo is intentionally built as an anti-pattern. It still uses modern protocol pieces, including authorization code with PKCE. That is important because the lesson is not “PKCE is bad”. RFC 7636 remains the right baseline for public clients that cannot hold a secret.

The problem starts after the authorization code has been redeemed.

In the demo, browser JavaScript completes the token request and stores the token response in localStorage. The page can then show the browser-visible access token, ID token, and refresh token. A second button simulates injected JavaScript. That script does not need a special browser privilege. It runs in the same origin and reads the same storage the application can read.

That is the boundary in plain terms: if the SPA can read the token, injected JavaScript in the same origin may be able to read it too.

For low-risk applications, a team may accept that tradeoff with short-lived access tokens, no browser refresh tokens, narrow scopes, strict redirect handling, and careful frontend hardening. But for high-value business applications, customer administration, security settings, billing, or sensitive personal data, browser-owned tokens deserve a much harder review.

The BFF demo changes who owns the token

The BFF demo keeps the browser experience useful without giving OAuth tokens to JavaScript.

After login, the browser has an HTTP-only application session cookie. The BFF stores token material server-side. The frontend can load filtered user information and call the orders API, but it calls the BFF route. The BFF retrieves the access token from its server-side token store and attaches it when forwarding the request to the API.

That is the essential ProAuth BFF model:

  • access and refresh tokens stay server-side
  • the browser authenticates to the BFF with a secure HTTP-only session cookie
  • API calls go through configured proxy routes
  • the BFF injects the access token on the server side
  • refresh handling is centralized in the backend component

In the demo environment, the BFF uses Redis-backed distributed locking, token storage, ticket storage, and data protection key persistence. That is not incidental plumbing. A real BFF is an operated backend service, not a JavaScript library with a different name.

Same screen, different flow

flowchart LR
    subgraph Direct["Direct-token SPA"]
        D1["Browser SPA starts login"]
        D2["Authorization code + PKCE"]
        D3["Browser JavaScript calls token endpoint"]
        D4["Access / ID / refresh token reachable by JavaScript"]
        D5["Browser calls API with Authorization header"]
        D6["Injected script can read or use browser-held token"]

        D1 --> D2 --> D3 --> D4 --> D5
        D6 -. "same origin" .-> D4
        D6 -. "can trigger API calls" .-> D5
    end

    subgraph Bff["Browser BFF"]
        B1["Browser starts login at BFF"]
        B2["BFF uses OIDC code flow as backend client"]
        B3["Tokens stored server-side"]
        B4["Browser receives HTTP-only session cookie"]
        B5["Browser calls same-origin BFF route"]
        B6["BFF forwards to API with server-side token injection"]

        B1 --> B2 --> B3 --> B4 --> B5 --> B6
    end

    classDef exposed fill:#fff4e5,stroke:#b45309,color:#3f2a00
    classDef contained fill:#eef8f2,stroke:#15803d,color:#12351f
    class D1,D2,D3,D4,D5,D6 exposed
    class B1,B2,B3,B4,B5,B6 contained

This is the same lesson the IETF browser-based apps draft makes at the architecture level: a full BFF should be considered when the application can route protected-resource calls through a backend. A token-mediating backend can improve part of the design by moving the code exchange and refresh-token handling server-side, but if it still returns access tokens to the browser, the access-token boundary remains in JavaScript.

That distinction is easy to lose in architecture diagrams. The question is not whether there is “some backend”. The question is whether the browser receives OAuth access tokens.

What ProAuth BFF adds around the boundary

The ProAuth BFF documentation describes the package as an intermediary server between the SPA and backend APIs. Its job is to host the browser session, store tokens server-side, and proxy API traffic with token injection.

The preview configuration model reflects the operational pieces a platform team needs to decide deliberately:

AreaWhat to decide
Session lifetimeHow long the BFF browser session can remain idle
Token storeIn-memory for development or single-instance use; Redis for production multi-instance deployments; Dapr for Dapr state management; ReaFx for ReaFx-based applications
Ticket storeWhere authentication tickets live, especially when cookies should stay small
Proxy routesWhich paths the BFF forwards, which require authentication, and whether a token is sent downstream
Token forwardingBearer forwarding for ordinary APIs, with DPoP or mTLS-oriented forwarding options where the API design requires sender constraints
Forwarded headersWhich reverse proxies are trusted to provide host and scheme information
LogoutWhether front-channel and back-channel logout endpoints are enabled for the client
CSRFHow state-changing browser requests include the anti-forgery token expected by the BFF

For a single local demo, in-memory state may be enough. For production-shaped multi-instance deployments, the docs call out shared storage options. The conference BFF demo uses Redis so token state, ticket state, locks, and related data protection material can survive beyond a single process instance.

What the BFF does not solve

A BFF does not make cross-site scripting harmless.

If unwanted JavaScript runs in the browser origin while the user has an active session, it may still read page data, click buttons, submit forms, or make same-origin requests through the BFF. The session cookie is not readable by JavaScript when it is HTTP-only, but the browser can still attach it to legitimate same-origin requests.

That is why the claim should stay precise: a BFF reduces token exposure. It does not remove the need for Content Security Policy, output encoding, dependency governance, frontend review, CSRF protection, safe cookie configuration, monitoring, or application authorization.

The gain is narrower and still significant. The attacker does not get a reusable OAuth access token merely because the frontend needed to call an API.

A practical review question

When reviewing an existing SPA, start with one question:

Does browser JavaScript ever receive an OAuth access token or refresh token?

If the answer is yes, review the decision as an explicit risk, not as a default SDK behavior. Check whether the application really needs direct API calls from the browser, whether refresh tokens are present, how long access tokens live, what scopes and resources they carry, and what would happen if JavaScript in that origin became hostile.

If the answer can become no, a BFF gives the team a cleaner boundary. The browser keeps being a browser. The backend becomes the OAuth client. Tokens become server-side credentials managed by infrastructure the platform team can operate and harden.

For implementation details, start with the ProAuth BFF documentation. For the standards background, read the OAuth 2.0 Security Best Current Practice, PKCE in RFC 7636, Bearer Token Usage in RFC 6750, and the IETF work-in-progress draft for OAuth 2.0 for Browser-Based Applications.