When users are designing APIs, there might be some security requirements that they must follow. For example, for Financial-grade API (FAPI), users must use client_assertion (client_secret_jwt or private_key_jwt) to send their request to IDP token endpoint.
As client_secret_jwt is not supported in FAPI Part II, I would only talk about how to use private_key_jwt with Kong OIDC plugin as it satisfy both FAPI Part I ad Part II.
Prerequisites:
Kong Gateway (Enterprise)
OIDC server is running. (Keycloak in my example) If you are not sure how to use keycloa, you can check my previous post
I highly recommend reading this article to understand how client authentication works.
Next we will create a path /demo to access our service.
1 2 3
curl -X POST \ --url "http://localhost:8001/services/oidc-demo/routes" \ --data 'paths[]=/demo'
Use KONG JWK with Keycloak
When Kong Enterprise started, it will publish its JWK at <admin_api>/openid-connect/jwks. We need to let Keycloak know this public key in order to verify client_assertion kong will send to it. Keycloak supports loading JWKs urls which means it need to either havce access to <admin_api>/openid-connect/jwks or users export Kong JWKs and publish this JWK somewhere else.
In reality it might not be possible to expose admin api endpoint directly to Keycloak, hence I would use a json-server to host the exported JWKs.
Export JWKs
Below command saves Kong JWKs to a jwk.json file.
1 2
curl -X GET -s\ --url "http://localhost:8001/openid-connect/jwks" > jwk.json
Host JWK file with json-server
This is for demonstration purpose only, you can use whatever server to host your JWK file.
Firstly I put my jwk.json file inside a jwks folder on my server. Then I start this server with below docker-compose file. This service is behind reverse proxy Traefik, I use it to handle TLS.
Go to Credentials tab and select Signed Jwt as Client Authenticator.
Put Use JWKS URL to ON and enter https://jwk.yourdomain in JWKS URL field.
Select PS256 as Signature Algorithm. (FAPI Part II allows ES256 or PS256 only)
Enable OIDC plugin
I only enable password flow in this example. If you want to know more about different flows, please check my previous post. As you can see we don’t need client_secret in our config. Kong will generate client_assertion automatically when it sends request to keycloak.
Not sure if it is a bug, but I could only get RS256 signature algorithm working for this method. If you are designing FAPI, this is not for you. You also need third party tools for generating JWK from a private key. If you don’t want to use third party tool, this method is not for you.
If you prefer using your own certificate, you can either use keycloak or third party software like openssl to generate key and cert pair.
Generate Cert from Keycloak
Click Clients on left sidebar.
Choose the client you want to use.
Go to Credentials tab and select Signed Jwt as Client Authenticator.
Select RS256 as Signature Algorithm.
Click Generate new keys and certificate button.
Select PKCS12 as Archive Format
Enter a simple Key Password and Store Password. We need this password to extract private key later.
Click Generate and Download button.
Now you should have file keystore.p12 downloaded, we will use openssl to extract private key.
Extract private key out from PKCS12. You will be prompted to enter password we set in step 7. Just keep input the same password. You need to enter 3 times, the last two time was to set password for private key file.
Remove password from private key file. You will be asked to enter password again. Enter it, and the new privatekey.pem will NOT be protected by password.
Use openssl to generate self-sign certificate is pretty straight forward. Enter below command and you will be prompted to enter some detail like country code, state, locality etc. After that you will get a self-sign certificate with 1 year validity.
If you generate certificate with third party tool, you need to upload self-signed-cert.pem to keycloak.
Convert private key to JWK
You need to use third party tool to convert privatekey.pem to JWK. I am using python library called authlib in my example. Let’s say the JWK we got was below: