Skip to content

cnapcloud/kong-oidc

Repository files navigation

kong-oidc

Kong 3.9 OIDC plugin based on revomatico/kong-oidc, with Keycloak compatibility fixes and enhancements. zmartzone/lua-resty-openidc is vendored and patched directly for fixes that cannot be applied at the plugin layer.

Implements OpenID Connect Authorization Code flow as a Kong plugin. Authenticates requests against an OIDC provider (Keycloak, Auth0, etc.) and forwards user identity to upstream services via HTTP headers.

Upstream headers set on authenticated requests:

Header Content
X-Userinfo Base64-encoded userinfo JSON
X-Access-Token Raw JWT string (or Bearer <token> if access_token_as_bearer=yes)
X-ID-Token Base64-encoded ID token (with sig)
X-Credential-Identifier sub claim from userinfo/ID token

Groups are extracted from the token (default claim: groups) and set in kong.ctx.shared.authenticated_groups.

A pre-built image is available on Docker Hub: cnapcloud/kong-oidc:3.9.1


Build

# Build image
make docker-build

# Quick rebuild after source changes
make docker-build-quick

# Publish multi-arch image to Docker Hub
make publish

Test

Unit tests

make docker-build-quick
make test

Integration tests (Kong + Keycloak)

make docker-build-quick
# host LAN IP — Kong container needs this to reach Keycloak
export IP=<host-lan-ip>
./bin/build-env.sh
# OIDC test:    http://localhost:8000/httpbin  (login: admin/password)
# Kong Manager: http://localhost:8002

# Teardown
./bin/teardown-env.sh

Docker Compose

kong:
  image: kong_oidc${KONG_TAG}
  ports:
    - 8000:8000
    - 8443:8443
    - 8001:8001
    - 8002:8002
    - 8444:8444
  environment:
    KONG_PLUGINS: bundled,oidc,cookies-to-headers
    KONG_X_SESSION_STORAGE: redis
    KONG_X_SESSION_REDIS_HOST: redis
    KONG_X_SESSION_REDIS_PASSWORD: redis
    KONG_X_SESSION_COMPRESSOR: zlib
    KONG_NGINX_LARGE_CLIENT_HEADER_BUFFERS: "4 16k"
    KONG_DATABASE: postgres
    KONG_PG_HOST: postgres_db
    KONG_PG_DATABASE: ${KONG_DB_NAME}
    KONG_PG_USER: ${KONG_DB_USER}
    KONG_PG_PASSWORD: ${KONG_DB_PW}
    KONG_ADMIN_LISTEN: 0.0.0.0:${KONG_HTTP_ADMIN_PORT}
    KONG_PROXY_LISTEN: 0.0.0.0:${KONG_HTTP_PROXY_PORT}
  depends_on:
    - postgres_db
    - redis

Full example: test/integration/docker-compose.yml


Plugin Configuration

Enable on a service

curl -X POST http://localhost:8001/services/<service_id>/plugins \
  -d "name=oidc" \
  -d "config.client_id=kong-oidc" \
  -d "config.client_secret=<secret>" \
  -d "config.discovery=https://<oidc_provider>/.well-known/openid-configuration"

Enable globally

curl -X POST http://localhost:8001/plugins \
  -d "name=oidc" \
  -d "config.client_id=kong-oidc" \
  -d "config.client_secret=<secret>" \
  -d "config.discovery=https://<oidc_provider>/.well-known/openid-configuration"

Parameters

Parameter Default Required Description
config.client_id yes OIDC Client ID
config.client_secret yes OIDC Client secret
config.discovery https://.../.well-known/openid-configuration no OIDC Discovery endpoint
config.scope openid no OAuth2 scope (must include openid)
config.ssl_verify false no Verify SSL cert of OIDC provider
config.session_secret no Secret for encrypting session cookie
config.introspection_endpoint no Token introspection endpoint
config.introspection_endpoint_auth_method client_secret_basic no Introspection auth method (client_secret_basic or client_secret_post)
config.timeout no Timeout for OIDC endpoint calls
config.bearer_only no no Introspect tokens only, no redirect
config.realm kong no Realm for WWW-Authenticate header
config.logout_path /logout no Path to trigger OIDC logout
config.unauth_action auth no auth = redirect to login, deny = return 401
config.recovery_page_path no Redirect path on error (use / to redirect home silently)
config.redirect_uri no URI the OP redirects to after authentication
config.ignore_auth_filters no Comma-separated list of paths to bypass auth
config.userinfo_header_name X-Userinfo no Upstream header for userinfo
config.id_token_header_name X-ID-Token no Upstream header for ID token
config.access_token_header_name X-Access-Token no Upstream header for access token
config.access_token_as_bearer no no Pass access token as Authorization: Bearer
config.disable_userinfo_header no no Disable forwarding userinfo header
config.disable_id_token_header no no Disable forwarding ID token header
config.disable_access_token_header no no Disable forwarding access token header
config.groups_claim groups no Token claim name to read groups from
config.skip_already_auth_requests no no Skip if credentials already set by a higher-priority plugin
config.bearer_jwt_auth_enable no no Validate JWT in Authorization: Bearer header
config.bearer_jwt_auth_allowed_auds no Allowed aud values for JWT validation (defaults to client_id)
config.bearer_jwt_auth_signing_algs ['RS256'] no Allowed signing algorithms for JWT validation
config.header_names no Custom upstream header names mapped from claims (paired with header_claims)
config.header_claims no Claim names to map to custom headers (paired with header_names)
config.http_proxy no HTTP proxy URL
config.https_proxy no HTTPS proxy URL (must use http:// scheme)
config.idletime 900 no Session idle timeout in seconds
config.lifetime 3600 no Session max lifetime in seconds
config.renew 600 no Session renewal window in seconds

To pass access token as a Bearer token:

config.access_token_header_name = Authorization
config.access_token_as_bearer   = yes

Session Storage

Session name is set per-service: <route_id>_<plugin_name>_session

Set KONG_X_SESSION_STORAGE to one of: cookie (default), shm, memcache, redis, dshm

Cookie (default, not recommended for production)

KONG_X_SESSION_COMPRESSOR=zlib                   # reduce cookie size
KONG_NGINX_LARGE_CLIENT_HEADER_BUFFERS='4 16k'   # if cookie is too large

SHM (recommended for single instance)

KONG_X_SESSION_STORAGE=shm
KONG_X_SESSION_SHM_STORE=oidc_sessions           # default
KONG_X_SESSION_SHM_STORE_SIZE=5m                 # default

Memcached

KONG_X_SESSION_STORAGE=memcache
KONG_X_SESSION_MEMCACHE_HOST=memcached           # default
KONG_X_SESSION_MEMCACHE_PORT="'11211'"           # note: numeric values must be double-quoted

Redis

KONG_X_SESSION_STORAGE=redis
KONG_X_SESSION_REDIS_HOST=redis
KONG_X_SESSION_REDIS_PASSWORD=<password>

DSHM (Hazelcast)

KONG_X_SESSION_STORAGE=dshm
KONG_X_SESSION_DSHM_HOST=hazelcast               # default
KONG_X_SESSION_DSHM_PORT=4321                    # default
KONG_X_SESSION_DSHM_REGION=oidc_sessions         # default

Environment Variables

Variable Description
KONG_X_SESSION_SECRET Session encryption secret (required). Set in nginx_kong.lua at build time
KONG_X_SESSION_NAME Session cookie name (default: oidc_session)
KONG_PLUGINS Enable plugins: bundled,oidc,cookies-to-headers
KONG_X_NOLOG_LIST_FILE Path to file listing IPs to exclude from access log

Numeric env vars must be double-quoted: KONG_X_VAR="'1234'"

Exclude IPs from access log (KONG_X_NOLOG_LIST_FILE=/tmp/nolog.txt):

127.0.0.1 0;

What We Changed and Why

Fixes and enhancements made while testing against Keycloak 26.5.

kong/plugins/oidc

Extended from revomatico/kong-oidc with the following changes:

  • Added: store_enc_id_token = true option for proper logout support
  • Changed: revoke_tokens_on_logout default to yes
  • Changed: logout_path and redirect_after_logout_uri are now supported
  • Changed: session name is now scoped per-service: <route_id>_<plugin_name>_session
  • Added: idletime (default 900s), lifetime (default 3600s), renew (default 600s) config options
  • Added: session storage variables to kong/templates/nginx_kong.lua
  • Changed: ID token header now forwards the original signed JWT (header.payload.signature) instead of the re-encoded payload JSON (which has no signature)

resty/openidc.lua

zmartzone/lua-resty-openidc is vendored directly into resty/openidc.lua to apply the following patches that cannot be made via the plugin layer alone:

  • Fixed: end_session_endpoint missing from Keycloak discovery response — auto-fallback to <issuer>/protocol/openid-connect/logout
  • Fixed: JSON parse failure after Keycloak endpoint discovery that caused opts not being applied
  • Removed redundant access token revocation on logout (Keycloak warns when access token is revoked after refresh token)
  • Fixed: logout always uses end_session_endpoint when present, ignoring redirect_after_logout_with_id_token_hint
  • Fixed: browser back button after auth caused 500 error (stale /cb with no session state) — now redirects gracefully to /
  • Fixed: /cb URL added to browser history after auth — replaced HTTP 302 redirect with window.location.replace() so the callback URL is not recorded in history

Image build

  • Added Dockerfile-quick (make docker-build-quick) for fast rebuilds during development
  • Integration tests now use the locally built image via docker-compose

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors