Token Handler Development Setup
On this page
The token handler pattern follows security best practices for Single Page Applications (SPA). OpenID Connect is combined with an application cookie layer, and only the most secure
SameSite=strict cookies are used in the browser. This is done without compromising the web architecture, and Curity also provides open source components, so that you do not need to write any security code.
The separation of concerns used in the token handler pattern does however add complexity to the development setup. In older website architectures, developers ran a web backend that managed static content, the OAuth client, and API routes together:
In the token handler pattern, the same concerns remain, but deployment is more separated. When adopting the token handler pattern, you need to start with some deployment work. This article describes some cloud native patterns you can use. Once complete, you will be able to write code in a pure SPA manner, while also running production level security on your workstation.
Docker Token Handler
The main components used are introduced in the token handler overview. On a development computer, the simplest way to get started running them is to use docker. A local API gateway then sits in front of the OAuth agent and manages forwarding JWT access tokens to APIs:
First, run a couple of
docker build commands, to configure the gateway and build the OAuth agent. If using Curity open source components, you would run commands similar to these:
docker build -t oauthagent:1.0.0 .docker build -t custom_kong:3.0.0 .
Then use a docker based deployment to run the token handler components locally. A docker compose setup would use settings such as these:
version: '3.8'services:kong-api-gateway:image: custom_kong:3.0.0hostname: apigateway-internalports:- 80:3000volumes:- ./components/api-gateway/kong/kong.yml:/usr/local/kong/declarative/kong.ymlenvironment:KONG_DATABASE: 'off'KONG_DECLARATIVE_CONFIG: '/usr/local/kong/declarative/kong.yml'KONG_PROXY_LISTEN: '0.0.0.0:3001'KONG_LOG_LEVEL: 'info'KONG_PLUGINS: 'bundled,oauth-proxy,phantom-token'KONG_NGINX_HTTP_LUA_SHARED_DICT: 'phantom-token 10m'oauth-agent:image: oauthagent:1.0.0hostname: oauthagent-internalenvironment:PORT: 3001TRUSTED_WEB_ORIGIN: 'http://localhost:3000'ISSUER: 'https://login.example-dev.com/oauth/v2/oauth-anonymous'AUTHORIZE_ENDPOINT: 'https://login.example-dev.com/oauth/v2/oauth-authorize'TOKEN_ENDPOINT: 'https://login.example-dev.com/oauth/v2/oauth-token'USERINFO_ENDPOINT: 'https://login.example-dev.com/oauth/v2/oauth-userinfo'LOGOUT_ENDPOINT: 'https://login.example-dev.com/oauth/v2/oauth-session/logout'CLIENT_ID: 'spa-client'CLIENT_SECRET: 'Password1'REDIRECT_URI: 'http://localhost:3000/'POST_LOGOUT_REDIRECT_URI: 'http://localhost:3000/'SCOPE: 'openid profile'COOKIE_DOMAIN: 'localhost'COOKIE_NAME_PREFIX: 'example'COOKIE_ENCRYPTION_KEY: 'fda91643fce9af565bdc34cd965b48da75d1f5bd8846bf0910dd6d7b10f06dfe'CORS_ENABLED: 'true'SERVER_CERT_P12_PATH: ''SERVER_CERT_P12_PASSWORD: ''
In this example, static web content is downloaded to the browser from an SPA based web host, such as the webpack development server, running at
http://localhost:3000. Meanwhile, the SPA sends OAuth and API requests via the API gateway, which runs at
Same Site Development Setup
Although you can use
localhost URLs, it is instead recommended to switch to a more meaningful web origin for development. The simplest option is to invent a web domain name for local development, then add it to your computer's hosts file:
The OAuth agent configuration can then be updated to use local development URLs that convey architectural meaning:
oauth-agent:image: oauthagent:1.0.0hostname: oauthagent-internalenvironment:PORT: 3001TRUSTED_WEB_ORIGIN: 'http://www.example-local.com:3000'CLIENT_ID: 'spa-client'CLIENT_SECRET: 'Password1'REDIRECT_URI: 'http://www.example-local.com:3000/'POST_LOGOUT_REDIRECT_URI: 'http://www.example-local.com:3000/'SCOPE: 'openid profile'COOKIE_DOMAIN: 'www.example-local.com'COOKIE_NAME_PREFIX: 'example'
This development setup mirrors the following production deployment, where all three web concerns are hosted together behind a gateway. The only difference during web development is that static content is served locally:
Multi Site Development Setup
Alternatively, you can add two local domains, to separate the web and API concerns more completely:
127.0.0.1 www.example-local.com api.example-local.com
The OAuth agent configuration can then be updated to use a different cookie domain. Cookies are only needed to securely access APIs, and not during requests for static content:
oauth-agent:image: oauthagent:1.0.0hostname: oauthagent-internalenvironment:PORT: 3001TRUSTED_WEB_ORIGIN: 'http://www.example-local.com:3000'CLIENT_ID: 'spa-client'CLIENT_SECRET: 'Password1'REDIRECT_URI: 'http://www.example-local.com:3000/'POST_LOGOUT_REDIRECT_URI: 'http://www.example-local.com:3000/'SCOPE: 'openid profile'COOKIE_DOMAIN: 'api.example-local.com'COOKIE_NAME_PREFIX: 'example'
This development setup mirrors the following production deployment, where web static content is downloaded from a content delivery network. Of course, during web development, the static content will continue to be served locally:
Develop with a Remote Token Handler
Once deployment is understood, you can then use a DNS solution to avoid needing to run token handler components locally. To do so, run the same Docker deployment to push them to a shared environment, dedicated to enabling productive web development.
One option is to spin up a domain in your cloud platform, while using adding an entry for the web domain to your hosts file. Either HTTP or HTTPS URLs could be used for a development setup. You then have a pure SPA web development setup, and you also operate with the same security as your customer users:
Cross Origin Requests
All of the development setups shown above serve static content from one process, while managing OAuth and API requests from other processes. This requires different ports or host names to be used during development. Requests from the SPA to the API gateway and OAuth agent are therefore cross origin. When calling APIs, the SPA will use CORS request and response headers. The Curity open source components manage this in the correct way.
Full Stack Setups
A Docker based deployment can remain useful, for best troubleshooting. It also provides finer control over custom individual setups, without impacting the rest of the web development team. A common use case is for a developer to want to run both the local SPA and one or more APIs locally. To do so, that developer can simply route from the Docker API gateway back to the local computer, using the well known host name of
If you are new to the token handler pattern, Curity provides an end-to-end example that you can run on a development computer. The deployment details are also explained, in a separate code example whose settings you can quickly reverse engineer, then apply to your own deployments:
The token handler pattern enables you to implement a secure cookie layer at the application level, while also following a pure SPA development model. This is done by separating web and API concerns, which in turn adds more moving parts to the development setup. Some initial investment in deployment is required, after which you can continue to focus only on web customer experiences, with the difficult security externalized.
Join our Newsletter
Get the latest on identity management, API Security and authentication straight to your inbox.
Start Free Trial
Try the Curity Identity Server for Free. Get up and running in 10 minutes.Start Free Trial