Token Handler Deployment Patterns
On this page
Using the Token Handler Pattern provides an optimal Single Page Application (SPA) architecture, as described in the Overview article. The separation of concerns leads to the simplest application code and the design can then be scaled across multiple SPAs and APIs. It does however add deployment complexity, so companies will need to design a solution based on their platform.
The token handler pattern uses two new components that need to be deployed to serve the SPA, called an
OAuth Agent and an
OAuth Proxy. A logical representation is shown below, and in this article we will describe some different ways in which the setup can be made more concrete.
OAuth Agent is used to perform OpenID Connect related work on behalf of the SPA. It is only called occasionally, when the SPA needs to perform operations such as authenticating the user or asking the backend for user info. It is best implemented as an API, since its behavior may need to be customised as new OpenID Connect options become available.
Meanwhile the OAuth Proxy executes on every request from the SPA to API, so needs to perform well and avoid additional HTTP requests. It is best implemented in a reverse proxy that can efficiently deal with cookie based concerns, then routing requests with JWT access tokens to APIs. Once deployed and reliable, this component should not need to change.
The critical requirement when using the token handler pattern is to ensure that the browser always receives first-party cookies. This is done by ensuring that the SPA's web origin and the API endpoints for token handler components share the same parent domain. Calls from the SPA to the OAuth Agent, and to APIs via the OAuth Proxy, will always be in the same site.
Hosting for token handler components is less clear-cut, since there are many possible API architectures. The token handler pattern can potentially work with any of these, so we will look at some popular options in this section.
Cloud native platforms such as Kubernetes typically provide a back end with the most advanced capabilities, but require more work related to deployment and availability. Application components are built into Docker containers, to run in a cluster, e.g, using Kubernetes, and can then be deployed anywhere.
Companies using cloud native are also likely to use existing cloud native reverse proxies or gateways hosted in front of APIs. This means the work to deploy token handler components is fairly straightforward and involves only these steps:
- Build the OAuth Agent into a container and configure it to point to your Identity Server
- Deploy an OAuth Proxy plugin with your reverse proxy and configure your API routes to use it
In this architecture, the SPA will make occasional lightweight CORS pre-flight requests when calling APIs. The token handler will then call the Identity Server with high performance
inside the cluster. This also means you may be able to avoid exposing certain OAuth endpoints to the internet, to reduce the security attack surface.
A variation of the above deployment is to also host the web static content behind the reverse proxy and use paths such as those below. This is a common form of the Backend for Frontend pattern, though it requires servers or containers to host your web static content. It can be a good choice if you expect to implement more complex code in the web host, or if you have concerns about the overhead of CORS pre-flight requests in the multi-domain setup.
|Web Static Content||https://www.example.com|
Some companies prefer Serverless technology for APIs, due to the reduced infratructure management. Since the OAuth Agent is just a utility stateless API, with no need for back end storage, a lambda implementation is a good fit. Meanwhile the OAuth Proxy can run as a plugin within the Cloud API Gateway. Deployment steps are now slightly different:
- Configure and deploy a lambda based OAuth Agent API that points to your Identity Server
- Configure and deploy a lambda based OAuth Proxy to run within the cloud API Gateway
The OAuth Agent lambdas will then need to call the Identity Server, which could be managed in various ways, e.g., via internet endpoints, or via a cloud private network. Not all cloud API gateways have mature support for plugins, but if you ever run into blocking issues you can usually switch to a more specialist option via a cloud managed service, e.g,
Some cloud platforms provide CDNs with the ability to run some kind of lambda or
worker during web requests, and you can potentially use them to perform token handler work before routing to APIs. This may also be useful for companies who do not yet host a reverse proxy in front of APIs. One possible setup is illustrated below, and involves these deployment steps:
- Configure and deploy a worker based OAuth Agent that points to your Identity Server
- Configure and deploy a worker based OAuth Proxy to run within the CDN before routing to APIs
This might be a convenient option but may not provide the cleanest separation of web and API concerns. One possible issue is that routing to APIs may have restrictions such as short timeouts and small message sizes, so check your CDN provider's capabilities in this area. The OAuth Agent only uses small messages, whereas larger API requests and responses will be routed through the OAuth Proxy.
Developers need to serve web static content locally when working on the SPA and this can be a little tricky to manage when using a backend for frontend. One option is for developers to run a local reverse proxy and token handler components using Docker, with URLs such as these:
|Web Static Content||http://localhost:3000|
A more productive option can be to use the multi-domain deployment option for developers, where they simply point the SPA to a deployed token handler, so that they only need to run a simple web host and the SPA code locally:
In the diagram, token handler components to support development are deployed to a dedicated API domain, at
http://api.myapp-dev.com. The local web host serves static content on
http://localhost:8080, which can be aliased to
http://web.myapp-dev.com via the hosts file. This web domain can then be used in the browser during development.
127.0.0.1 localhost web.myapp-dev.com :1 localhost
In both of these deployment options, the SPA and token handler components are in the same site, as long as the HTTP/S scheme and parent domains match, so first party cookies are issued. In both cases also, CORS pre-flight requests will be sent to token handler components, since these requests are considered cross origin if either the full domain name or the port does not match the web origin exactly.
When deploying SPAs it is vital to ensure that cookies are isolated between applications. The standard way to manage this in any web cookie based architecture is to use different API routes per web app, all of which forward to the actual APIs, which can be hosted anywhere.
When using the token handler pattern with separated web and API domains, the cleanest option is to use distinct API subdomains per app, each with their own OAuth Agent and OAuth Proxy Plugin. One possible setup that provides the required isolation is shown below:
Spinning up new domains or subdomains, such as
https://api-brand1.example.com, is straightforward when using cloud hosting, but may be more of a challenge when using on-premise hosting. It also works quite well in multi-team setups, where each app's developers should be responsible for the reliability of their own API routes. Adopting the token handler pattern will then have no impact on microservices or the teams who build them.
The token handler pattern is focused on separating web and API concerns for the best architecture. It can be deployed in many setups, as summarized in this article, and the option you choose will depend on your preferred architecture.
Curity provide a number of OAuth Agent and OAuth Proxy implementations that you can plug in, or adapt if required. See the OAuth for Web Home Page for details and links to tutorials. The Token Handler Deployment Example explains the URL and configuration details, which can then be adapted to other deployment use cases.