Token Handler Overview
On this page
- Recent browser changes to prevent tracking may result in dropped
third partySSO Cookies during traditional SPA flows, leading to blocking usability issues
The Token Handler Pattern article provides background on threats in various web architectures, resulting in a current SPA Best Practice of using a
Backend for Frontend approach. The SPA then implements OpenID Connect security via a backend component that manages token storage and issuing of secure cookies for the browser.
Token Handler Pattern
The token handler pattern therefore issues only the most secure
HTTP-Only, SameSite=strict cookies on behalf of the SPA. These are first-party cookies and not subject to browser restrictions:
There are key differences between the token handler pattern and alternative approaches. Firstly, an API driven OpenID Connect flow is used. It is initiated by Ajax requests from the SPA, and secure cookies are then issued by utility API components. This maintains a pure SPA architecture, where developers only work with a frontend.
Secondly, all API requests are routed via an API gateway rather than a web backed. In cloud native setups this will be a high performance gateway such as NGINX. Doing so simplifies code, and ensures that authorization responsibilities are always implemented on the API side of the architecture.
The main benefits of using the token handler pattern are summarized in the below table:
|Best Browser Security||Follows current best security practices for browser based apps, with no tokens in the browser and only |
|Great User Experience||All UI actions, including OpenID Connect redirects, are initiated in the browser so that the app has best control over usability|
|Deploy Anywhere||The SPA is built to static content that does not need securing and could be deployed to a Content Delivery Network (CDN)|
For any web application that uses secure cookies, the domains used need to be designed with care, and this is also true when using the token handler pattern. For a web origin of
https://www.example.com, the token handler components must either run on the same domain, or a related domain such as https://api.example.com.
Token Handler HostingFor secure cookies to work reliably for the SPA they must be first-party, and the token handler components must be hosted on the same parent domain as the SPA's web origin
When the SPA needs to sign the user or perform other OAuth work it makes Ajax calls to the OAuth Agent. This is a utility API which makes OAuth requests to the Identity Server and attaches a client secret when needed. After user authentication, the OAuth Agent writes secure cookies that are returned to the browser.
In total the following non-persistent cookies are issued, if a cookie prefix of
example is used, each of which is strongly protected using AES256 symmetric encryption, with the encryption key known only by token handler components:
|example-login||A temporary cookie used between start / end login, to verify that the state before and after login matches, and to store the PKCE code verifier|
|example-auth||The main SPA cookie contains the refresh token representing the user's authenticated session|
|example-id||This cookie contains the ID token, the contents of which the token handler API provides to the SPA via its /claims endpoint.|
|example-at||This cookie contains the current access token, which is used later when calling APIs|
|example-csrf||This cookie can be used for additional protection, as described in OWASP CSRF defense in depth|
Secure Cookie Properties
All cookies have the following properties, though the paths of the refresh and ID cookies are restricted to the OAuth Agent endpoints that use them. Cookies only need to be issued to the API domain, and are not used during requests for web static content, or during user navigation:
|Secure||The cookie requires an SSL connection|
|SameSite=strict||The cookie can only be sent from the web origin, and not from malicious domains|
|Domain=api.example.com||The cookie is only needed when calling APIs|
|Path=/||The access token cookie is available to all APIs within the API domain|
If following Curity token recommendations, you will be using the Phantom Token Pattern, where access and refresh tokens issued are small opaque values, similar to UUIDs. This means cookies issued do not exceed browser or HTTP server limits. Cookie issuing is then stateless from a backend viewpoint, without the need to implement any token storage.
Login User Experience
The OAuth Agent never abruptly redirects the SPA, and instead returns OpenID Connect URLs to the SPA, so that the location and page state can be saved before a redirect and then restored after. In addition there are no dropped cookie problems during navigation, since cookies are not needed during web navigation requests. All of this leads to the type of behavior you would expect from an SPA, where the front end code is in control of the UX.
Once authentication has completed, the SPA will make API calls to access data. These requests are usually cross-origin, since web static content hosting is usually done differently to API hosting. The end result for your microservices should be to receive a JWT access token, and the preferred end-to-end API flow is illustrated below:
In order for end-to-end SPA to API requests to be made securely and reliably, it is then necessary to deal with these cookie based concerns:
- Entry points must be protected against OWASP Cross Site Request Forgery (CSRF) risks
- Cookies must be decrypted, to extract the opaque access token
- If token handler components are deployed to an API domain, the browser must be authorized to use Cross Origin Resource Sharing (CORS)
This work just involves general security plumbing, which is best kept out of your API code. Instead, since APIs are already hosted behind a reverse proxy / API gateway as a security best practice, the gateway is the simplest place to deal with them.
The OAuth Proxy is a small component that implements these tasks, and is deployed as a lightweight plugin component, so that the code involved performs well under load.
At Curity, we are providing tested implementations of the OAuth Agent and OAuth Proxy, which will work with any Authorization Server that supports the security standards used. This enables companies to simply configure or deploy a working solution, without needing to build their own solutions from scratch. For a list of all token handler resources, including guidance on integration and deployment, see the OAuth for Web Home Page.
The token handler pattern enables frontend developers to run code for just the Web UI, and to work solely in a browser based technology, such as
Angular. This provides maximum productivity, with static content built locally and no need to run a web backend. Moving to the token handler pattern does require developers to manage additional components though. See the Web Development Setup article for some recommendations on managing this.
Remaining Browser Threats
Due to the use of the token handler pattern, the SPA's OpenID Connect and API calls follow current best practices for browser based apps, but of course this does not make your app fully secure. Software companies must also follow a security development lifecycle, and in particular protect against Cross Site Scripting (XSS). If there is an XSS vulnerability then secure cookies could be abused by an attacker to steal data.
Using cookies optimally in an SPA is challenging, but needs to be done in order for SPAs to be secure. The best current way to manage this is to use the token handler pattern, which maintains all of the other benefits of an SPA architecture. To get started and run an SPA that uses the token handler pattern, see the SPA Code Example.