The OIDC extension for the Kong API Gateway

🌐 This document is available in both English and Ukrainian. Use the language toggle in the top right corner to switch between versions.

1. General component overview

The kong-oidc is an extension for the Kong API Gateway that implements the functionality of a Relying Party in the OpenID Connect protocol.

This extension is used as a reverse proxy server, acting as an OAuth/OpenID Connect termination point before the target service. This enables securing the target service with the appropriate standards without implementing them directly within the service itself.

2. Component functions

The extension authenticates users through the OpenID Connect Provider (KeyCloak IAM) using OpenID Connect Discovery and the basic client profile (Authorization Code flow).

It also supports sessions for authenticated users, utilizing the lua-resty-openidc library. This solution offers a customizable choice between storing the session state in the client’s browser cookie or using one of the available server-side storage mechanisms: shared-memory|memcache|redis.

The access token JWT validation feature allows previously authenticated users and/or programs with access tokens to pass through Kong. The Resource Server performs the actual token validation in such cases.

3. Implementation principles

This component is an extension of the nokia/kong-oidc plugin, which in turn is based on the lua-resty-openidc library.

The diagram below illustrates the message exchange between the involved parties during the authentication process.

kong_oidc_flow

After authentication and before forwarding the request to the target service, the kong-oidc plugin adds the following headers to the request:

  • X-Access-Token: Contains the access token

  • X-Id-Token: Contains the OIDC token

  • X-Userinfo: Contains the response body from the /userinfo endpoint.

X-Userinfo: {"preferred_username":"alice","id":"60f65308-3510-40ca-83f0-e9c0151cc680","sub":"60f65308-3510-40ca-83f0-e9c0151cc680"}

The plugin also sets the ngx.ctx.authenticated_consumer variable, which can be used in other Kong plugins:

ngx.ctx.authenticated_consumer = {
    id = "60f65308-3510-40ca-83f0-e9c0151cc680",   -- sub field from Userinfo
    username = "alice"                             -- preferred_username from Userinfo
}

5. Usage

The plugin supports the following configurations:

Parameter

Default value

Required

Description

name

yes

Plugin name, must be oidc

config.client_id

yes

OIDC Client ID

config.client_secret_var_name

no

Name of the environment variable containing the OIDC Client secret.

config.client_secret

no

OIDC Client secret. Can also be set via an environment variable. In such case, config.client_secret_var_name must be set.

config.discovery

https://.well-known/openid-configuration

no

OIDC Discovery Endpoint (/.well-known/openid-configuration)

config.scope

openid

no

OAuth2 Token scope. To use OIDC, it must contain openid.

config.ssl_verify

false

no

Enables SSL verification to the OIDC provider.

config.introspection_endpoint

no

Token introspection endpoint

config.allow_token_auth

false

no

Allows access with a Bearer token. If enabled, introspection_endpoint must be set.

config.timeout

no

Timeout for OIDC endpoint calls.

config.introspection_endpoint_auth_method

client_secret_basic

no

Authentication method for Token introspection. resty-openidc supports client_secret_(basic|post).

config.bearer_only

no

no

Only token verification without redirection.

config.realm

kong

no

Realm used in the WWW-Authenticate response header.

config.logout_path

/logout

no

Absolute path for logout from the OIDC RP.

config.unauth_action

deny

yes

Behavior when a user is unauthenticated: nil - redirect to the authentication page, pass - do not redirect, deny - do not redirect but return an error instead.

config.access_token_header_name

X-Access-Token

yes

Name for the access token header.

config.bearer_access_token

"no"

yes

Use bearer access token.

config.id_token_header_name

X-ID-Token

yes

Name for the ID token header.

config.user_header_name

X-Userinfo

yes

Name for the header containing user information.

config.session_opts

no

JSON string with user session configuration. Session management is built on lua-resty-session. Detailed description of configuration parameters for session management is provided in the library’s documentation.

config.enable_authorization

true

no

To enable authorization based on JWT access token attributes like issuer, scope, and roles.

config.issuers_allowed

no

List of allowed access token issuers (iss claim). Example: issuers_allowed:\["https://domain/issuer1", "https://domain/issuer2"]

config.scopes_required

nil

no

Scopes that must be present in the access token for successful authorization.

config.roles_required

nil

no

Roles (roles_claim claim) that must be present (at least one) in the access token for successful authorization.

config.realm_roles_required

nil

no

Realm roles (realm_access.roles claim) that must be present (at least one) in the access token for successful authorization.

config.client_roles_required

nil

no

Client roles (resource_access claim) that must be present (at least one) in the access token for successful authorization.

config.token_claim_header_value

no

Name of the token claim attribute whose value will be added to the "token-claim" request header. The value of this header can be used by other plugins, for example, to calculate request limits for an authenticated user or group of users.

When using environment variables to configure parameters that enable such a mechanism, an additional environment variable KONG_NGINX_MAIN_ENV needs to be set with values listing other read-access environment variables that the OIDC plugin should be allowed to read. KONG_NGINX_MAIN_ENV utilizes NGINX Injected Directives to declare environment variables containing secrets in nginx.conf using the env command, thereby allowing the OIDC plugin to read their values.

5.1. Request to the target service

By default, the plugin adds three additional headers to the original request: X-Userinfo, X-Access-Token, and X-Id-Token, which can be utilized by services. All of these headers are base64-encoded.

GET / HTTP/1.1
Host: netcat:9000
Connection: keep-alive
X-Forwarded-For: 172.19.0.1
X-Forwarded-Proto: http
X-Forwarded-Host: localhost
X-Forwarded-Port: 8000
X-Real-IP: 172.19.0.1
Cache-Control: max-age=0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: pl-PL,pl;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: session=KOn1am4mhQLKazlCA.....
X-Userinfo: eyJnaXZlbl9uYW1lIjoixITEmMWaw5PFgcW7xbnEhiIsInN1YiI6ImM4NThiYzAxLTBiM2ItNDQzNy1hMGVlLWE1ZTY0ODkwMDE5ZCIsInByZWZlcnJlZF91c2VybmFtZSI6ImFkbWluIiwibmFtZSI6IsSExJjFmsOTxYHFu8W5xIYiLCJ1c2VybmFtZSI6ImFkbWluIiwiaWQiOiJjODU4YmMwMS0wYjNiLTQ0MzctYTBlZS1hNWU2NDg5MDAxOWQifQ==
X-Access-Token: eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJGenFSY0N1Ry13dzlrQUJBVng1ZG9sT2ZwTFhBNWZiRGFlVDRiemtnSzZRIn0.eyJqdGkiOiIxYjhmYzlkMC1jMjlmLTQwY2ItYWM4OC1kNzMyY2FkODcxY2IiLCJleHAiOjE1NDg1MTA4MjksIm5iZiI6MCwiaWF0IjoxNTQ4NTEwNzY5LCJpc3MiOiJodHRwOi8vMTkyLjE2OC4wLjk6ODA4MC9hdXRoL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOlsibWFzdGVyLXJlYWxtIiwiYWNjb3VudCJdLCJzdWIiOiJhNmE3OGQ5MS01NDk0LTRjZTMtOTU1NS04NzhhMTg1Y2E0YjkiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJrb25nIiwibm9uY2UiOiJmNGRkNDU2YzBjZTY4ZmFmYWJmNGY4ZDA3YjQ0YWE4NiIsImF1dGhfdGltZSI6…IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJhZG1pbiJ9.GWuguFjSEDGxw_vbD04UMKxtai15BE2lwBO0YkSzp-NKZ2SxAzl0nyhZxpP0VTzk712nQ8f_If5-mQBf_rqEVnOraDmX5NOXP0B8AoaS1jsdq4EomrhZGqlWmuaV71Cnqrw66iaouBR_6Q0s8bgc1FpCPyACM4VWs57CBdTrAZ2iv8dau5ODkbEvSgIgoLgBbUvjRKz1H0KyeBcXlVSgHJ_2zB9q2HvidBsQEIwTP8sWc6er-5AltLbV8ceBg5OaZ4xHoramMoz2xW-ttjIujS382QQn3iekNByb62O2cssTP3UYC747ehXReCrNZmDA6ecdnv8vOfIem3xNEnEmQw
X-Id-Token: eyJuYmYiOjAsImF6cCI6ImtvbmciLCJpYXQiOjE1NDg1MTA3NjksImlzcyI6Imh0dHA6XC9cLzE5Mi4xNjguMC45OjgwODBcL2F1dGhcL3JlYWxtc1wvbWFzdGVyIiwiYXVkIjoia29uZyIsIm5vbmNlIjoiZjRkZDQ1NmMwY2U2OGZhZmFiZjRmOGQwN2I0NGFhODYiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJhZG1pbiIsImF1dGhfdGltZSI6MTU0ODUxMDY5NywiYWNyIjoiMSIsInNlc3Npb25fc3RhdGUiOiJiNDZmODU2Ny0zODA3LTQ0YmMtYmU1Mi1iMTNiNWQzODI5MTQiLCJleHAiOjE1NDg1MTA4MjksImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwianRpIjoiMjI1ZDRhNDItM2Y3ZC00Y2I2LTkxMmMtOGNkYzM0Y2JiNTk2Iiwic3ViIjoiYTZhNzhkOTEtNTQ5NC00Y2UzLTk1NTUtODc4YTE4NWNhNGI5IiwidHlwIjoiSUQifQ==

The names of these headers can be changed in the plugin’s settings using the corresponding parameters: config.access_token_header_name, config.id_token_header_name, and config.user_header_name. Additionally, the access token can be made a bearer token by setting the value of the config.bearer_access_token parameter to yes.

Example:

config:
...
access_token_header_name: Authorization
bearer_access_token: 'yes'
kind: KongPlugin

As a result of this configuration, an Authorization header containing the bearer access token will be added to the original request.

Authorization: bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJGenFSY0N1Ry13dzlrQUJBVng1ZG9sT2ZwTFhBNWZiRGFlVDRiemtnSzZRIn0.eyJqdGkiOiIxYjhmYzlkMC1jMjlmLTQwY2ItYWM4OC1kNzMyY2FkODcxY2IiLCJleHAiOjE1NDg1MTA4MjksIm5iZiI6MCwiaWF0IjoxNTQ4NTEwNzY5LCJpc3MiOiJodHRwOi8vMTkyLjE2OC4wLjk6ODA4MC9hdXRoL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOlsibWFzdGVyLXJlYWxtIiwiYWNjb3VudCJdLCJzdWIiOiJhNmE3OGQ5MS01NDk0LTRjZTMtOTU1NS04NzhhMTg1Y2E0YjkiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJrb25nIiwibm9uY2UiOiJmNGRkNDU2YzBjZTY4ZmFmYWJmNGY4ZDA3YjQ0YWE4NiIsImF1dGhfdGltZSI6…IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJhZG1pbiJ9.GWuguFjSEDGxw_vbD04UMKxtai15BE2lwBO0YkSzp-NKZ2SxAzl0nyhZxpP0VTzk712nQ8f_If5-mQBf_rqEVnOraDmX5NOXP0B8AoaS1jsdq4EomrhZGqlWmuaV71Cnqrw66iaouBR_6Q0s8bgc1FpCPyACM4VWs57CBdTrAZ2iv8dau5ODkbEvSgIgoLgBbUvjRKz1H0KyeBcXlVSgHJ_2zB9q2HvidBsQEIwTP8sWc6er-5AltLbV8ceBg5OaZ4xHoramMoz2xW-ttjIujS382QQn3iekNByb62O2cssTP3UYC747ehXReCrNZmDA6ecdnv8vOfIem3xNEnEmQw

6. User session management

To manage user sessions, the plugin utilizes the lua-resty-session library, which allows storing JWT user tokens both on the client side in cookies and on the server side in one of the supported storage systems: shared-memory, memcache, or redis (single or cluster). Instructions for configuring these storage systems are available in the library’s repository linked above.

6.1. Storing sessions in Redis Sentinel

The library’s functionality has been extended to allow session storage in Redis Sentinel.

Example OIDC plugin configuration for storing sessions in Redis Sentinel:

config:
  session_opts:
    {"storage":"sentinel",
        "sentinel":{"sentinel_master_name":"mymaster",
            "sentinel_db":"sessions",
            "sentinel_host":"my-sentinel-host.env-dev.svc",
            "sentinel_port":"26379",
            "uselocking":false}}

The Redis access secret is set using the REDIS_AUTH_SECRET environment variable.

REDIS_AUTH_SECRET = some_secret_value
KONG_NGINX_MAIN_ENV = REDIS_AUTH_SECRET

6.2. Setting session secret

By default, the lua-resty-session library uses an auto-generated secret of 32 bytes for session management and HMAC digest calculation. However, if Kong installation involves multiple instances, each may generate its own secret. This can lead to sessions created by one Kong instance not being readable by other instances. To address this, you can either use sticky session concept (user requests always go to the same Kong instance) or explicitly set the same secret on all Kong instances. The secret can be set either by passing it in the OIDC plugin configuration as an additional parameter for session_opts:

config:
  session_opts:
    {"secret":"c29tZV9zZWNyZXRfdmFsdWU="}

Or through the OIDC_SESSION_SECRET environment variable:

OIDC_SESSION_SECRET = some_secret_value
KONG_NGINX_MAIN_ENV = OIDC_SESSION_SECRET

When using the OIDC configuration, the value of the session_opts.secret parameter must be base64-encoded. When using an environment variable, there’s no need to base64 encode the secret.