Custom Authentication With Kong
Tags: kong, authentication, custom, lua
Published: 2022-08-25 02:08:00

## The Kong Gateway

The [Kong Gateway](https://docs.konghq.com/gateway/latest/) is an API Gateway to manage, configure and route requests to multiple APIs.

It's used to release a single address to access multiple APIs at the same time.

## Objectives

This post presents a step by step to create a plugin with authentication using an external service. This solution is used when exists multiple services managed by Kong that extends the **same authorization method**.

## The Original Problem
![OriginalProblem](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sbgob0ys4vgq30ngb9go.png)

In our original problem the front-end needs to know how the addresses of all services to execute requests, and both services needs request the **external authentication service** to validate the **authorization token**.

## Adding Kong
![Using Kong](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xxoxeh5f0aowrw8x3con.png)

Now, the front-end knows just kong address and kong register all services to release requests. However, all services still require the external authentication service.

## The Solution
Our solution, implements a **Kong Plugin** to validate the authentication in external service and just at success case the request to services is released.

![Final Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rymyyltpdh5o7f49dqpd.png)

## Plugin Development
It's possible add some logics to execute during the **Kong requests lifecycle**. To do this, we add plugins with different purposes. In our case, we created a plugin named _cutom-auth_ to request the **external auth request** and validate the authentication before requesting services.

The plugin structure contains two files developed in [lua](https://www.lua.org/):

- **schema.lua**: This file is used to define the plugin configurations. In our case we defined the external auth request URL and the paths to ignore authentication.
- **handler.lua**: The core file to execute the plugin logic. Here is the request to external auth to validate authentication.

Bellow we can see the code of this two files:

#### schema.lua

```
local typedefs = require "kong.db.schema.typedefs"

return {
  name = "custom-auth",
  fields = {
    { protocols = typedefs.protocols_http },
    { consumer = typedefs.no_consumer },
    { config = {
      type = "record",
      fields = {
        { url = typedefs.url({ required = true }) },
        {
          public_paths = {
            type = "array",
            default = {},
            required = false,
            elements = { type = "string" },
          }
        },
      },
    }, },
  },
}
```

#### handler.lua

```
local http = require "resty.http"
local ngx = require "ngx"
local cjson = require "cjson"

local ExternalAuthHandler = {
  VERSION = "1.0",
  PRIORITY = 1000,
}

function ExternalAuthHandler:access(conf)
  local path = kong.request.get_path()
  local publicPaths = conf.public_paths;

  for i, pub_path in ipairs(publicPaths) do
    if pub_path == path then
      return
    end
  end

  local client = http.new()

  kong.log("Validating Authentication: ", conf.url)
  local res, err = client:request_uri(conf.url, {
    ssl_verify = false,
    headers = {
      Authorization = kong.request.get_header("Authorization"),
    }
  })

  if not res then
    kong.log.err("Invalid Authentication Response: ", err)
    return kong.response.exit(500)
  end

  if res.status ~= 200 then
    kong.log.err("Invalid Authentication Response Status: ", res.status)
    return kong.response.exit(401)
  end

  local json = cjson.encode(res.body)
  local user_info = cjson.decode(json)
  kong.service.request.set_header("X-UserInfo", ngx.encode_base64(user_info))
end

return ExternalAuthHandler
```

## Explanation
In **schema.lua** we defined two params:

- **url**: Used to set the external auth url to validate authentication. In our case, this request validates the authentication and returns the current user.
- **public_paths**: Used to set the service public paths. The paths mapped here will not require authentication validation.

In **handler.lua** have some steps. The _access_ method is the entry point to Kong execute this plugin in lifecycle.

The first operation is execute a **GET** in URL defined to validate the authentication. In case the request returns an error or an invalid status the Kong will be exit with status 500 or 401.

In case of success the **X-UserInfo** header will be added to the request with the current user data mapped in a _base64 string_. The services can be use this header to validate authorization or get user data if necessary.

## Conclusion
With this plugin the Kong can validate authentication before requesting services and the structure of the diagram solution is finished.

We validated the use of this plugin in [I9Partner Company](https://i9partner.com.br/) using [Kong](https://konghq.com/) and multiple internal services.

Here is an example of this plugin usage mapped in [Konga](https://github.com/pantsel/konga).

![Konga Configuration](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yl8ylzokpbz25g5klxns.png)

You can follow the repository to this project [here](https://github.com/jeffersonxavier/custom-kong-auth).

## Reference

- [Kong Gateway](https://docs.konghq.com/gateway/latest/)
- [Custom Authentication With Kong](https://konghq.com/blog/custom-authentication-and-authorization-framework-with-kong)
 
## Authors

[Jefferson Xavier](https://github.com/jeffersonxavier)
[Thyaki Takuno](https://github.com/thyakitakuno)

A [I9Partner](https://i9partner.com.br/) initiative.