On this page
The Token Handler pattern is a modern, secure solution for Single Page Applications. It utilises encrypted, same-site session cookies, so that tokens are kept out of the browser. If you're not yet familiar with the pattern, then have a look at an architectural overview, which describes two new components:
- The OAuth Agent is responsible for obtaining tokens and issuing session cookies.
- The OAuth Proxy is the module responsible for decrypting the access token cookie and passing it to underlying APIs.
This code example shows a Cloudflare worker implementation of the OAuth Proxy module. The module is ready to use with the Cloudflare gateway, but can also serve as an example of a TypeScript implementation of the OAuth Proxy.
Overview of the Setup
Cloudflare is a popular Content Delivery Network solution, used by many company for a fast delivery of static content to users around the globe. Cloudflare's CDN offers a number of features which enable using it as an API gateway, and Workers are one of those features. They enable running serverless code functions on any route of a domain exposed via Cloudflare.
As a security component, the OAuth Proxy module should be run on every route in your API. This can be achieved by deploying the worker to a wildcard route. For example, if your business API would be exposed on the
api.example.com domain with a root path of
/api, then in the
wrangler.toml file you would have an entry like:
route = api.example.com/api/*
The worker performs request validation (checking allowed origin and CSRF tokens if required) and then decrypts the access token cookie to get the access token. If the Phantom Token Pattern is used, then the worker can exchange the opaque token for a JWT.
Next, the worker fetches the response from the API with the obtained access token sent in the
Authorization header. The API response is returned to the caller without any modifications.
Getting the Code
The code for this module is available on the Curity's GitHub. Use the Download on GitHub button to go to the repository.
Development and Testing
The worker uses environment variables from the
wrangler.toml file. You can run a local worker with the following commands, and the code in the
handler.ts file is then ready to receive secure cookie requests from a Single Page Application:
npm run loginnpm run buildnpm start
You can then act as the SPA by running some unit tests, developed in Jest. Note that tests run using the node API, thus polyfills are created for
btoa() in the
As tests run, the code in
src/handler.ts executes, and you can view its code to understand how cookie requests are processed. The key points around cookie and access token handling are summarized in this code snippet:
const accessTokenEncryptedCookie = cookies[config.cookieNamePrefix + '-at']accessToken = await decryptCookie(accessTokenEncryptedCookie, config.encryptionKey)accessToken = await exchangePhantomToken(accessToken, config, fetch)requestToForward = new Request(request)requestToForward.headers.set('Authorization', 'Bearer ' + accessToken)
Deploying the Worker
To deploy the worker set appropriate values in the
wrangler.toml file, then run
npm run publish.
The worker uses environment variables for configuration. These can be set in the
wrangler.toml file or directly from the Cloudflare workers UI. See the README file for additional information on these settings.
Integrating with an SPA and OAuth Agent
For a complete solution to function correctly, you need a few elements to work together:
- This module and the OAuth Agent module need to use the same encryption key. The key is a 256 bits encryption key represented as 64 hex characters.
- This module can use the Phantom Token pattern. When enabled in settings (
USE_PHANTOM_TOKENvariable set to
true), then the module will exchange the opaque token for a JWT and pass it to underlying APIs. For this option to work you need to properly set the introspection endpoint URL as well as a client ID and a client secret. The module uses Basic HTTP authentication for the client.
- Remember that the SPA, the OAuth Agent and this module must share the same domain (at least the parent domain), so that cookies work properly.
There is an end-to-end working example which contains Docker images for all the components needed to show the working solution. This example can be tailored to work with this Cloudflare worker, but will require some work to update the necessary configurations.