Skip to content
Authreads

Protocols and how-tos

OAuth and OpenID Connect for single-page apps: where tokens should live

Why the implicit flow is out, why a backend-for-frontend is the safest pattern for SPAs, and the trade-offs of keeping tokens in the browser.

The short answer

A single-page app should use the authorization code flow with PKCE, never the implicit flow. The most robust pattern is a backend-for-frontend (BFF): a small server component performs the OAuth exchange, keeps the tokens server-side, and gives the browser only an HttpOnly, Secure, SameSite session cookie. Keeping tokens in browser JavaScript is possible, but any cross-site scripting flaw then exposes them.

Sources last reviewed .

Why implicit is gone

The implicit grant returned access tokens directly in the URL fragment. Tokens in URLs end up in browser history and can leak through referrers and extensions, and there is no way to sender-constrain them. RFC 9700 says the implicit grant should not be used. The IETF draft on browser-based apps reaches the same conclusion.

The three architectures

The IETF browser-based apps draft describes these patterns, in decreasing order of the protection they offer:

  • Backend-for-frontend: the server is a confidential OAuth client, tokens never reach the browser, and the browser holds only a session cookie. XSS can still make requests while the user is on the page, but cannot carry tokens away.
  • Token-mediating backend: the server performs the exchange but hands the access token to the browser, keeping the refresh token. Shorter exposure, but the access token is still readable by script.
  • Browser-based OAuth client: the SPA does everything itself with PKCE. Simplest to deploy, and every token is reachable by injected script. If you choose this, use short-lived access tokens, rotate refresh tokens, and consider DPoP with a non-extractable key.

What 'store it in memory' actually buys

Keeping tokens in a JavaScript variable rather than localStorage removes persistence across tabs and reloads, which removes some exfiltration paths. It does not stop script running in the same page from reading the variable or calling your API with it. It is a mitigation, not a boundary.

Cookie settings for the BFF session

Use HttpOnly so script cannot read it, Secure so it never travels over plain HTTP, and SameSite=Lax or Strict to limit cross-site sending. A __Host- prefixed cookie name (RFC 6265bis) additionally pins it to your exact host with Path=/. Add CSRF protection for state-changing requests; SameSite is a defence in depth, not a complete one (see the OWASP CSRF cheat sheet).

In Authreads

How this applies to Authreads

Authreads supports confidential BFF integrations and public OpenID Connect clients with mandatory PKCE. The developer portal describes both paths.

Authreads is a multi-tenant identity platform in beta. Capability status is maintained on /roadmap as Available, In progress or Planned.

Building on any of this?

Tell us what you're putting together. We'll tell you plainly whether Authreads fits it.