/images/resources/tutorials/integration/lua-oauth-proxy-plugin.jpg

NGINX Lua OAuth Proxy Plugin

On this page

Overview

The OAuth proxy plugin can be deployed with an NGINX LUA based reverse proxy or API gateway. Its role is to deal with secure cookies and cross origin permissions, rather than needing to complicate code within your APIs. This article will show how to run, deploy and test the plugin.

Get the Code

First download the code from the GitHub repository, via the following command. The repository files consist of the plugin's LUA code and also some helper scripts for deployment and testing on a development computer:

bash
1
git clone https://github.com/curityio/nginx-lua-oauth-proxy-plugin

Prerequisites

Ensure that you have these components installed on the local computer:

The plugin is coded in the high level LUA programming language and can run in any NGINX based system with the LUA module enabled. To demonstrate this, a deployment script is included that can run the plugin in either Kong Open Source or OpenResty using Docker containers.

API Route Configuration

When using Kong, the OAuth proxy plugin is configured for an API or path using the required settings shown below. This enables it to return CORS headers for the SPA, and to decrypt received cookies:

yaml
1234567891011121314151617
_format_version: '2.1'
_transform: true
services:
- name: business-api
url: http://apiserver:3001
routes:
- name: business-api-route
paths:
- /
plugins:
- name: oauth-proxy
config:
cookie_name_prefix: example
encryption_key: $ENCRYPTION_KEY
trusted_web_origins:
- http://www.example.com
cors_enabled: true

When using OpenResty, the equivalent configuration looks like this:

nginx
12345678910111213141516171819
location ~ ^/ {
rewrite_by_lua_block {
local config = {
cookie_name_prefix = 'example',
encryption_key = os.getenv('ENCRYPTION_KEY'),
trusted_web_origins = {
'http://www.example.com'
},
cors_enabled = true
}
local oauthProxy = require 'resty.oauth-proxy'
oauthProxy.run(config)
}
proxy_pass http://apiserver:3001;
}

If required, the allow_tokens optional property can be used to pass requests that already contain a bearer token directly to the API, without applying cookie logic. In some setups this enables the same API routes to be used for both web and mobile clients. If some API routes are unsecured and return public information, you can use the path features of the reverse proxy to exclude the plugin from those routes.

Cross Origin Resource Sharing (CORS)

If the reverse proxy is deployed in a different domain to the web host, then CORS request and response headers need to be managed. In this case cors_enabled must be set to true. If instead you are running a same site deployment, with the reverse proxy running in the web host's domain, use the false setting instead.

When required, the plugin can return CORS Response Headers needed for the SPA and API to interact, and it is preferred to manage this concern in the gateway rather than complicating your API code. By default the plugin will return the following headers, and a number of optional CORS settings can be used for finer control. These are described in the README file of the GitHub repository.

text
12345
access-control-allow-origin: http://www.example.com
access-control-allow-credentials: true
access-control-allow-methods: OPTIONS,GET,HEAD,POST,PUT,PATCH,DELETE
access-control-allow-headers: x-example-csrf
access-control-max-age: 86400

Managing Cookie Encryption Keys

The OAuth proxy will receive secure cookies encrypted using AES256, and needs to be able to decrypt them, so must be configured with the same 32 byte (256 bit) symmetric key that the OAuth Agent uses, and this must be represented as 64 hex characters. The encryption key can be initially generated using a command such as the following:

bash
1
openssl rand 32 | xxd -p -c 64

Encryption keys should be renewed periodically as part of your deployments. Curity token handler components and the SPA Code Example handle this condition reliably. Users with existing sessions whose cookies use the old encryption key will need to sign in again, but will not experience any errors.

Deploy the Plugin

The simplest way to deploy the plugin is to use a custom Dockerfile and luarocks install, which deploys both the plugin and its dependencies. It is also possible to clone the GitHub repo and manually copy files to the locations described in this section.

Kong Deployment

The below example Dockerfile also installs the Kong Phantom Token Plugin. The luarocks command uses git to download LUA files, and it may be necessary to run git config commands first, depending on your environment.

dockerfile
1234567
FROM kong:3.0.0-alpine
USER root
RUN git config --global url."https://".insteadOf git:// && \
git config --global advice.detachedHead false && \
luarocks install kong-oauth-proxy 1.3.0 &&
luarocks install kong-phantom-token 2.0.0
USER kong

This deploys three files called handler.lua, schema.lua and access.lua to the docker image, inside an oauth-proxy folder within the Kong plugins location, which is a path such as /usr/local/share/lua/5.1/kong/plugins. The Docker Compose file then needs to reference the additional plugins in the KONG_PLUGINS environment variable.

yaml
1234567891011121314151617
version: '3.8'
services:
kong:
image: custom_kong:3.0.0-alpine
hostname: kongserver
ports:
- 3000:3000
volumes:
- ./kong/kong.yml:/usr/local/kong/declarative/kong.yml
environment:
KONG_DATABASE: 'off'
KONG_DECLARATIVE_CONFIG: '/usr/local/kong/declarative/kong.yml'
KONG_PROXY_LISTEN: '0.0.0.0:3000'
KONG_LOG_LEVEL: 'info'
KONG_PLUGINS: 'bundled,oauth-proxy,phantom-token'
profiles:
- kong

OpenResty Deployment

The equivalent command to build the OpenResty custom Docker image is almost the same, and this example also deploys the OpenResty Phantom Token Plugin:

dockerfile
1234567
FROM openresty/openresty:1.21.4.1-bionic
RUN apt-get update && apt-get install git -y
RUN git config --global url."https://".insteadOf git:// && \
git config --global advice.detachedHead false && \
luarocks install lua-resty-oauth-proxy 1.3.0 && \
luarocks install lua-resty-phantom-token 2.0.0

This deploys an oauth-proxy.lua file to the docker image, inside a resty folder within the lua_package_path, at a location such as /usr/local/openresty/lualib/resty. The Docker Compose file then needs to reference an NGINX configuration:

yaml
1234567891011
version: '3.8'
services:
openresty:
image: custom_openresty/openresty:1.21.4.1-bionic
hostname: openrestyserver
ports:
- 3000:3000
volumes:
- ./openresty/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf
profiles:
- openresty

Test the Plugin

Some basic integration tests are provided and use the curl tool to test various success and failure scenarios. These will help you to understand the plugin's behavior, and the responses returned to your applications. First deploy a local Docker image for your preferred Lua platform:

bash
123
cd docker
./deploy.sh kong
./deploy.sh openresty

Then run some curl based tests to act as a Single Page Application:

bash
12
cd ../test
./test.sh

The plugin follows OWASP Cross Site Request Forgery (CSRF) Best Practices for calls to APIs with secure cookies. This includes verifying the web origin and applying double submit cookie checks.

text
123456789101112131415161718192021222324252627282930
5. Testing CORS headers for error responses to the SPA ...
5. CORS error responses to the SPA have the correct headers
6. Testing GET with a valid encrypted cookie ...
6. GET with a valid encrypted cookie was successfully routed to the API
{
"accessToken": "42665300-efe8-419d-be52-07b53e208f46"
}
7. Testing POST with missing CSRF cookie ...
7. POST with a missing CSRF cookie was successfully rejected
{
"code": "unauthorized",
"message": "The request failed cookie authorization"
}
8. Testing POST with missing CSRF header ...
8. POST with a missing CSRF header was successfully rejected
{
"code": "unauthorized",
"message": "The request failed cookie authorization"
}
9. Testing POST with incorrect CSRF header ...
9. POST with an incorrect CSRF header was successfully rejected
{
"code": "unauthorized",
"message": "The request failed cookie authorization"
}
10. Testing POST with correct CSRF cookie and header ...
10. POST with correct CSRF cookie and header was successfully routed to the API
{
"accessToken": "42665300-efe8-419d-be52-07b53e208f46"
}

During execution you can view details of invalid and valid requests written to NGINX logs:

Proxy Logs

Access Token Types

The plugin can work with secure cookies that contain either of these access token types. The opaque option is recommended and is used by the SPA using Token Handler Pattern code example.

Access Token TypeBehavior
OpaqueThis keeps cookie sizes small, in which case a phantom token plugin is run next to get a JWT
JWTThis leads to large cookie sizes that can exceed default HTTP header size limits allowed by NGINX

Browsers and HTTP servers typically have a 4KB per cookie limit, so aim to stay within that. If the overall size of the cookie header exceeds 4KB then NGINX requires you to change server properties such as proxy_buffers, proxy_buffer_size and large_client_header_buffers. See the NGINX HTTP Proxy Module documentation for further details.

Conclusion

The OAuth proxy plugin enables you to handle cookie and CORS aspects centrally in the API gateway, and the cookie decryption key is also managed there. This enables you to use the strongest browser security in your SPAs without impacting the code or deployment of your APIs.

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