Integrating Kong & ForgeRock

Stéphane Orluc

Wayne Blacklock from ForgeRock posted an article how to integrate Apigee and ForgeRock a few weeks ago. Microservice API gateways is definitely a hot topic these days. I had recently to integrate ForgeRock with Kong. In this post I’ll try to explain in a nutshell what is Kong Microservice API gateway and how we can integrate ForgeRock Access Management to authenticate accesses to any API/Microservice defined in Kong.

What is Kong ?

Kong Inc. is a company created in 2007 in Italy and now in San Francisco. This company sells 3 different solutions:

  1. API Gateway: a Microservice API Gateway to deploy in front of any RESTful API,
  2. Kubernetes Ingress: a Kubernetes Ingress controller,
  3. Service mesh: an inter-service communication/routing service.

These products exist in a community edition (OpenSource) and an enterprise edition.

Kong Microservice API Gateway

Kong Microservice API Gateway is used to wrap all the internal enterprise API and provide a simple and unified access point for all the APIs. It is some kind of reverse proxy for API, routing requests from clients to services. This sort of component is mandatory in Microservice world to avoid a spaghetti plate.

For example, Kong API gateway enables clients to retrieve data or perform actions from multiple services from a centralized access point.
An API Gateway basically simplify your architecture as described in the following schema.

With or without API Gateway

Kong Architecture & concepts

Kong is a really simple 2 components architecture:

  1. A Kong Lua application running in NGINX. The whole (Kong, LUA & NGINX) is distributed with OpenResty,
  2. A data persistence component. Currently supported databases are: PostgreSQL and Cassandra.

TK Present used port of the solution.

Kong concepts are straightforward:

  • You define Service to manage : your API,
  • You define Routes to your service : what is the entry and exit points.

Note: In previous Kong versions and in the current Enterprise Edition documentation, Service and Routes are not explained and you deal with API. This is due to the fact that formerly Kong was an API Gateway and now it is transitioning to Microservices API Gateway which is more granular that API.

On top of Services and Routes you can add:

  • Consumers which are clients that will use your Service,
  • Plugins which are components to easily extend kong functionalities.

To integrate with ForgeRock Access Management, we will use the Openid-connect plugin which is only available for Kong Enterprise Edition (EE). With Kong EE there are 2 ways to interact:

  1. Using the Web GUI which is only available with the EE,
  2. Using the REST API which is common with EE and CE.

We’ll do this with the REST API using Curl or Postman.

How to integrate ForgeRock and Kong ?

Assumptions

In this part, I assume that we already have Kong API Gateway EE v 0.34-1 (Kong) and a ForgeRock Access Management v 6.5.0 (AM) up and running.
Note: In next screenshots and texts I note mylab.am65.forgerockfor for my AM server hostname and kong-tr.forgerock for my Kong server hostname.

In the next steps we will configure Kong EE as a gateway for the HTTPBin service (a really convenient REST API service for testing).

Define a Service

To define a Service we just have to post a request with the name of the service, the host, port, protocol and path. Enter the following curl command to define the Service:

curl -X POST http://kong-tr.forgerock:8001/default/services/ \
-H 'Content-Type: application/json' \
-d '{"host":"httpbin.org", "name":"httpbin-service","port":80,"protocol":"http","path":"/anything"}'

With this, we defined a Service named httpbin-service to manage the API located at this URL : http://httpbin.org:80/anything.

Kong Should answer something like this:

{
"host": "httpbin.org",
"created_at": 1547150984,
"connect_timeout": 60000,
"id": "c837c2a1-2351-4f58-bbd0-93d91fe89c44",
"protocol": "http",
"name": "httpbin-service",
"read_timeout": 60000,
"port": 80,
"path": "/anything",
"updated_at": 1547150984,
"retries": 5,
"write_timeout": 60000
}

Define a Route

To define a Route to access this Service we enter the following curl command:

curl -X POST \
http://kong-tr.forgerock:8001/services/httpbin-service/routes \
-H 'Content-Type: application/json' \
-d '{"hosts":["httpbin.kong-tr.forgerock"]}'

With this, we said that each time an HTTP request with the host header equal to httpbin.kong-tr.forgerock is received by Kong, then Kong must use the Service named httpbin-service to process the request and “proxying” the response.

Kong Should answer something like this:

{
"created_at": 1547152326,
"strip_path": true,
"hosts": [
"httpbin.kong-tr.forgerock"
],
"preserve_host": false,
"regex_priority": 0,
"updated_at": 1547152326,
"paths": null,
"service": {
"id": "c837c2a1-2351-4f58-bbd0-93d91fe89c44"
},
"methods": null,
"protocols": [
"http",
"https"
],
"id": "d7db6ffd-1495-4d80-b621-7f5ff338e973"
}

Now, if we open Chrome and try to access http://httpbin.kong-tr.forgerock:8000 then we get the following page.

httpbin.org/anything defined as Service in Kong

Similarly, with curl if we enter this command:

curl -X GET http://httpbin.kong-tr.forgerock:8000/ \
-H 'Host: httpbin.kong-tr.forgerock' \
-d blahblahblah

We get this response:

{
"args": {},
"data": "blahblahblah",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Cache-Control": "no-cache",
"Connection": "close",
"Content-Length": "12",
"Content-Type": "text/plain",
"Cookie": "authorization=QCol4k3TNZhKwo6cVxAjtw..|1547151238|Zvnu-53X8cYCehYHHgzMARQbaTIXrsfGt1QqaNwg5MSr-ATgINiUA65n2rDjwekom_ZCRHzjOsKmrgpkxWk3OJfdHtIgnysW8nIk4s0XIf47WIvbAps29YAtu1OhI_y3|ShRSRBAh88I5m8L6434ZafDGwGQ.",
"Host": "httpbin.org",
"X-Forwarded-Host": "httpbin.kong-tr.forgerock"
},
"json": null,
"method": "GET",
"origin": "192.168.99.1, 213.41.85.162",
"url": "http://httpbin.kong-tr.forgerock/anything"
}

Add the OIDC plugin to your service

To add the openid-connect plugin to our Service we will execute this curl command:

curl -X POST \
http://kong-tr.forgerock:8001/services/httpbin-service/plugins/ \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Postman-Token: e487c736-43f2-427f-8d9a-345a5dd4dfa1' \
-H 'cache-control: no-cache' \
-d 'name=openid-connect&config.client_id=kongclient&config.client_secret=kongpassword&config.ssl_verify=false&config.issuer=http://mylab.am65.forgerock:8080/am/oauth2/.well-known/openid-configuration&config.scopes=api_url'

This command activate the openid-connect plugin with the following parameters:

  • config.client_id: client ID of the OAuth client as defined in AM,
  • config.client_secret: password of the OAuth client defined in AM,
  • config.ssl_verify: we set this to false because we won’t use HTTPS and don’t want to ckeck any SSL certificate,
  • config.issuer : this is the URL of the well-known page. This is used by Kong for discovering,
  • config.scope : this parameters is used to define the scope we want to use in the configuration. We defined a specific scope in AM we named api_url.

After this command, our Service is protected with AM. We should receive this:

{
"created_at": 1547150776591,
"config": {
"session_memcache_host": "127.0.0.1",
"authorization_cookie_lifetime": 600,
"leeway": 0,
"response_mode": "query",
"verify_parameters": true,
"cache_tokens": true,
"run_on_preflight": true,
"authorization_cookie_samesite": "off",
"login_tokens": [
"id_token"
],
"authorization_cookie_path": "/",
"cache_token_exchange": true,
"introspect_jwt_tokens": false,
"logout_revoke": false,
"auth_methods": [
"password",
"client_credentials",
"authorization_code",
"bearer",
"introspection",
"kong_oauth2",
"refresh_token",
"session"
],
"reverify": false,
"client_secret": [
"kongpassword"
],
"session_redis_port": 6379,
"bearer_token_param_type": [
"header",
"query",
"body"
],
"hide_credentials": false,
"cache_user_info": true,
"issuer": "http://mylab.am65.forgerock:8080/am/oauth2/.well-known/openid-configuration",
"rediscovery_lifetime": 300,
"session_storage": "cookie",
"scopes_claim": [
"scope"
],
"session_memcache_prefix": "sessions",
"forbidden_destroy_session": true,
"credential_claim": [
"sub"
],
"cache_introspection": true,
"session_cookie_path": "/",
"jwt_session_claim": "sid",
"consumer_by": [
"username",
"custom_id"
],
"http_version": 1.1,
"session_redis_host": "127.0.0.1",
"client_credentials_param_type": [
"header",
"query",
"body"
],
"audience_claim": [
"aud"
],
"timeout": 10000,
"verify_signature": true,
"keepalive": true,
"login_action": "upstream",
"introspection_hint": "access_token",
"id_token_param_type": [
"header",
"query",
"body"
],
"session_cookie_lifetime": 3600,
"forbidden_error_message": "Forbidden",
"cache_ttl": 3600,
"session_cookie_name": "session",
"ssl_verify": false,
"upstream_access_token_header": "authorization:bearer",
"session_redis_prefix": "sessions",
"login_methods": [
"authorization_code"
],
"authorization_cookie_httponly": true,
"session_memcache_port": 11211,
"scopes": [
"api_url"
],
"unauthorized_error_message": "Unauthorized",
"session_cookie_samesite": "Lax",
"session_cookie_httponly": true,
"verify_nonce": true,
"consumer_optional": false,
"client_id": [
"kongclient"
],
"password_param_type": [
"header",
"query",
"body"
],
"client_arg": "client_id",
"authorization_cookie_name": "authorization",
"logout_methods": [
"POST",
"DELETE"
],
"verify_claims": true,
"login_redirect_mode": "fragment"
},
"id": "653288e4-11cb-42a0-b1e2-4f448823987f",
"service_id": "564d5752-430d-4950-b633-ef90f2afbfda",
"name": "openid-connect",
"enabled": true
}

At this stage you have a fully functional Kong using AM as OpenID Connect provider.

Using Kong and AM

The various use cases for the Kong openid-connect plugin are detailed in the documentation. See below an extract of Kong documentation.

Protecting Server to Server API Access
For server-to-server we recommend you to use OAuth 2.0 client credentials grant that is enhanced with OpenID Connect features (such as standardized security feature, and automatic discovery). Client credentials are easier to revoke than say password credentials without affecting too many things.

Protecting Interactive Browser based API / Web Site Access
The best method to use here is to use OpenID Connect Authentication using authorization code flow. Kong sets up a session with the browser. After initial authentication the browser will send the cookie automatically — even when making API requests using JavaScript. With authorization code flow you can usually utilize stronger authentication methods such as two-factor authentication on your identity provider.

Protecting Access to APIs from 1st Party Client
Here you can use OAuth 2.0 password grant that is enhanced with OpenID Connect features.

Protecting Access to APIs with Stateless JWT Tokens
When you have JWT (or JWS to be more specific) available for your client, that is possibly issued directly from the identity provider (e.g. by using implicit flow), and want you to use that token to access API protected by Kong, you should use a plugin that provides you OpenID Connect aware stateless verification.

Accessing APIs from Basic Authentication Aware Client
Basic authentication is supported in many 3rd party clients. One such client is Microsoft Excel. The openid-connect plugin allows you to supply username and password or client id and client secret using normal basic authentication headers.

In this post we will test the Server to Server use-case and the Interactive Browser based API.

Server to Server use case
To test the Server 2 Server use case we will do as suggested by Kong: we will use the Client Credential grant flow. For a reminder concerning the Client Credential grant flow I suggest you to read the ForgeRock OAuth 2.0 Guide on Client Credential grant.

With this grant flow, the client is the resource owner and we have to get an access token from AM. Execute the following curl command:

curl -X POST \
http://mylab.am65.forgerock:8080/am/oauth2/access_token \
-H ‘Content-Type: application/x-www-form-urlencoded’ \
-d ‘grant_type=client_credentials&client_id=kongclient&client_secret=kongpassword&scope=openid'

AM will respond something like this:

{
"access_token": "JdYVHUT4qZtBSTTcGBg5AGhmn-E",
"scope": "openid",
"id_token": "eyJ0eXAiOiJKV1QiLCJraWQiOiJ3VTNpZklJYUxPVUFSZVJCL0ZHNmVNMVAxUU09IiwiYWxnIjoiUlMyNTYifQ.eyJhdF9oYXNoIjoic05QSURiZGlwUFpVTjVUdTZzUjc0QSIsInN1YiI6ImtvbmdjbGllbnQiLCJhdWRpdFRyYWNraW5nSWQiOiI4ZGQ1MDFiMC1hMTRlLTQ4MDQtOGI0Yy02N2ZjZTJlNDQ0YjYtMTQwNTM0IiwiaXNzIjoiaHR0cDovL215bGFiLmFtNjUuZm9yZ2Vyb2NrOjgwODAvYW0vb2F1dGgyIiwidG9rZW5OYW1lIjoiaWRfdG9rZW4iLCJhdWQiOiJrb25nY2xpZW50IiwiYXpwIjoia29uZ2NsaWVudCIsImF1dGhfdGltZSI6MTU0Nzc0NjgwMSwicmVhbG0iOiIvTXlMYWIiLCJleHAiOjE1NDc3NTA0MDEsInRva2VuVHlwZSI6IkpXVFRva2VuIiwiaWF0IjoxNTQ3NzQ2ODAxfQ.wUFbloc7QUsvTavDecBcl8fo1z6Ttg_s-8bdFVU3wzAt9a6BV-bsjIuYSSFU7FUDODcET_rNWaYMQEGKylafZW0NxPmYL9uGf7CSpnzF4vxp0IBiAWjPmxQ21TvYU64jVyR4QBdznyZoIakdpjcZRUwy4emW1Ft5N-Bc65lJXFsQdpqucnxMuKbw2SAg6sZk3OdOeBVcKMkMyLWNpN5ss_F4-pYBBZIk2EWQBoU0u8Y-sO0y1GCrV4k69bpwhjxeOBgAy90B0YBXauDK7oofJ2USgXQhAVrgZOHwFU1WkKn2FqIY-MvpGvlTgY9EGpebeG3_lfrg1WMyGtnCPyDT5w",
"token_type": "Bearer",
"expires_in": 3599
}

Finally we access the API from our client. Execute the following curl command to simulate this behavior:

curl -X GET \
'http://httpbin.kong-tr.forgerock:8000/?=' \
-H 'Authorization: Bearer JdYVHUT4qZtBSTTcGBg5AGhmn-E' \
-H 'Host: httpbin.kong-tr.forgerock'

Kong verify the access token provided and display the API; we’ll get the following response:

{
"args": {
"": ""
},
"data": "",
"files": {},
"form": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Authorization": "Bearer JdYVHUT4qZtBSTTcGBg5AGhmn-E",
"Cache-Control": "no-cache",
"Connection": "close",
"Cookie": "authorization=QGsUakOlKCuCs5o1DyAtKg..|1547747507|SzsfbIhNr7ug_JmU62_9-sr6uLud_A6W2sCz3LnNEAS0gtgaccJxEY1blxulQFlUJywu79UYL4YEEAVMQciB1jtbPhsn_hftLJKmfQD9LU99puzQNzc6-Xo2aqW1MRym|s6bVgfaUsuh3nR1hquNvnIDGmS0.",
"Host": "httpbin.org",
"X-Forwarded-Host": "httpbin.kong-tr.forgerock"
},
"json": null,
"method": "GET",
"origin": "192.168.99.1, 88.191.16.249",
"url": "http://httpbin.kong-tr.forgerock/anything?="
}

This is the result of our request to the API http://httpbin.org/anything through Kong API gateway.

Interactive Browser based API use case
To test the Interactive Browser based API use case we will use the Authorization Code grant flow. Same as before, I recommend you to read the ForgeRock OAuth 2.0 Guide on Authorization Code grant.

With this grant flow we will use our Chrome browser to access the API http://httpbin.kong-tr.forgerock:8000/. We will be instantly redirected to the AM login page.

AM Login page after redirect

On this page we enter the login and password of the Resource Owner (demo/changeit) and we are redirected to the consent page (for api_url scope in my exemple).

Consent page for api_url scope

Then we click on “Allow”, with the authorization code, Kong authenticates to AM and ask for an Access Token. If everything is ok, then AM returns an Access Token to Kong. And finally, Kong displays the “proxied” API.

Displayed page after a successful Authorization Code grant flow

Conclusion

Today we’ve seen how to configure Kong Microservice API gateway to use ForgeRock Access Management as OpenID Connect Provider in a really simple way. The openid-connect plugin has many options and parameters to play with. I encourage you to test it.
Comments are welcome !

Stéphane Orluc

Written by

Solution Architect at ForgeRock

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade