Use Kong OIDC Plugin to Protect Your API Services
Kong’s OIDC plugin could be one of the most complicated plugins they offer. When I first saw it, I was overwhelmed by the number of settings it has and I had no idea where to start. In today’s post I would like to explore using this plugin with different authentication grants as well as consumer mapping.
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
Prepare Kong
I am running latest Kong Gateway (Enterprise) version 2.3.3.2.
Create service
Define a service object in kong and use your api server as upstream. In our example, I will use httpbin.
1 | curl -X POST \ |
Create Route
Next we will create a path /demo
to access our service.
1 | curl -X POST \ |
OIDC Plugin Flows
There are quite a few OIDC authorization methods we can use. You can enable all of them (which is the default option). For demonstration purpose, I will bundle similar authenticaition methods in below sub sections and enable plugin globally.
authorization_code
Enable Plugin
1
2
3
4
5
6
7curl --request POST \
--url http://localhost:8001/plugins \
--data name=openid-connect \
--data config.issuer=https://<keycloak_host>/auth/realms/demo \
--data config.client_id=demo-client-id \
--data config.client_secret=<client_secret> \
--data config.auth_methods=authorization_codeAuthenticate
Go go<proxy_url>/demo
, users will be redirected to keycloak’s website to authenticate themselves.
password
and client_credentials
Enable Plugin
1
2
3
4
5
6
7
8curl --request POST \
--url http://localhost:8001/plugins \
--data name=openid-connect \
--data config.issuer=https://<keycloak_host>/auth/realms/demo \
--data config.client_id=demo-client-id \
--data config.client_secret=<client_secret> \
--data config.auth_methods=client_credentials \
--data config.auth_methods=passwordAuthenticate
clients need to provide their credentials (either username/password or client_id/client_credential) for authenticatioin. These credentials can be set as Basic Authorization header in base64 format, in query string or as payload in the request body.Example:
In Headers
Calculate base64 header
1
echo -n "username:password" | base64
You will get
dXNlcm5hbWU6cGFzc3dvcmQ=
.Make the call
1
2
3curl --request GET \
--url http://localhost:8000/demo \
--header "Authorization:Basic dXNlcm5hbWU6cGFzc3dvcmQ="
In Query String
1
2curl --request GET \
--url http://localhost:8000/demo?username=username&password=passwordIn Body
1
2
3
4curl --request GET \
--url http://localhost:8000/demo \
--data username=admin \
--data password=admin
bearer
, introspection
and kong_oauth2
If these methods are enabled, clients need to provide token (JWT, Opaque token) in Authorization header as Bearer token.
Enable Plugin
1
2
3
4
5
6
7
8
9curl --request POST \
--url http://localhost:8001/plugins \
--data name=openid-connect \
--data config.issuer=https://<keycloak_host>/auth/realms/demo \
--data config.client_id=demo-client-id \
--data config.client_secret=<client_secret> \
--data config.auth_methods=bearer \
--data config.auth_methods=kong_oauth2 \
--data config.auth_methods=introspectionAuthenticate
1
2
3curl --request GET \
--url http://localhost:8000/demo \
--header 'Authorization: Bearer zCSrD05fpRdHdvrlAmwkViNDaAq1lkmN'
refresh_token
and session
These two auth methods can be used to any of above methods. Kong will use these methods if it is enabled and supported by your idp. You can enable them as below.
1 | --data config.auth_methods=refresh_token \ |
Consumer Mapping
If you want to further contorl access to your api per Kong consumer, you can use config.consumer_claim
to mapping authentication to a specific Kong consumer. Let’s use below JWT token as an example.
1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhY3IiOiIxIiwiYXVkIjoiYWNjb3VudCIsImF6cCI6ImtvbmctZGVtbyIsImVtYWlsIjoiYWRtaW5AZGVtbyIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwiZXhwIjoxNjIwNTM3OTAwLCJmYW1pbHlfbmFtZSI6IkRlbW8iLCJnaXZlbl9uYW1lIjoiYWRtaW4iLCJpYXQiOjE2MjA1Mzc2MDAsImlzcyI6Imh0dHBzOi8va2V5Y2xvYWtkZW1vL2F1dGgvcmVhbG1zL2RlbW8iLCJqdGkiOiJiN2U2YmMwOC05OWM5LTRmOGQtOTg2NS0wODEyNWU4NmFmYjYiLCJuYW1lIjoiYWRtaW4gRGVtbyIsInByZWZlcnJlZF91c2VybmFtZSI6ImFkbWluIiwiZGVtby11c2VyIjoiYWRtaW4ifQ.nvx-0JvP6jSs-C3muBuNGU8-Uk-N1PbAhXbW3Ur4BnU |
If we decode this JWT, we will see below in its payload.
1 | { |
You can use any of this values a consumer_claim
as long as its value matches your consumer id, username or custom_id.
For example, let me create a consumer admin
1 | curl --request POST \ |
Then we add below to our plugin.
1 | --data config.consumer_claim=preferred_username |
Now when I successfully authenticate user admin, I will see X-Consumer-Id
and X-Consumer-Id
headers added to my request to upstream. It means consumer mapping is successful and we can user ACL or rate limiting plugin to further control this consumer’s access.
1 | "X-Consumer-Id": "31354b6b-6cf7-46d9-bbd9-6189218e0000", |
One thing to note is that if you enable consumer mapping and you did not set config.consumer_optional
or config.anonymous
, you will get HTTP/1.1 403 Forbidden
if the consumer claim value does not match any Kong consumer.
That’s all for today’s post. I hope it is useful to you.