The Token Handler pattern is a modern, secure solution for Single Page Applications. It utilizes encrypted, same-site session cookies to keep tokens 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 an AWS Lambda Authorizer implementation of the OAuth Proxy module. The module is ready to use with the AWS API Gateway, but can also serve as an example of a TypeScript implementation of the OAuth Proxy.
AWS API Gateway is a powerful gateway with numerous features. It allows companies to expose APIs with data from different sources: AWS lambda functions, microservices deployed to EC2 instances, other AWS services, or even external sources.
Among many features of the gateway is the possibility to secure endpoints with Lambda Authorizers — serverless functions which process the request and make authorization decisions. Authorizers can return data to the gateway, that can be used in further processing of the request. This is used by the OAuth Proxy module — the extracted access token is returned to the gateway so that it can be forwarded to APIs.
As a security component, the OAuth Proxy authorizer should be run on every route in your API. This can only be achieved by properly configuring every resource and method exposed through the gateway so that it is protected by the authorizer.
The authorizer 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 authorizer exchanges the opaque token for a JWT. The authorizer then returns an
allow policy to the gateway and sets the JWT access token in the authorizer's context.
The gateway overwrites the
Authorization header with the token from the authorizer's context and proxies the request to the upstream service. The gateway is also responsible for setting proper CORS response headers, both on the responses from your APIs and on preflight requests.
The code for this module is available on Curity's GitHub. Use the Download on GitHub button to go to the repository.
To deploy an API secured with the OAuth Proxy authorizer, follow these steps:
- In the
serverless.yamlset your organization name, app name, and service name.
- Still in the
serverless.yaml, in the resources section, describe your API. Every endpoint is configured by a Resource entry and Method entries for supported HTTP methods. There is an example
OptionsDataMethod. The path is set in the resource entry. The Options method entry shows how to configure CORS headers. The example
PostDataMethodis configured to use the lambda authorizer. Make sure to copy the configuration for other methods and resources in your API, so that proper authorization request headers and CORS response headers are set.
- Move or copy the (hidden) file
.env; set any necessary environment variables in
serverless deployto deploy the authorizer code and API definition to AWS.
It is possible to manually build and deploy the authorizer to an API. See the repository's README for more details.
As touched on above, the authorizer uses environment variables for configuration. These are set in the
.env file. See the README file for additional information on these settings. The same file is used to pass CORS configuration settings to the serverless framework.
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. That example can be tailored to work with this example AWS API, but will require some work to update the necessary configurations. The
serverless.yaml file in the authorizer's repository creates an API gateway definition compatible with the API used in the end-to-end example.