OpenID Connect (1.0) plugin allows for integration with a third party identity provider (IdP) in a standardized way. This plugin can be used to implement Kong as a (proxying) OAuth 2.0 resource server (RS) and/or as an OpenID Connect relying party (RP) between the client, and the upstream service.
The plugin supports several types of credentials and grants:
- Signed JWT access tokens (JWS)
- Opaque access tokens
- Refresh tokens
- Authorization code with client secret or PKCE
- Username and password
- Client credentials
- Session cookies
The plugin has been tested with several OpenID Connect providers:
- Auth0 (Kong Integration Guide)
- Amazon AWS Cognito (Kong Integration Guide)
- Connect2id
- Curity
- Dex
- Gluu
- Google (Kong Integration Guide)
- IdentityServer
- Keycloak
- Microsoft Azure Active Directory (Kong Integration Guide)
- Microsoft Active Directory Federation Services
- Microsoft Live Connect
- Okta (Kong Integration Guide)
- OneLogin
- OpenAM
- Paypal
- PingFederate
- Salesforce
- WSO2
- Yahoo!
As long as your provider supports OpenID Connect standards, the plugin should work, even if it is not specifically tested against it. Let Kong know if you want your provider to be tested and added to the list.
Once applied, any user with a valid credential can access the Service.
This plugin can be used for authentication in conjunction with the Application Registration plugin.
Important Configuration Parameters
This plugin includes many configuration parameters that allow finely grained customization. The following steps will help you get started setting up the plugin:
-
Configure:
config.issuer
.This parameter tells the plugin where to find discovery information, and it is the only required parameter. You should set the value
realm
oriss
on this parameter if you don’t have a discovery endpoint.Note: This does not have to match the URL of the
iss
claim in the access tokens being validated. To set URLs supported in theiss
claim, useconfig.issuers_allowed
. -
Decide what authentication grants to use with this plugin and configure the
config.auth_methods
field accordingly.In order to restrict the scope of potential attacks, the parameter should only contain the grants that you want to use.
-
In many cases, you also need to specify
config.client_id
, and if your identity provider requires authentication, such as on a token endpoint, you will need to specify the client authentication credentials too, for exampleconfig.client_secret
. -
If you are using a public identity provider, such as Google, you should limit the audience with
config.audience_required
to contain only yourconfig.client_id
. You may also need to adjustconfig.audience_claim
in case your identity provider uses a non-standard claim (other thanaud
as specified in JWT standard). This is important because some identity providers, such as Google, share public keys with different clients. -
If you are using Kong in DB-less mode with a declarative configuration and session cookie authentication, you should set
config.session_secret
. Leaving this parameter unset will result in every Nginx worker across your nodes encrypting and signing the cookies with their own secrets.
In summary, start with the following parameters:
config.issuer
config.auth_methods
config.client_id
(and in many cases the client authentication credentials)config.audience_required
(if using a public identity provider)config.session_secret
(if using Kong in DB-less mode)
Configuration Reference
This plugin is compatible with DB-less mode.
In DB-less mode, you configure Kong Gateway declaratively. Therefore, the Admin API is mostly read-only. The only tasks it can perform are all related to handling the declarative config, including:
- Setting a target's health status in the load balancer
- Validating configurations against schemas
- Uploading the declarative configuration using the
/config
endpoint
Example plugin configuration
Parameters
Here's a list of all the parameters which can be used in this plugin's configuration:
Form Parameter | Description |
---|---|
name
required Type: string |
The name of the plugin, in this case openid-connect . |
instance_name
optional Type: string |
An optional custom name to identify an instance of the plugin, for example Useful when running the same plugin in multiple contexts, for example, on multiple services. |
service.name or service.id
optional Type: string |
The name or ID of the service the plugin targets. Set one of these parameters if adding the plugin to a service through the top-level Not required if using |
route.name or route.id
optional Type: string |
The name or ID of the route the plugin targets. Set one of these parameters if adding the plugin to a route through the top-level Not required if using |
enabled
optional Type: boolean Default value: |
Whether this plugin will be applied. |
Authentication Grants Parameters for enabling only grants/credentials that you want to use. | |
Form Parameter | Description |
config.auth_methods
optional Type: array of string elements Default value: |
Types of credentials/grants to enable:
|
Anonymous Access Parameter for allowing anonymous access. This parameter is disabled by default. | |
Form Parameter | Description |
config.anonymous
optional Type: string |
An optional string (consumer UUID or username) value that functions as an “anonymous” consumer if authentication fails. If empty (default null), requests that fail authentication will return a |
General Settings Parameters for settings that affect different grants and flows. | |
Form Parameter | Description |
config.preserve_query_args
optional Type: boolean Default value: |
With this parameter, you can preserve request query arguments even when doing authorization code flow.
|
config.refresh_tokens
optional Type: boolean Default value: |
Specifies whether the plugin should try to refresh (soon to be) expired access tokens if the
plugin has a |
config.hide_credentials
required Type: boolean Default value: |
Remove the credentials used for authentication from the request.
|
config.search_user_info
optional Type: boolean Default value: |
Specify whether to use the user info endpoint to get additional claims for consumer mapping, credential mapping, authenticated groups, and upstream and downstream headers.
|
Discovery Parameters for auto-configuring most of the settings and providing the means for key rotation. | |
Form Parameter | Description |
config.issuer
required Type: url |
The discovery endpoint (or the issuer identifier).
|
config.rediscovery_lifetime
optional Type: integer Default value: |
Specifies how long (in seconds) the plugin waits between discovery attempts. Discovery is still triggered on an as-needed basis.
|
Client | |
Form Parameter | Description |
config.client_id
optional Type: array of string elements (the plugin supports multiple clients) |
The client id(s) that the plugin uses when it calls authenticated endpoints on the identity provider. Other settings that are associated with the client are:
Use the same array index when configuring related settings for the client. If keyring database encryption is enabled, this value will be encrypted. This field is referenceable, which means it can be securely stored as a secret in a vault. References must follow a specific format. |
config.client_arg
optional Type: string |
The client to use for this request (the selection is made with a request parameter with the same name).
For example, setting this value to |
Client Authentication Parameters for configuring how the client should authenticate with the identity provider. | |
Form Parameter | Description |
config.client_auth
optional Type: array of string elements (one for each client) Default value: |
The authentication method used by the client (plugin) when calling the endpoints:
|
config.client_secret
optional Type: array of string elements (one for each client) |
The client secret.
If keyring database encryption is enabled, this value will be encrypted. This field is referenceable, which means it can be securely stored as a secret in a vault. References must follow a specific format. |
config.client_jwk
optional Type: array of JWK records (one for each client) Default value: |
The JWK used for the |
config.client_alg
optional Type: array of string elements (one for each client) Default value: |
The algorithm to use for
|
JWT Access Token Authentication Parameters for specifying the location of the bearer token, and introspection options. | |
Form Parameter | Description |
config.bearer_token_param_type
optional Type: array of string elements Default value: |
Where to look for the bearer token:
|
config.bearer_token_cookie_name
optional Type: string |
The name of the cookie in which the bearer token is passed. |
config.introspect_jwt_tokens
optional Type: boolean Default value: |
Specifies whether to introspect the JWT access tokens (can be used to check for revocations). |
Client Credentials Grant Parameters specifying the location of client credentials. | |
Form Parameter | Description |
config.client_credentials_param_type
optional Type: array of string elements Default value: |
Where to look for the client credentials:
|
Password Grant Parameters for where to look for the username and password. | |
Form Parameter | Description |
config.password_param_type
optional Type: array of string elements Default value: |
Where to look for the username and password:
|
Refresh Token Grant Parameters for where to look for the refresh token (rarely used as the refresh tokens are in many cases bound to the client). | |
Form Parameter | Description |
config.refresh_token_param_type
optional Type: array of string elements Default value: |
Where to look for the refresh token:
|
config.refresh_token_param_name
optional Type: string |
The name of the parameter used to pass the refresh token. |
ID Token Parameters for where to look for the id token (rarely sent as part of the request). | |
Form Parameter | Description |
config.id_token_param_type
optional Type: array of string elements Default value: |
Where to look for the id token:
|
config.id_token_param_name
optional Type: string |
The name of the parameter used to pass the id token. |
Consumer Mapping Parameters for mapping external identity provider managed identities to Kong managed ones. | |
Form Parameter | Description |
config.admin_claim
optional Type: string |
The claim used for admin mapping for Kong Manager. Required if mapping IdP claims to Kong Manager admins. |
config.consumer_claim
optional Type: array of string elements |
The claim used for consumer mapping. |
config.consumer_by
optional Type: array of string elements Default value: |
Consumer fields used for mapping:
|
config.by_username_ignore_case
optional Type: boolean Default value: |
If |
config.consumer_optional
optional Type: boolean Default value: |
Do not terminate the request if consumer mapping fails. |
Credential Mapping Parameters for mapping external identity provider managed identities to a Kong credential (virtual in this case). | |
Form Parameter | Description |
config.credential_claim
optional Type: array of string elements Default value: |
The claim used to derive virtual credentials (e.g. to be consumed by the rate-limiting plugin), in case the consumer mapping is not used. |
Issuer Verification | |
Form Parameter | Description |
config.issuers_allowed
optional Type: array of string elements Default value: |
The issuers allowed to be present in the tokens ( |
Authorization | |
Form Parameter | Description |
config.authenticated_groups_claim
optional Type: array of string elements |
The claim that contains authenticated groups. This setting can be used together with ACL plugin, but it also enables IdP managed groups with other applications and integrations (for example, Kong Manager and Dev Portal). The OpenID Connect plugin itself does not do anything other than set the context value. |
config.scopes_required
optional Type: array of string elements Default value: |
The scopes (
|
config.scopes_claim
optional Type: array of string elements Default value: |
The claim that contains the scopes. |
config.audience_required
optional Type: array of string elements |
The audiences (
|
config.audience_claim
optional Type: array of string elements Default value: |
The claim that contains the audience. |
config.groups_required
optional Type: array of string elements |
The groups (
|
config.groups_claim
optional Type: array of string elements Default value: |
The claim that contains the groups. |
config.roles_required
optional Type: array of string elements |
The roles (
|
config.roles_claim
optional Type: array of string elements Default value: |
The claim that contains the roles. |
Claims Verification Parameters for verification rules for standard claims. | |
Form Parameter | Description |
config.verify_claims
optional Type: boolean Default value: |
Verify tokens for standard claims. |
config.leeway
optional Type: integer Default value: |
Allow some leeway on the ttl / expiry verification. |
config.domains
optional Type: array of string elements |
The allowed values for the |
config.max_age
optional Type: integer |
The maximum age (in seconds) compared to the |
config.jwt_session_claim
optional Type: string Default value: |
The claim to match against the JWT session cookie. |
config.jwt_session_cookie
optional Type: string |
The name of the JWT session cookie. |
Signature Verification | |
Form Parameter | Description |
config.verify_signature
optional Type: boolean Default value: |
Verify signature of tokens. |
config.enable_hs_signatures
optional Type: boolean Default value: |
Enable shared secret, for example, HS256, signatures (when disabled they will not be accepted). |
config.ignore_signature
optional Type: array of string elements |
Skip the token signature verification on certain grants:
|
config.extra_jwks_uris
optional Type: array of string elements |
JWKS URIs whose public keys are trusted (in addition to the keys found with the discovery). |
Authorization Code Flow Verification | |
Form Parameter | Description |
config.verify_nonce
optional Type: boolean Default value: |
Verify nonce on authorization code flow. |
Introspection Verification | |
Form Parameter | Description |
config.introspection_check_active
optional Type: boolean Default value: |
Check that the introspection response has an |
Configuration Verification | |
Form Parameter | Description |
config.verify_parameters
optional Type: boolean Default value: |
Verify plugin configuration against discovery. |
Upstream Headers Parameters for the headers for the upstream service request. | |
Form Parameter | Description |
config.upstream_headers_claims
optional Type: array of string elements |
The upstream header claims. |
config.upstream_headers_names
optional Type: array of string elements |
The upstream header names for the claim values. |
config.upstream_access_token_header
optional Type: string Default value: |
The upstream access token header. |
config.upstream_access_token_jwk_header
optional Type: string |
The upstream access token JWK header. |
config.upstream_id_token_header
optional Type: string |
The upstream id token header. |
config.upstream_id_token_jwk_header
optional Type: string |
The upstream id token JWK header. |
config.upstream_refresh_token_header
optional Type: string |
The upstream refresh token header. |
config.upstream_user_info_header
optional Type: string |
The upstream user info header. |
config.upstream_user_info_jwt_header
optional Type: string |
The upstream user info JWT header (in case the user info returns a JWT response). |
config.upstream_introspection_header
optional Type: string |
The upstream introspection header. |
config.upstream_introspection_jwt_header
optional Type: string |
The upstream introspection header (in case the introspection returns a JWT response). |
config.upstream_session_id_header
optional Type: string |
The upstream session id header. |
Downstream Headers Parameters for the headers for the downstream response. | |
Form Parameter | Description |
config.downstream_headers_claims
optional Type: array of string elements |
The downstream header claims. |
config.downstream_headers_names
optional Type: array of string elements |
The downstream header names for the claim values. |
config.downstream_access_token_header
optional Type: string Default value: |
The downstream access token header. |
config.downstream_access_token_jwk_header
optional Type: string |
The downstream access token JWK header. |
config.downstream_id_token_header
optional Type: string |
The downstream id token header. |
config.downstream_id_token_jwk_header
optional Type: string |
The downstream id token JWK header. |
config.downstream_refresh_token_header
optional Type: string |
The downstream refresh token header. |
config.downstream_user_info_header
optional Type: string |
The downstream user info header. |
config.downstream_user_info_jwt_header
optional Type: string |
The downstream user info JWT header (in case the user info returns a JWT response). |
config.downstream_introspection_header
optional Type: string |
The downstream introspection header. |
config.downstream_introspection_jwt_header
optional Type: string |
The downstream introspection header (in case the introspection returns a JWT response). |
config.downstream_session_id_header
optional Type: string |
The downstream session id header. |
Cross-Origin Resource Sharing (CORS) | |
Form Parameter | Description |
config.run_on_preflight
optional Type: boolean Default value: |
Specifies whether to run this plugin on pre-flight ( |
Login Parameters for what action the plugin completes after a successful login. | |
Form Parameter | Description |
config.login_methods
optional Type: array of string elements Default value: |
Enable login functionality with specified grants:
|
config.login_action
optional Type: string Default value: |
What to do after successful login:
|
config.login_tokens
optional Type: array of string elements Default value: |
What tokens to include in
|
config.login_redirect_mode
optional Type: string Default value: |
Where to place
|
config.login_redirect_uri
optional Type: array of urls (one for each client) |
Where to redirect the client when
|
Logout Parameters for triggering logout with the plugin and the actions to take on logout. | |
Form Parameter | Description |
config.logout_query_arg
optional Type: string |
The request query argument that activates the logout. |
config.logout_post_arg
optional Type: string |
The request body argument that activates the logout. |
config.logout_uri_suffix
optional Type: string |
The request URI suffix that activates the logout. |
config.logout_methods
optional Type: array of string elements |
The request methods that can activate the logout:
|
config.logout_revoke
optional Type: boolean Default value: |
Revoke tokens as part of the logout. |
config.logout_revoke_access_token
optional Type: boolean Default value: |
Revoke the access token as part of the logout. |
config.logout_revoke_refresh_token
optional Type: boolean Default value: |
Revoke the refresh token as part of the logout. |
config.logout_redirect_uri
optional Type: array of urls (one for each client) |
Where to redirect the client after the logout. |
Unauthorized Parameters for how to handle unauthorized requests. | |
Form Parameter | Description |
config.unauthorized_redirect_uri
optional Type: array of urls (one for each client) |
Where to redirect the client on unauthorized requests. |
config.unauthorized_error_message
optional Type: string Default value: |
The error message for the unauthorized requests (when not using the redirection). |
Forbidden Parameters for how to handle forbidden requests. | |
Form Parameter | Description |
config.forbidden_redirect_uri
optional Type: array of urls (one for each client) |
Where to redirect the client on forbidden requests. |
config.forbidden_error_message
optional Type: string Default value: |
The error message for the forbidden requests (when not using the redirection). |
config.forbidden_destroy_session
optional Type: boolean Default value: |
Destroy any active session for the forbidden requests. |
Errors Parameters for how to handle unexpected errors. | |
Form Parameter | Description |
config.unexpected_redirect_uri
optional Type: array of urls (one for each client) |
Where to redirect the client when unexpected errors happen with the requests. |
config.display_errors
optional Type: boolean Default value: |
Display errors on failure responses. |
Authorization Cookie Parameters used during authorization code flow for verification and preserving settings. | |
Form Parameter | Description |
config.authorization_cookie_name
optional Type: string Default value: |
The authorization cookie name. |
config.authorization_rolling_timeout
optional Type: integer Default value: |
The authorization cookie rolling timeout in seconds. Specifies how long the authorization cookie can be used until it needs to be renewed. |
config.authorization_cookie_path
optional Type: string Default value: |
The authorization cookie Path flag. |
config.authorization_cookie_domain
optional Type: string |
The authorization cookie Domain flag. |
config.authorization_cookie_same_site
optional Type: string Default value: |
Controls whether a cookie is sent with cross-origin requests, providing some protection against cross-site request forgery attacks:
|
config.authorization_cookie_http_only
optional Type: boolean Default value: |
Forbids JavaScript from accessing the cookie, for example, through the |
config.authorization_cookie_secure
optional Type: boolean Default value: |
Cookie is only sent to the server when a request is made with the https: scheme (except on localhost), and therefore is more resistant to man-in-the-middle attacks. |
Session Cookie Parameters used with the session cookie authentication. | |
Form Parameter | Description |
config.session_cookie_name
optional Type: string Default value: |
The session cookie name. |
config.session_rolling_timeout
optional Type: integer Default value: |
The session cookie rolling timeout in seconds. Specifies how long the session can be used until it needs to be renewed. |
config.session_absolute_timeout
optional Type: integer Default value: |
The session cookie absolute timeout in seconds. Specifies how long the session can be used until it is no longer valid. |
config.session_idling_timeout
optional Type: integer Default value: |
The session cookie idle time in seconds. |
config.session_cookie_path
optional Type: string Default value: |
The session cookie Path flag. |
config.session_cookie_domain
optional Type: string |
The session cookie Domain flag. |
config.session_cookie_same_site
optional Type: string Default value: |
Controls whether a cookie is sent with cross-origin requests, providing some protection against cross-site request forgery attacks:
|
config.session_cookie_http_only
optional Type: boolean Default value: |
Forbids JavaScript from accessing the cookie, for example, through the |
config.session_cookie_secure
optional Type: boolean Default value: |
Cookie is only sent to the server when a request is made with the https: scheme (except on localhost), and therefore is more resistant to man-in-the-middle attacks. |
Session Settings | |
Form Parameter | Description |
config.session_secret
optional Type: string Default value: |
The session secret. If keyring database encryption is enabled, this value will be encrypted. This field is referenceable, which means it can be securely stored as a secret in a vault. References must follow a specific format. |
config.disable_session
optional Type: array of string elements |
Disable issuing the session cookie with the specified grants:
|
config.session_audience
optional Type: string Default value: |
The session audience, which is the intended target application. For example |
config.session_remember
optional Type: boolean Default value: |
Enables or disables persistent sessions. |
config.session_remember_cookie_name
optional Type: string Default value: |
Persistent session cookie name. Use with the |
config.session_remember_rolling_timeout
optional Type: integer Default value: |
The persistent session rolling timeout window, in seconds. |
config.session_remember_absolute_timeout
optional Type: integer Default value: |
The persistent session absolute timeout limit, in seconds. |
config.session_request_headers
optional Type: array of string elements |
List of information to include, as headers, in the request to the upstream.
Accepted values are: |
config.session_response_headers
optional Type: array of string elements |
List of information to include, as headers, in the response to the downstream.
Accepted values are: |
config.session_store_metadata
optional Type: boolean Default value: |
Configures whether or not session metadata should be stored. This metadata includes information about the active sessions for a specific audience belonging to a specific subject. |
config.session_enforce_same_subject
optional Type: boolean Default value: |
When set to |
config.session_hash_subject
optional Type: boolean Default value: |
When set to |
config.session_hash_storage_key
optional Type: boolean Default value: |
When set to |
config.session_storage
optional Type: string Default value: |
The session storage for session data:
|
config.reverify
optional Type: boolean Default value: |
Specifies whether to always verify tokens stored in the session. |
Session Settings for Memcached | |
Form Parameter | Description |
config.session_memcached_prefix
optional Type: string Default value: |
The memcached session key prefix. |
config.session_memcached_socket
optional Type: string |
The memcached unix socket path. |
config.session_memcached_host
optional Type: string Default value: |
The memcached host. |
config.session_memcached_port
optional Type: integer Default value: |
The memcached port. |
Session Settings for Redis | |
Form Parameter | Description |
config.session_redis_prefix
optional Type: string Default value: |
The Redis session key prefix. |
config.session_redis_socket
optional Type: string |
The Redis unix socket path. |
config.session_redis_host
optional Type: string Default value: |
The Redis host |
config.session_redis_port
optional Type: integer Default value: |
The Redis port. |
config.session_redis_username
optional Type: string |
Username to use for Redis connection when the This requires Redis v6.0.0+. The username cannot be set to This field is referenceable, which means it can be securely stored as a secret in a vault. References must follow a specific format. |
config.session_redis_password
optional Type: string Default value: |
Password to use for Redis connection when the If keyring database encryption is enabled, this value will be encrypted. This field is referenceable, which means it can be securely stored as a secret in a vault. References must follow a specific format. |
config.session_redis_connect_timeout
optional Type: integer Default value: |
The Redis connection timeout in milliseconds. |
config.session_redis_read_timeout
optional Type: integer Default value: |
The Redis read timeout in milliseconds. |
config.session_redis_send_timeout
optional Type: integer Default value: |
The Redis send timeout in milliseconds. |
config.session_redis_ssl
optional Type: boolean Default value: |
Use SSL/TLS for Redis connection. |
config.session_redis_ssl_verify
optional Type: boolean Default value: |
Verify Redis server certificate. |
config.session_redis_server_name
optional Type: string |
The SNI used for connecting the Redis server. |
config.session_redis_cluster_nodes
optional Type: array of host records |
The Redis cluster node host. Takes an array of host records, with
either |
config.session_redis_cluster_max_redirections
optional Type: integer |
The Redis cluster maximum redirects. |
Endpoints Parameters normally not needed as the endpoints are discovered. | |
Form Parameter | Description |
config.authorization_endpoint
optional Type: url Default value: |
The authorization endpoint. |
config.token_endpoint
optional Type: url Default value: |
The token endpoint. |
config.introspection_endpoint
optional Type: url Default value: |
The introspection endpoint. |
config.revocation_endpoint
optional Type: url Default value: |
The revocation endpoint. |
config.userinfo_endpoint
optional Type: url Default value: |
The user info endpoint. |
config.end_session_endpoint
optional Type: url Default value: |
The end session endpoint. |
config.token_exchange_endpoint
optional Type: url Default value: |
The token exchange endpoint. |
Endpoint Authentication Parameters normally not needed as the client authentication can be specified for the client. | |
Form Parameter | Description |
config.token_endpoint_auth_method
optional Type: string Default value: |
The token endpoint authentication method:
|
config.introspection_endpoint_auth_method
optional Type: string Default value: |
The introspection endpoint authentication method:
|
config.revocation_endpoint_auth_method
optional Type: string Default value: |
The revocation endpoint authentication method:
|
Discovery Endpoint Arguments | |
Form Parameter | Description |
config.discovery_headers_names
optional Type: array of string elements |
Extra header names passed to the discovery endpoint. |
config.discovery_headers_values
optional Type: array of string elements |
Extra header values passed to the discovery endpoint. |
Authorization Endpoint Arguments | |
Form Parameter | Description |
config.response_mode
optional Type: string Default value: |
The response mode passed to the authorization endpoint:
|
config.response_type
optional Type: array of string elements Default value: |
The response type passed to the authorization endpoint. |
config.scopes
optional Type: array of string elements Default value: |
The scopes passed to the authorization and token endpoints. |
config.audience
optional Type: array of string elements |
The audience passed to the authorization endpoint. |
config.redirect_uri
optional Type: array of urls (one for each client) Default value: |
The redirect URI passed to the authorization and token endpoints. |
config.authorization_query_args_names
optional Type: array of string elements |
Extra query argument names passed to the authorization endpoint. |
config.authorization_query_args_values
optional Type: array of string elements |
Extra query argument values passed to the authorization endpoint. |
config.authorization_query_args_client
optional Type: array of string elements |
Extra query arguments passed from the client to the authorization endpoint. |
Token Endpoint Arguments | |
Form Parameter | Description |
config.token_headers_names
optional Type: array of string elements |
Extra header names passed to the token endpoint. |
config.token_headers_values
optional Type: array of string elements |
Extra header values passed to the token endpoint. |
config.token_headers_client
optional Type: array of string elements |
Extra headers passed from the client to the token endpoint. |
config.token_post_args_names
optional Type: array of string elements |
Extra post argument names passed to the token endpoint. |
config.token_post_args_values
optional Type: array of string elements |
Extra post argument values passed to the token endpoint. |
config.token_post_args_client
optional Type: array of string elements |
Pass extra arguments from the client to the OpenID-Connect plugin. If arguments exist, the client can pass them using:
This parameter can be used with
In this case, the token would take the |
Token Endpoint Response Headers Parameters for an uncommon use case of sending certain token endpoint headers to the downstream client. | |
Form Parameter | Description |
config.token_headers_replay
Type: array of string elements |
The names of token endpoint response headers to forward to the downstream client. |
config.token_headers_prefix
Type: string |
Add a prefix to the token endpoint response headers before forwarding them to the downstream client. |
config.token_headers_grants
Type: array of string elements |
Enable the sending of the token endpoint response headers only with certain grants:
|
Introspection Endpoint Arguments | |
Form Parameter | Description |
config.introspection_hint
optional Type: string Default value: |
Introspection hint parameter value passed to the introspection endpoint. |
config.introspection_accept
optional Type: string Default value: |
The value of
|
config.introspection_headers_names
optional Type: array of string elements |
Extra header names passed to the introspection endpoint. |
config.introspection_headers_values
optional Type: array of string elements |
Extra header values passed to the introspection endpoint. |
config.introspection_headers_client
optional Type: array of string elements |
Extra headers passed from the client to the introspection endpoint. |
config.introspection_post_args_names
optional Type: array of string elements |
Extra post argument names passed to the introspection endpoint. |
config.introspection_post_args_values
optional Type: array of string elements |
Extra post argument values passed to the introspection endpoint. |
config.introspection_post_args_client
optional Type: array of string elements |
Extra post arguments passed from the client to the introspection endpoint. |
User Info Endpoint Arguments | |
Form Parameter | Description |
config.userinfo_accept
optional Type: string Default value: |
The value of
|
config.userinfo_headers_names
optional Type: array of string elements |
Extra header names passed to the user info endpoint. |
config.userinfo_headers_values
optional Type: array of string elements |
Extra header values passed to the user info endpoint. |
config.userinfo_headers_client
optional Type: array of string elements |
Extra headers passed from the client to the user info endpoint. |
config.userinfo_query_args_names
optional Type: array of string elements |
Extra query argument names passed to the user info endpoint. |
config.userinfo_query_args_values
optional Type: array of string elements |
Extra query argument values passed to the user info endpoint. |
config.userinfo_query_args_client
optional Type: array of string elements |
Extra query arguments passed from the client to the user info endpoint. |
HTTP Client Parameters for generic settings for the HTTP client when the plugin needs to interact with the identity provider. | |
Form Parameter | Description |
config.keepalive
optional Type: boolean Default value: |
Use keepalive with the HTTP client. |
config.ssl_verify
optional Type: boolean Default value: |
Verify identity provider server certificate. |
config.timeout
optional Type: integer Default value: |
Network IO timeout in milliseconds. |
config.http_version
optional Type: number Default value: |
The HTTP version used for the requests by this plugin:
|
HTTP Client Proxy Settings Parameters only needed if the HTTP(S) requests to identity provider need to go through a proxy server. | |
Form Parameter | Description |
config.http_proxy
optional Type: url |
The HTTP proxy |
config.http_proxy_authorization
optional Type: string |
The HTTP proxy authorization. |
config.https_proxy
optional Type: url |
The HTTPS proxy |
config.https_proxy_authorization
optional Type: string |
The HTTPS proxy authorization. |
config.no_proxy
optional Type: array of string elements |
Do not use proxy with these hosts. |
Cache TTLs These do not apply to the discovery cache. | |
Form Parameter | Description |
config.cache_ttl
optional Type: integer Default value: |
The default cache ttl in seconds that is used in case the cached object does not specify the expiry. |
config.cache_ttl_max
optional Type: integer |
The maximum cache ttl in seconds (enforced). |
config.cache_ttl_min
optional Type: integer |
The minimum cache ttl in seconds (enforced). |
config.cache_ttl_neg
optional Type: integer Default value: |
The negative cache ttl in seconds. |
config.cache_ttl_resurrect
optional Type: integer Default value: |
The resurrection ttl in seconds. |
Cache Settings for the Endpoints | |
Form Parameter | Description |
config.cache_tokens
optional Type: boolean Default value: |
Cache the token endpoint requests. |
config.cache_tokens_salt
optional Type: string Default value: |
Salt used for generating the cache key that us used for caching the token endpoint requests.
|
config.cache_introspection
optional Type: boolean Default value: |
Cache the introspection endpoint requests. |
config.cache_token_exchange
optional Type: boolean Default value: |
Cache the token exchange endpoint requests. |
config.cache_user_info
optional Type: boolean Default value: |
Cache the user info requests. |
config.resolve_distributed_claims
optional Type: boolean Default value: |
Distributed claims are represented by the |
Records
In the above parameter list, two configuration settings used an array of records as a data type:
config.client_jwk
: array of JWK records (one for each client)config.session_redis_cluster_nodes
: array of host records, either as IP addresses or hostnames, and their ports.
Below are descriptions of the record types.
JWK Record
The JSON Web Key (JWK) record is specified in RFC7517. This record is used with the
config.client_jwk
when using private_key_jwk
client authentication.
Here is an example of JWK record generated by the plugin itself (see: JSON Web Key Set):
{
"kid": "B2FxBJ8G_e61tnZEfaYpaMLjswjNO3dbVEQhR7-i_9s",
"kty": "RSA",
"alg": "RS256",
"use": "sig"
"e": "AQAB",
"n": "…",
"d": "…",
"p": "…",
"q": "…",
"dp": "…",
"dq": "…",
"qi": "…"
}
The JWK private fields (k
, d
, p
, q
, dp
, dq
, qi
, oth
, r
, t
) are referenceable,
which means they can be securely stored as a
secret
in a vault. References must follow a specific format.
Host Record
The Host record used with the config.session_redis_cluster_nodes
is simple.
It contains ip
or host
, and the port
where the port
defaults to 6379
.
Here is an example of Host the record:
{
"ip": "127.0.0.1"
"port": 6379
}
Admin API
The OpenID Connect plugin extends the Kong Admin API with a few endpoints.
Discovery Cache
When configuring the plugin using config.issuer
, the plugin will store the fetched discovery
information to the Kong database, or in the worker memory with DB-less. The discovery cache does
not have an expiry or TTL, and so must be cleared manually using the DELETE
endpoints listed below.
The discovery cache will attempt to be refreshed when a token is presented with required discovery
information that is not already available, based on the config.issuer
value. Once a rediscovery attempt
has been made, a new attempt will not occur until the number of seconds defined in rediscovery_lifetime
has elapsed - this avoids excessive discovery requests to the identity provider.
If a JWT cannot be validated due to missing discovery information and an invalid status code is received from the rediscovery request (for example, non-2xx), the plugin will attempt to validate the JWT by falling back to any sufficient discovery information that is still in the discovery cache.
Discovery Cache Object
{
"id": "<uuid>",
"issuer": "<config.issuer>"
"created_at": <timestamp>,
"configuration": {
<discovery>
},
"keys": [
<keys>
]
}
List All Discovery Cache Objects
Response
HTTP 200 OK
{
"data": [{
"id": "<uuid>",
"issuer": "<config.issuer>"
"created_at": <timestamp>,
"configuration": {
<discovery>
},
"keys": [
<keys>
]
}],
"next": null
}
Retrieve Discovery Cache Object
Attributes | Description |
---|---|
issuer or id required |
The unique identifier or the value of config.issuer |
Response
HTTP 200 OK
{
"id": "<uuid>",
"issuer": "<config.issuer>"
"created_at": <timestamp>,
"configuration": {
<discovery>
},
"keys": [
<keys>
]
}
Delete All Discovery Cache Objects
Response
HTTP 204 No Content
Note: The automatically generated session secret (that can be overridden with the
config.session_secret
) is stored with the discovery cache objects. Deleting discovery cache objects will invalidate all the sessions created with the associated secret.
Delete Discovery Cache Object
Attributes | Description |
---|---|
issuer or id required |
The unique identifier or the value of config.issuer |
Response
HTTP 204 No Content
JSON Web Key Set
When the OpenID Connect client (the plugin) is set to communicate with the identity provider endpoints
using private_key_jwt
, the plugin needs to use public key cryptography. Thus, the plugin needs
to generate the needed keys. Identity provider on the other hand has to verify that the assertions
used for the client authentication.
The plugin will automatically generate the key pairs for the different algorithms. It will also publish the public keys with the admin api where the identity provider could fetch them.
{
"keys": [{
<keys>
}]
}
Retrieve JWKS
This endpoint will return a standard JWK Set document with the private keys stripped out.
Response
HTTP 200 OK
{
"keys": [{
<keys>
}]
}
Rotate JWKS
Deleting JWKS will also cause auto-generation of a new JWK set, so
DELETE
will actually cause a key rotation.
Response
HTTP 204 No Content
Preparations
The OpenID Connect plugin relies in most cases on a third party identity provider. In this section, we explain configuration of Keycloak and Kong.
All the *.test
domains in the following examples point to the localhost
(127.0.0.1
and/or ::1
).
Keycloak Configuration
We use Keycloak as the identity provider in the following examples, but the steps will be similar in other standard identity providers. If you encounter difficulties during this phase, please refer to the Keycloak documentation.
- Create a confidential client
kong
withprivate_key_jwt
authentication and configure Keycloak to download the public keys from the OpenID Connect Plugin JWKS endpoint:
- Create another confidential client
service
withclient_secret_basic
authentication. For this client, Keycloak will auto-generate a secret similar to the following:cf4c655a-0622-4ce6-a0de-d3353ef0b714
. Enable the client credentials grant for the client:
- Create a verified user with the name:
john
and the non-temporary password:doe
that can be used with the password grant:
Alternatively you can download the exported Keycloak configuration, and use it to configure the Keycloak. Please refer to Keycloak import documentation for more information.
You need to modify Keycloak standalone.xml
configuration file, and change the socket binding from:
<socket-binding name="https" port="${jboss.https.port:8443}"/>
to
<socket-binding name="https" port="${jboss.https.port:8440}"/>
The Keycloak default https
port conflicts with the default Kong TLS proxy port,
and that can be a problem if both are started on the same host.
Kong Configuration
Create a Service
http -f put :8001/services/openid-connect url=http://httpbin.org/anything
HTTP/1.1 200 OK
{
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd",
"name": "openid-connect",
"protocol": "http",
"host": "httpbin.org",
"port": 80,
"path": "/anything"
}
Create a Route
http -f put :8001/services/openid-connect/routes/openid-connect paths=/
HTTP/1.1 200 OK
{
"id": "ac1e86bd-4bce-4544-9b30-746667aaa74a",
"name": "openid-connect",
"paths": [ "/" ]
}
Create a Plugin
You may execute this before patching the plugin (as seen on following examples) to reset the plugin configuration.
http -f put :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
name=openid-connect \
service.name=openid-connect \
config.issuer=http://keycloak.test:8080/auth/realms/master \
config.client_id=kong \
config.client_auth=private_key_jwt
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"issuer": "http://keycloak.test:8080/auth/realms/master",
"client_id": [ "kong" ],
"client_auth": [ "private_key_jwt" ]
}
}
Also, check the discovery cache:
http :8001/openid-connect/issuers
. It should contain Keycloak OpenID Connect discovery document, and the keys.
Summary
At this point we have:
- Created a Service
- Routed traffic to the service
- Enabled OpenID Connect plugin on the service
The following sections will guide you through the process of enabling the OpenID Connect plugin for specific grants or flows.
Authentication
Before you proceed, check that you have done the preparations.
We use HTTPie to execute the examples. The output is stripped for a better readability. httpbin.org is used as an upstream service.
Using Admin API is convenient when testing the plugin, but similar configs can be done in declarative format as well.
When this plugin is configured with multiple grants/flows there is a hard-coded search order for the credentials:
- Session Authentication
- JWT Access Token Authentication
- Kong OAuth Token Authentication
- Introspection Authentication
- User Info Authentication
- Refresh Token Grant
- Password Grant
- Client Credentials Grant
- Authorization Code Flow
Once credentials are found, the plugin will stop searching further. Multiple grants may
share the same credentials. For example, both the password and client credentials grants can use
basic access authentication through the Authorization
header.
The choices made in the examples below are solely aimed at simplicity. Because
httpbin.org
is used as an upstream service, it is highly recommended that you do not run these usage examples with a production identity provider as there is a great chance of leaking information. Also the examples below use the plain HTTP protocol that you should never use in production.
Authorization Code Flow
The authorization code flow is the three-legged OAuth/OpenID Connect flow. The sequence diagram below describes the participants and their interactions for this usage scenario, including the use of session cookies:
Patch the Plugin
Let’s patch the plugin that we created in the Kong configuration step:
- We want to only use the authorization code flow and the session authentication.
- We want to set the response mode to
form_post
so that authorization codes won’t get logged to the access logs. - We want to preserve the original request query arguments over the authorization code flow redirection.
- We want to redirect the client to original request url after the authorization code flow so that
the
POST
request (because ofform_post
) is turned to theGET
request, and the browser address bar is updated with the original request query arguments. - We don’t want to include any tokens in the browser address bar.
Reset the plugin configuration before patching.
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.auth_methods=authorization_code \
config.auth_methods=session \
config.response_mode=form_post \
config.preserve_query_args=true \
config.login_action=redirect \
config.login_tokens=
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [
"authorization_code",
"session"
],
"login_action": "redirect",
"preserve_query_args": true,
"login_tokens": null
}
}
Test the Authorization Code Flow
- Open the Service Page with some query arguments:
open http://service.test:8000/?hello=world
- See that the browser is redirected to the Keycloak login page:
You may examine the query arguments passed to Keycloak with the browser developer tools.
- And finally you will be presented a response from httpbin.org:
- Done.
It looks rather simple from the user point of view, but what really happened is described in the diagram above.
Password Grant
Password grant is a legacy authentication grant. This is a less secure way of authenticating end users than the authorization code flow, because, for example, the passwords are shared with third parties. The image below illustrates the grant:
Patch the Plugin
Let’s patch the plugin that we created in the Kong configuration step:
- We want to only use the password grant.
- We want to search credentials for password grant from the headers only.
Reset the plugin configuration before patching.
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.auth_methods=password \
config.password_param_type=header
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [ "password" ],
"password_param_type": [ "header" ]
}
}
Test the Password Grant
- Request the service with basic authentication credentials created in the Keycloak configuration step:
http -v -a john:doe :8000
GET / HTTP/1.1 Authorization: Basic BEkg3bHT0ERXFmKr1qelBQYrLBeHb5Hr
HTTP/1.1 200 OK
{ "headers": { "Authorization": "Bearer <access-token>" }, "method": "GET" }
- Done.
If you make another request using the same credentials, you should see that Kong adds less latency to the request as it has cached the token endpoint call to Keycloak.
Client Credentials Grant
The client credentials grant is very similar to the password grant. The most important difference in the Kong OpenID Connect plugin is that the plugin itself does not try to authenticate. It just forwards the credentials passed by the client to the identity server’s token endpoint. The client credentials grant is visualized below:
Patch the Plugin
Let’s patch the plugin that we created in the Kong configuration step:
- We want to only use the client credentials grant.
- We want to search credentials for client credentials from the headers only.
Reset the plugin configuration before patching.
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.auth_methods=client_credentials \
config.client_credentials_param_type=header
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [ "client_credentials" ],
"client_credentials_param_type": [ "header" ]
}
}
Test the Client Credentials Grant
- Request the service with client credentials created in the Keycloak configuration step:
http -v -a service:cf4c655a-0622-4ce6-a0de-d3353ef0b714 :8000
GET / HTTP/1.1 Authorization: Basic c2VydmljZTpjZjRjNjU1YS0wNjIyLTRjZTYtYTBkZS1kMzM1M2VmMGI3MTQ=
HTTP/1.1 200 OK
{ "headers": { "Authorization": "Bearer <access-token>" }, "method": "GET" }
- Done.
If you make another request using the same credentials, you should see that Kong adds less latency to the request as it has cached the token endpoint call to Keycloak.
Refresh Token Grant
The refresh token grant can be used when the client has a refresh token available. There is a caveat with this: identity providers in general only allow refresh token grant to be executed with the same client that originally got the refresh token, and if there is a mismatch, it may not work. The mismatch is likely when Kong OpenID Connect is configured to use one client, and the refresh token is retrieved with another. The grant itself is very similar to password grant and client credentials grant:
Patch the Plugin
Let’s patch the plugin that we created in the Kong configuration step:
- We want to only use the refresh token grant, but we also enable the password grant for demoing purposes
- We want to search the refresh token for the refresh token grant from the headers only.
- We want to pass refresh token from the client in
Refresh-Token
header. - We want to pass refresh token to upstream in
Refresh-Token
header.
Reset the plugin configuration before patching.
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.refresh_token_param_name=refresh_token \
config.refresh_token_param_type=header \
config.auth_methods=refresh_token \
config.auth_methods=password \
config.upstream_refresh_token_header=refresh_token
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [
"refresh_token",
"password"
],
"refresh_token_param_name": "refresh_token",
"refresh_token_param_type": [ "header" ],
"upstream_refresh_token_header": "refresh_token"
}
}
The config.auth_methods
and config.upstream_refresh_token_header
are only enabled for demoing purposes so that we can get a refresh token with:
http -a john:doe :8000 | jq -r '.headers."Refresh-Token"'
Output:
<refresh-token>
We can use the output in Refresh-Token
header.
Test the Refresh Token Grant
- Request the service with a bearer token:
http -v :8000 Refresh-Token:$(http -a john:doe :8000 | \ jq -r '.headers."Refresh-Token"')
or
http -v :8000 Refresh-Token:"<refresh-token>"
GET / HTTP/1.1 Refresh-Token: <refresh-token>
HTTP/1.1 200 OK
{ "headers": { "Authorization": "Bearer <access-token>", "Refresh-Token": "<refresh-token>" }, "method": "GET" }
- Done.
JWT Access Token Authentication
For legacy reasons, the stateless JWT Access Token
authentication is named bearer
with the Kong
OpenID Connect plugin (see: config.auth_methods
). Stateless authentication basically means
the signature verification using the identity provider published public keys and the standard
claims’ verification (such as exp
(or expiry)). The client may have received the token directly
from the identity provider or by other means. It is simple:
Patch the Plugin
Let’s patch the plugin that we created in the Kong configuration step:
- We want to only use the bearer authentication, but we also enable the password grant for demoing purposes
- We want to search the bearer token for the bearer authentication from the headers only.
Reset the plugin configuration before patching.
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.bearer_token_param_type=header \
config.auth_methods=bearer \
config.auth_methods=password # only enabled for demoing purposes
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [
"bearer",
"password"
],
"bearer_token_param_type": [ "header" ]
}
}
The password grant is enabled so that we can get a JWT access token that we can use to show how the JWT access token authentication works. That is: we need a token. One way to get a JWT access token is to issue the following call (we use jq to filter the response):
http -a john:doe :8000 | jq -r .headers.Authorization
Output:
Bearer <access-token>
We can use the output in Authorization
header.
Test the JWT Access Token Authentication
- Request the service with a bearer token:
http -v :8000 Authorization:"$(http -a john:doe :8000 | \ jq -r .headers.Authorization)"
or
http -v :8000 Authorization:"Bearer <access-token>"
GET / HTTP/1.1 Authorization: Bearer <access-token>
HTTP/1.1 200 OK
{ "headers": { "Authorization": "Bearer <access-token>" }, "method": "GET" }
- Done.
Introspection Authentication
As with JWT Access Token Authentication), the introspection authentication relies on a bearer token that the client has already gotten from somewhere. The difference to stateless JWT authentication is that the plugin needs to call the introspection endpoint of the identity provider to find out whether the token is valid and active. This makes it possible to issue opaque tokens to the clients.
Patch the Plugin
Let’s patch the plugin that we created in the Kong configuration step:
- We want to only use the introspection authentication, but we also enable the password grant for demoing purposes
- We want to search the bearer token for the introspection from the headers only.
Reset the plugin configuration before patching.
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.bearer_token_param_type=header \
config.auth_methods=introspection \
config.auth_methods=password # only enabled for demoing purposes
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [
"introspection",
"password"
],
"bearer_token_param_type": [ "header" ]
}
}
Test the Introspection Authentication
- Request the service with a bearer token:
http -v :8000 Authorization:"$(http -a john:doe :8000 | \ jq -r .headers.Authorization)"
or
http -v :8000 Authorization:"Bearer <access-token>"
GET / HTTP/1.1 Authorization: Bearer <access-token>
HTTP/1.1 200 OK
{ "headers": { "Authorization": "Bearer <access-token>" }, "method": "GET" }
- Done.
User Info Authentication
The user info authentication uses OpenID Connect standard user info endpoint to verify the access token. In most cases it is preferable to use Introspection Authentication as that is meant for retrieving information from the token itself, whereas the user info endpoint is meant for retrieving information about the user for whom the token was given. The sequence diagram below looks almost identical to introspection authentication:
Patch the Plugin
Let’s patch the plugin that we created in the Kong configuration step:
- We want to only use the user info authentication, but we also enable the password grant for demoing purposes
- We want to search the bearer token for the user info from the headers only.
Reset the plugin configuration before patching.
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.bearer_token_param_type=header \
config.auth_methods=userinfo \
config.auth_methods=password # only enabled for demoing purposes
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [
"userinfo",
"password"
],
"bearer_token_param_type": [ "header" ]
}
}
Test the User Info Authentication
- Request the service with a bearer token:
http -v :8000 Authorization:"$(http -a john:doe :8000 | \ jq -r .headers.Authorization)"
or
http -v :8000 Authorization:"Bearer <access-token>"
GET / HTTP/1.1 Authorization: Bearer <access-token>
HTTP/1.1 200 OK
{ "headers": { "Authorization": "Bearer <access-token>" }, "method": "GET" }
- Done.
Kong OAuth Token Authentication
The OpenID Connect plugin can also verify the tokens issued by Kong OAuth 2.0 Plugin. This is very similar to third party identity provider issued JWT access token authentication or introspection authentication:
Prepare Kong OAuth Application
- Create a Consumer:
http -f put :8001/consumers/jane
- Create Kong OAuth Application for the consumer:
http -f put :8001/consumers/jane/oauth2/client \ name=demo \ client_secret=secret \ hash_secret=true
- Create a Route:
http -f put :8001/routes/auth paths=/auth
- Apply OAuth plugin to the Route:
http -f put :8001/plugins/7cdeaa2d-5faf-416d-8df5-533d1e4cd2c4 \ name=oauth2 \ route.name=auth \ config.global_credentials=true \ config.enable_client_credentials=true
- Test the token endpoint:
https -f --verify no post :8443/auth/oauth2/token \ client_id=client \ client_secret=secret \ grant_type=client_credentials
HTTP/1.1 200 OK
{ "access_token": "<access-token>", "expires_in": 7200, "token_type": "bearer" }
At this point we should be able to retrieve a new access token with:
https -f --verify no post :8443/auth/oauth2/token \
client_id=client \
client_secret=secret \
grant_type=client_credentials | \
jq -r .access_token
Output:
<access-token>
Patch the Plugin
Let’s patch the plugin that we created in the Kong configuration step:
- We want to only use the Kong OAuth authentication
- We want to search the bearer token for the Kong OAuth authentication from the headers only.
Reset the plugin configuration before patching.
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.auth_methods=kong_oauth2 \
config.bearer_token_param_type=header
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [ "kong_oauth2" ],
"bearer_token_param_type": [ "header" ]
}
}
Test the Kong OAuth Token Authentication
- Request the service with Kong OAuth token:
http -v :8000 Authorization:"Bearer $(https -f --verify no \ post :8443/auth/oauth2/token \ client_id=client \ client_secret=secret \ grant_type=client_credentials | \ jq -r .access_token)"
or
http -v :8000 Authorization:"Bearer <access-token>"
GET / HTTP/1.1 Authorization: Bearer <access-token>
HTTP/1.1 200 OK
{ "headers": { "Authorization": "Bearer <access-token>", "X-Consumer-Id": "<consumer-id>", "X-Consumer-Username": "jane" }, "method": "GET" }
- Done.
Session Authentication
Kong OpenID Connect plugin can issue a session cookie that can be used for further session authentication. To make OpenID Connect issue a session cookie, you need to first authenticate with one of the other grant / flows described above. In authorization code flow we already demonstrated session authentication when we used the redirect login action. The session authentication is described below:
Patch the Plugin
Let’s patch the plugin that we created in the Kong configuration step:
- We want to only use the session authentication, but we also enable the password grant for demoing purposes
Reset the plugin configuration before patching.
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.auth_methods=session \
config.auth_methods=password # only enabled for demoing purposes
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [
"session",
"password"
]
}
}
Test the Session Authentication
- Request the service with basic authentication credentials (created in the Keycloak configuration step),
and store the session:
http -v -a john:doe --session=john :8000
GET / HTTP/1.1 Authorization: Basic BEkg3bHT0ERXFmKr1qelBQYrLBeHb5Hr
HTTP/1.1 200 OK Set-Cookie: session=<session-cookie>; Path=/; SameSite=Lax; HttpOnly
{ "headers": { "Authorization": "Bearer <access-token>" }, "method": "GET" }
- Make request with a session cookie (stored above):
http -v --session=john :8000
GET / HTTP/1.1 Cookie: session=<session-cookie>
HTTP/1.1 200 OK
{ "headers": { "Authorization": "Bearer <access-token>" }, "method": "GET" }
- Done.
If you want to disable session creation with some grants, you can use the
config.disable_session
.
Authorization
Before you proceed, check that you have completed the preparations.
The OpenID Connect plugin has several features to do coarse grained authorization:
Claims Based Authorization
The following options can be configured to manage claims verification during authorization:
config.scopes_claim
andconfig.scopes_required
config.audience_claim
andconfig.audience_required
config.groups_claim
andconfig.groups_required
config.roles_claim
andconfig.roles_required
For example, the first configuration option, config.scopes_claim
, points to a source, from which the value is
retrieved and checked against the value of the second configuration option: config.scopes_required
.
Let’s take a look at a JWT access token:
Reset the plugin configuration before patching.
- Patch the plugin to enable the password grant:
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \ config.auth_methods=password
- Retrieve the content of an access token:
http -a john:doe :8000 | jq -r .headers.Authorization
HTTP/1.1 200 OK
Bearer <access-token>
- The signed JWT
<access-token>
(JWS) comes with three parts separated with a dot.
:<header>.<payload>.<signature>
(a JWS compact serialization format) - We are interested with the
<payload>
, and you should have something similar to:eyJleHAiOjE2MjI1NTY3MTMsImF1ZCI6ImFjY291bnQiLCJ0eXAiOiJCZWFyZXIiLC JzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwicHJlZmVycmVkX3VzZXJuYW1l Ijoiam9obiIsImdpdmVuX25hbWUiOiJKb2huIiwiZmFtaWx5X25hbWUiOiJEb2UifQ
That can be base64 url decoded to the following
JSON
:{ "exp": 1622556713, "aud": "account", "typ": "Bearer", "scope": "openid email profile", "preferred_username": "john", "given_name": "John", "family_name": "Doe" }
This payload may contain arbitrary claims, such as user roles and groups, but as we didn’t configure them in Keycloak, let’s just use the claims that we have. In this case we want to authorize against the values in
scope
claim.
Let’s patch the plugin that we created in the Kong configuration step:
- We want to only use the password grant for demonstration purposes.
- We require the value
openid
andemail
to be present inscope
claim of the access token.
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.auth_methods=password \
config.scopes_claim=scope \
config.scopes_required="openid email"
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [ "password" ],
"scopes_claim": [ "scope" ],
"scopes_required": [ "openid email" ]
}
}
Now let’s see if we can still access the service:
http -v -a john:doe :8000
GET / HTTP/1.1
Authorization: Basic BEkg3bHT0ERXFmKr1qelBQYrLBeHb5Hr
HTTP/1.1 200 OK
{
"headers": {
"Authorization": "Bearer <access-token>"
},
"method": "GET"
}
Works as expected, but let’s try to add another authorization:
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.audience_claim=aud \
config.audience_required=httpbin
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [ "password" ],
"audience_claim": [ "scope" ],
"audience_required": [ "httpbin" ]
}
}
As we know, the access token has "aud": "account"
, and that does not match with "httpbin"
, so
the request should now be forbidden:
http -v -a john:doe :8000
HTTP/1.1 403 Forbidden
{
"message": "Forbidden"
}
A few words about config.scopes_claim
and config.scopes_required
(and the similar configuration options).
You may have noticed that config.scopes_claim
is an array of string elements. Why? It is used to traverse
the JSON when looking up a claim, take for example this imaginary payload:
{
"user": {
"name": "john",
"groups": [
"employee",
"marketing"
]
}
}
In this case you would probably want to use config.groups_claim
to point to groups
claim, but that claim
is not a top-level claim, so you need to traverse there:
- Find the
user
claim. - Inside the
user
claim, find thegroups
claim, and read its value:
{
"config": {
"groups_claim": [
"user",
"groups"
]
}
}
or
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.groups_claim=user \
config.groups_claim=groups
The value of a claim can be the following:
- a space separated string (such as
scope
claim usually is) - an JSON array of strings (such as the imaginary
groups
claim above) - a simple value, such as a
string
What about the config.groups_required
then? That is also an array?
That is correct, the required checks are arrays to allow logical and
/or
type of checks:
and
: use a space separated valuesor
: specify value(s) in separate array indices
For example:
{
"config": {
"groups_required": [
"employee marketing",
"super-admins"
]
}
}
or
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.groups_required="employee marketing" \
config.groups_required="super-admins"
The above means that a claim has to have:
employee
andmarketing
values in it, ORsuper-admins
value in it
ACL Plugin Authorization
The plugin can also be integrated with Kong ACL Plugin that provides access control functionality in form of allow and deny lists.
Please read the claims verification section for a basic information, as a lot of that applies here too.
Let’s first configure the OpenID Connect plugin for integration with the ACL plugin (please remove other authorization if enabled):
Reset the plugin configuration before patching.
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.auth_methods=password \
config.authenticated_groups_claim=scope
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [ "password" ],
"authorized_groups_claim": [ "scope" ]
}
}
Before we apply the ACL plugin, let’s try it once:
http -v -a john:doe :8000
HTTP/1.1 200 OK
{
"headers": {
"X-Authenticated-Groups": "openid, email, profile",
}
}
Interesting, the X-Authenticated-Groups
header was injected in a request.
This means that we are all good to add the ACL plugin:
http -f put :8001/plugins/b238b64a-8520-4bbb-b5ff-2972165cf3a2 \
name=acl \
service.name=openid-connect \
config.allow=openid
Let’s test it again:
http -v -a john:doe :8000
HTTP/1.1 200 OK
Let’s make it forbidden by changing it to a deny-list:
http -f patch :8001/plugins/b238b64a-8520-4bbb-b5ff-2972165cf3a2 \
config.allow= \
config.deny=profile
And try again:
http -v -a john:doe :8000
HTTP/1.1 403 Forbidden
{
"message": "You cannot consume this service"
}
Consumer Authorization
The third option for authorization is to use Kong consumers and dynamically map from a claim value to a Kong consumer. This means that we restrict the access to only those that do have a matching Kong consumer. Kong consumers can have ACL groups attached to them and be further authorized with the Kong ACL Plugin.
As a remainder our token payload looks like this:
{
"exp": 1622556713,
"aud": "account",
"typ": "Bearer",
"scope": "openid email profile",
"preferred_username": "john",
"given_name": "John",
"family_name": "Doe"
}
Out of these the preferred_username
claim looks promising for consumer mapping.
Let’s patch the plugin:
Reset the plugin configuration before patching.
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.auth_methods=password \
config.consumer_claim=preferred_username \
config.consumer_by=username
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [ "password" ],
"consumer_claim": [ "preferred_username" ],
"consumer_by": [ "username" ]
}
}
Before we proceed, let’s make sure we don’t have consumer john
:
http delete :8001/consumers/john
HTTP/1.1 204 No Content
Let’s try to access the service without a matching consumer:
http -a john:doe :8000
HTTP/1.1 403 Forbidden
{
"message": "Forbidden"
}
Now, let’s add the consumer:
http -f put :8001/consumers/john
HTTP/1.1 200 OK
{
"id": "<consumer-id>",
"username": "john"
}
Let’s try to access the service again:
http -a john:doe :8000
HTTP/1.1 200 OK
{
"headers": {
"Authorization": "Bearer <access-token>",
"X-Consumer-Id": "<consumer-id>",
"X-Consumer-Username": "john"
},
"method": "GET"
}
Nice, as you can see the plugin even added the X-Consumer-Id
and X-Consumer-Username
as request headers.
It is possible to make consumer mapping optional and non-authorizing by setting the
config.consumer_optional=true
.
Headers
Before you proceed, check that you have completed the preparations.
The OpenID Connect plugin can pass claim values, tokens, JWKs, and the session identifier to the upstream service
in request headers, and to the downstream client in response headers. By default, the plugin passes an access token
in Authorization: Bearer <access-token>
header to the upstream service (this can be controlled with
config.upstream_access_token_header
). The claim values can be taken from:
- an access token,
- an id token,
- an introspection response, or
- a user info response
Let’s take a look for an access token payload:
{
"exp": 1622556713,
"aud": "account",
"typ": "Bearer",
"scope": "openid email profile",
"preferred_username": "john",
"given_name": "John",
"family_name": "Doe"
}
To pass the preferred_username
claim’s value john
to the upstream with an Authenticated-User
header,
we need to patch our plugin:
Reset the plugin configuration before patching.
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.auth_methods=password \
config.upstream_headers_claims=preferred_username \
config.upstream_headers_names=authenticated_user
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [ "password" ],
"upstream_headers_claims": [ "preferred_username" ],
"upstream_headers_names": [ "authenticated_user" ]
}
}
Let’s see if it had any effect:
http -a john:doe :8000
HTTP/1.1 200 OK
{
"headers": {
"Authorization": "Bearer <access-token>",
"Authenticated-User": "john"
},
"method": "GET"
}
See the configuration parameters for other options.
Logout
The logout functionality is mostly useful together with session authentication that is mostly useful with authorization code flow.
As part of the logout, the OpenID Connect plugin implements several features:
- session invalidation
- token revocation
- relying party (RP) initiated logout
Let’s patch the OpenID Connect plugin to provide the logout functionality:
Reset the plugin configuration before patching.
http -f patch :8001/plugins/5f35b796-ced6-4c00-9b2a-90eef745f4f9 \
config.auth_methods=session \
config.auth_methods=password \
config.logout_uri_suffix=/logout \
config.logout_methods=POST \
config.logout_revoke=true
HTTP/1.1 200 OK
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"auth_methods": [ "password", "session" ],
"logout_uri_suffix": "/logout",
"logout_methods": [ "POST" ],
"logout_revoke": true
}
}
Login and establish a session:
http -a john:doe --session=john :8000
HTTP/1.1 200 OK
Test that session authentication works:
http --session=john :8000
HTTP/1.1 200 OK
Logout, and follow the redirect:
http --session=john --follow -a john: post :8000/logout
HTTP/1.1 200 OK
We needed to pass
-a john:
as there seems to be a feature withHTTPie
that makes it to store the original basic authentication credentials in a session - not just the session cookies.
At this point the client has logged out from both Kong and the identity provider (Keycloak).
Check that the session is really gone:
http --session=john :8000
HTTP/1.1 401 Unauthorized
Debugging
The OpenID Connect plugin is complex, integrating with third-party identity providers can present challenges. If you have issues with the plugin or integration, try the following:
- Set Kong log level to
debug
, and check the Kongerror.log
(you can filter it withopenid-connect
)KONG_LOG_LEVEL=debug kong restart
- Set the Kong OpenID Connect plugin to display errors:
{ "config": { "display_errors": true } }
- Disable the Kong OpenID Connect plugin verifications and see if you get further, just for debugging purposes:
{ "config": { "verify_nonce": false, "verify_claims": false, "verify_signature": false } }
- See what kind of tokens the Kong OpenID Connect plugin gets:
{ "config": { "login_action": "response", "login_tokens": [ "tokens" ], "login_methods": [ "password", "client_credentials", "authorization_code", "bearer", "introspection", "userinfo", "kong_oauth2", "refresh_token", "session" ] } }
With session related issues, you can try to store the session data in Redis
or memcache
as that will make the session
cookie much smaller. It is rather common that big cookies do cause issues. You can also enable session
compression.
Also try to eliminate indirection as that makes it easier to find out where the problem is. By indirection, we mean other gateways, load balancers, NATs, and such in front of Kong. If there is such, you may look at using:
- port maps
X-Forwarded-*
headers
Changelog
Kong Gateway 3.2.x
- The plugin has been updated to use version 4.0.0 of the
lua-resty-session
library which introduced several new features such as the possibility to specify audiences. The following configuration parameters have been affected:
Added:
session_audience
session_remember
session_remember_cookie_name
session_remember_rolling_timeout
session_remember_absolute_timeout
session_absolute_timeout
session_request_headers
session_response_headers
session_store_metadata
session_enforce_same_subject
session_hash_subject
session_hash_storage_key
Renamed:
authorization_cookie_lifetime
toauthorization_rolling_timeout
authorization_cookie_samesite
toauthorization_cookie_same_site
authorization_cookie_httponly
toauthorization_cookie_http_only
session_cookie_lifetime
tosession_rolling_timeout
session_cookie_idletime
tosession_idling_timeout
session_cookie_samesite
tosession_cookie_same_site
session_cookie_httponly
tosession_cookie_http_only
session_memcache_prefix
tosession_memcached_prefix
session_memcache_socket
tosession_memcached_socket
session_memcache_host
tosession_memcached_host
session_memcache_port
tosession_memcached_port
session_redis_cluster_maxredirections
tosession_redis_cluster_max_redirections
Removed:
session_cookie_renew
session_cookie_maxsize
session_strategy
session_compressor
Kong Gateway 3.0.x
- The deprecated
session_redis_auth
field has been removed from the plugin.
Kong Gateway 2.8.x
-
Added the
session_redis_username
andsession_redis_password
configuration parameters.These parameters replace the
session_redis_auth
field, which is now deprecated and planned to be removed in 3.x.x. -
Added the
resolve_distributed_claims
configuration parameter. -
The
client_id
,client_secret
,session_secret
,session_redis_username
, andsession_redis_password
configuration fields are now marked as referenceable, which means they can be securely stored as secrets in a vault. References must follow a specific format.
Kong Gateway 2.7.x
-
Starting with Kong Gateway 2.7.0.0, if keyring encryption is enabled, the
config.client_id
,config.client_secret
,config.session_auth
, andconfig.session_redis_auth
parameter values will be encrypted.Additionally, the
d
,p
,q
,dp
,dq
,qi
,oth
,r
,t
, andk
fields insideopenid_connect_jwks.previous[...].
andopenid_connect_jwks.keys[...]
will be marked as encrypted.There’s a bug in Kong Gateway that prevents keyring encryption from working on deeply nested fields, so the
encrypted=true
setting does not currently have any effect on the nested fields in this plugin. -
The plugin now allows Redis cluster nodes to be specified by hostname through the
session_redis_cluster_nodes
field, which is helpful if the cluster IPs are not static.
Kong Gateway 2.6.x
- The OpenID Connect plugin can now handle JWT responses from a
userinfo
endpoint. - Added support for JWE introspection.
- Added a new parameter,
by_username_ignore_case
, which allowsconsumer_by
username values to be matched case-insensitive with identity provider (IdP) claims.