Single page applications (SPAs) have changed how web apps are delivered by separating the user interface from backend services. They offer fast, seamless user experiences by dynamically updating content without full-page reloads and enable a clear separation between frontend and backend, improving scalability and development speed. SPAs also support rich interactivity, efficient state management, and easy deployment via CDNs. But this architecture also introduces new security challenges, especially around token handling in the browser.
The Problem with Browser-Based Tokens
Traditional web applications used server-side sessions with HTTP-only cookies, which provided a strong defense against common attacks like Cross-Site Scripting (XSS). In contrast, SPAs store tokens in JavaScript-accessible storage, leaving them exposed to XSS attacks, which could allow malicious scripts to steal tokens and impersonate users, even after the session has ended.
When tokens are stored in the browser, they create a security vulnerability, as attackers can exfiltrate them to maintain access long after a user leaves the app. Moreover, the ongoing deprecation of third-party cookies is disrupting some token flows that SPAs have traditionally relied on, like a silent refresh.
Rethinking Token Security in SPAs
Given the lack of secure storage in browsers, the best way to protect tokens is to avoid placing them in the browser at all. This is where the Token Handler Pattern comes into play. Inspired by the current best security practice — the Backend for Frontend (BFF) approach, this architecture delegates token management to a secure backend service.
In this pattern, the SPA communicates with an OAuth Agent, which is a backend component that performs authorization flows and manages tokens securely. The agent exchanges credentials with the authorization server and issues session cookies to the SPA. These cookies, being HTTP-only and bound to the SPA’s domain, give better protection against XSS.
A key advantage of this setup is that the SPA still behaves like a modern, front-end-driven application. It doesn’t need to proxy every business request through a traditional backend or give up its CDN-based deployment model. Instead, it simply delegates authentication to the OAuth Agent, which can be deployed separately and scaled independently.
Implementation Approaches: Stateful vs. Stateless
Token handler components can be implemented in stateful or stateless modes. A stateful version stores tokens in a secure backend database, issuing cookies with session identifiers that reference stored tokens. This design is straightforward and supports session tracking but might synchronize if the backend components are deployed to multiple locations.
Stateless implementations, on the other hand, store tokens directly in the session cookies. These cookies are unreadable by JavaScript and their values could even be encrypted to remain secure when intercepted. This approach eliminates the need for backend storage but results in larger cookie sizes, particularly when using JWTs. Developers can influence cookie sizes by using opaque tokens with the phantom token approach or by ensuring smaller JWT signatures with cryptographic algorithms like ES256 or EdDSA.
Deployment Flexibility
The token handler pattern is lightweight by design and can be deployed as a backend service, serverless function, or even directly on CDNs that support edge computing. Regardless of deployment strategy, the key requirement is that the token handler must operate on the same or a sibling domain as the SPA to ensure cookies are treated as first-party.
Improving User Experience and Reducing Complexity
One of the main benefits of the token handler approach is that it enhances security without compromising user experience. SPAs retain control over navigation, redirects, and state restoration. They can initiate OAuth flows when needed, and the backend can handle complex security requirements such as Pushed Authorization Requests or JWT-based client authentication transparently. This separation of concerns improves developer productivity by reducing cognitive load, improving security, and allowing frontend teams to focus on building features while backend teams can manage authentication logic centrally and consistently.
Scaling Security Across SPAs
For organizations managing multiple single-page applications, it’s critical to design deployments that minimize the potential for security breaches. Assigning separate domains or subdomains per product area (e.g., product1.example
, product2.example
) helps isolate session cookies. Each SPA can have its own OAuth scopes and security configuration, limiting the impact of vulnerabilities.
In cases where multiple micro-frontends share a domain, the same token handler can support all apps by sharing session cookies. Developers can also mix static, unauthenticated pages with secured SPAs in a single domain by carefully segmenting the app paths and authentication scope.
Ready-Made Solution from Curity
SPAs need a secure mechanism to interact with APIs, one that doesn’t compromise user data through browser vulnerabilities. The Token Handler Pattern is the recommended modern approach: it reintroduces secure, cookie-based sessions in a way that complements OAuth standards and SPA architectures. Curity provides a robust, tested open source solution that is production-ready and fully aligned with OAuth and OpenID Connect standards. For those interested in learning more, Curity offers example projects, comprehensive documentation, and a white paper that outlines SPA security strategies in depth.
Top comments (0)