Overview
Demonstrating Proof-of-Possession (DPoP) is an alternative technique to the Mutual TLS certificate-bound access tokens. Unlike its alternative, which binds the token to the mTLS client certificate, it binds the token to a JSON Web Key (JWK) provided by the client.
sequenceDiagram autonumber participant client as Client
(e.g. mobile app) participant kong as API Gateway
(Kong) participant upstream as Upstream
(backend service,
e.g. httpbin) participant idp as Authentication Server
(e.g. Keycloak) activate client client->>client: generate key pair client->>idp: POST /oauth2/token
DPoP:$PROOF deactivate client activate idp idp-->>client: DPoP bound access token ($AT) activate client deactivate idp client->>kong: GET https://example.com/resource
Authorization: DPoP $AT
DPoP: $PROOF activate kong deactivate client kong->>kong: validate $AT and $PROOF kong->>upstream: proxied request
GET https://example.com/resource
Authorization: Berear $AT deactivate kong activate upstream upstream-->>kong: upstream response deactivate upstream activate kong kong-->>client: response deactivate kong
You can use the Demonstrating Proof-of-Possession option without Mutual TLS, and even with plain HTTP, although HTTPS is recommended for enhanced security.
When verification of the DPoP proof is enabled, Kong removes the DPoP
header and changes the token type from dpop
to bearer
.
This effectively downgrades the request to use a conventional bearer token.
This allows an OAuth2 upstream without DPoP support to work with the DPoP token without losing the protection of the key-binding mechanism.
It’s a prerequisite that the auth server is configured to enable the DPoP for you to enable it with Kong.
If you are setting up Demonstrating Proof-of-Possession in Keycloak, refer to the Keycloak configuration section in the prerequisites. For other authentication servers, please consult their specific documentation to configure this functionality.
Many instructions in the other how-to guides for OpenID Connect support validation of access tokens using Demonstrating Proof-of-Possession.
Enabling the proof_of_possession_dpop
configuration option in the plugin helps to ensure that the supplied access token
is bound to the client by verifying its association with the JWT provided in the request.
DPoP is compatible with the following authentication methods:
- JWT Access Token Authentication
- Introspection Authentication
-
Session Authentication is only compatible with DPoP when used along with one of the other supported authentication methods:
- If multiple
openid-connect
plugins are configured with thesession
authentication method, we strongly recommend configuring different values ofsession_secret
across plugin instances for additional security. This avoids sessions being shared across plugins and possibly bypassing the proof of possession validation.
- If multiple
The following example shows how to enable this feature for the JWT Access Token Authentication method. Similar steps can be followed for the other methods.
Demo
Prerequisites
Follow these prerequisites to set up a demo Keycloak app and a Kong service and route for testing mTLS client auth.
In most cases, the OpenID Connect plugin relies on a third party identity provider (IdP). The examples in this guide use Keycloak as a sample IdP.
Expand the following sections to configure Keycloak and Kong Gateway.
Configure Keycloak
All the *.test
domains in the following examples point to the localhost
(127.0.0.1
and/or ::1
).
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, 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][json-web-key-set]:
-
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:
-
(Optional) Create another confidential client
cert-bound
with settings similar to theservice
client created previously. From the Advanced tab, enable the OAuth 2.0 Mutual TLS Certificate Bound Access Tokens Enabled toggle. -
(Optional, to test mTLS Client Authentication) Create another confidential client
client-tls-auth
with settings similar to theservice
client created above. From the Credentials tab, select the X509 Certificate Client Authenticator and fill the Subject DN field so that it matches the Kong client certificate’s, e.g.:CN=JohnDoe, OU=IT
. -
(Optional, to test Demonstrating Proof-of-Possession Client Authentication) Create another confidential client
client-dpop-auth
with settings similar to theservice
client created above. From the Advanced tab, enable theOAuth 2.0 DPoP Bound Access Tokens Enabled toggle. - 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.
Note: The mTLS Client Authentication, along with the proof of possession feature that validates OAuth 2.0 Mutual TLS Certificate Bound Access Tokens, both require configuring Keycloak to validate client certificates with mTLS using the
--https-client-auth=request
option, and to configure TLS appropriately, including adding the trusted client certificates to the truststore. For more information, refer to the Keycloak documentation.
Configure Kong Gateway
-
Create a service:
curl -i -X POST http://localhost:8001/services \ --data "name=openid-connect" \ --data "url=https://httpbin.konghq.com/anything"
-
Create a route:
curl -i -X POST http://localhost:8001/services/openid-connect/routes \ --data "name=openid-connect" \ --data "paths[]=/"
Set up Demonstrating Proof-of-Possession with the OIDC plugin
To enable Demonstrating Proof-of-Possession, use the proof_of_possession_dpop
configuration option,
and set the service name to openid-connect
to follow the demo:
If configured correctly, it returns a 200
response like this:
{
"id": "5f35b796-ced6-4c00-9b2a-90eef745f4f9",
"name": "openid-connect",
"service": {
"id": "5fa9e468-0007-4d7e-9aeb-49ca9edd6ccd"
},
"config": {
"issuer": "https://keycloak.test:8440/auth/realms/master",
"client_id": [ "cert-bound" ],
"client_secret": [ "cf4c655a-0622-4ce6-a0de-d3353ef0b714" ],
"auth_methods": [ "bearer" ],
"proof_of_possession_dpop": "strict",
}
}
Test Demonstrating Proof-of-Possession
Obtain the token from the IdP. A DPoP supporting client is required. For this demo, here is an example using oauth2c
:
oauth2c https://keycloak.test:8440/realms/master/.well-known/openid-configuration \
--client-id cert-bound --client-secret cf4c655a-0622-4ce6-a0de-d3353ef0b714 \
--grant-type client_credentials --scopes openid --dpop \
--signing-key https://path/to/the_jwks.json
If this is configured correctly, it returns a 200
response and something like the following:
{
"access_token": "eyJhbG...",
"token_type":"DPoP"
}
The token you obtain should include a claim that consists of the hash of the client key:
{
"exp": 1622556713,
"typ": "Bearer",
"cnf": {
"jkt": "hh_XBS..."
}
}
Use the same client key to obtain both the token and sign the request to access the resource.
The client should generate proper proof for possession of the key and send it via the DPoP
header, which will be verified by Kong together with the token.