Kong Dynamic User Routing Plugin

Kong Dynamic User Routing Plugin


This tutorial shows how to implement dynamic routing of OAuth requests using a Kong reverse proxy. The code resources described are available in the Dynamic User Routing GitHub Repository.

This routing capability enables companies to deploy the Curity Identity Server to multiple regions, while forwarding OAuth requests for users to their home region. The overall solution is described in the Implementing Dynamic User Routing walkthrough.

Docker Image

The default docker image has been customized to copy in the following two plugins:

Zone TransferPlugin created in this how-to, which implements custom routing logic
lua-resty-jwtUsed by the plugin to parse JWTs in LUA
FROM kong:2.8.1-alpine

USER root
RUN luarocks install lua-resty-jwt
COPY reverse-proxy/kong/zone-transfer-plugin/* /usr/local/share/lua/5.1/kong/plugins/zone-transfer/
USER kong

Proxy Configuration

The example uses the DB-less and Declarative Configuration which provides the simplest local deployment options. The Kong proxy routes are provided by a kong.yml file, which would be different for each stage of a company's deployment pipeline.

_format_version: '2.1'
_transform: true

- name: curity
  url: http://internal-curity-eu:8443
  - name: default
    - /
  - name: zone-transfer
      eu_host: internal-curity-eu
      us_host: internal-curity-us
      cookie_name: zone
      claim_name: zone

The plugin comes into effect for all paths and supplies values for host names to the plugin's logic. The host name in the URL field will then be overridden at runtime by the plugin's calculated value.

Plugin Code

The code for the plugin is easy to follow and simply involves reading HTTP request values. The various responsibilities are broken down into functions in a standard way.

-- Try to read the zone value from an OAuth request
function _M.run(config)

  local method = string.lower(ngx.var.request_method)
  if method == 'options' or method == 'head' then
    return nil

  -- First try to find a value in the zone cookie
  local zone = get_zone_from_cookie(config.cookie_name)

  -- Otherwise, for POST messages look in the form body
  if zone == nil and method == 'post' then
    zone = get_zone_from_form(config.claim_name)

  -- Update the host name if a zone is found
  if zone == 'eu' then
    ngx.ctx.balancer_address.host = config.eu_host
  elseif zone == 'us' then
    ngx.ctx.balancer_address.host = config.us_host

Docker Compose

In the repository's Docker Compose file, the overall deployment of the Kong reverse proxy is defined, which includes copying in the environment specific kong.yml file. In the example setup Kong runs on port 8080 and is exposed to the host PC at port 80:

    image: custom_kong:2.8.1
    hostname: internal-kong
      - 80:8080
      - ./reverse-proxy/kong/kong.yml:/usr/local/kong/declarative/kong.yml
      KONG_DATABASE: 'off'
      KONG_DECLARATIVE_CONFIG: '/usr/local/kong/declarative/kong.yml'
      KONG_LOG_LEVEL: 'info'
      KONG_PLUGINS: 'bundled,zone-transfer'

Working Plugin

When the reverse proxy is run as part of an overall solution, Kong outputs log messages to show zones received in OAuth requests, so that the plugin's behavior can be visualized:

*** Found zone 'eu' in cookie, client:, server: , request: "POST /authn/authentication/UserName-Password HTTP/1.1"
*** Found zone 'eu' in wrapped token, client:, server: , request: "POST /oauth/v2/oauth-token HTTP/1.1"
*** Found zone 'eu' in cookie, client:, server: , request: "POST /authn/authentication/UserName HTTP/1.1"
*** Found zone 'us' in cookie, client:, server: , request: "POST /authn/authentication/UserName-Password HTTP/1.1"
*** Found zone 'us' in wrapped token, client:, server: , request: "POST /oauth/v2/oauth-token HTTP/1.1"


It is a standard job to implement dynamic routing in API gateways, and this is straightforward using Kong's extensibility features. When combined with the Curity Identity Server's multi zone features, a company can deploy a global IAM system with user data separated by region, and with good reliability.