search
articles

Configuring Kong to Use ForgeRock Access Management as an OpenID Connect Provider

Summary

Microservice APIs are a hot topic. We recently posted an article about how to integrate Apigee and ForgeRock. In this article, we'll show you how to configure Kong to use ForgeRock Access Management as an OpenID Connect provider. 

 

Introducing Kong

Kong Inc. is a San Francisco-based company offering three different products in Open Source (community) and enterprise versions. They include a microservice API gateway deployed in front of any RESTful API, a Kubernetes ingress controller, and an inter-service communication/routing service. 

Understanding Kong Microservice API Gateway

Kong's Microservice API gateway is used to wrap all internal enterprise APIs, and provide a simple and unified access point for them. It is some kind of reverse proxy for API, routing requests from clients to services. 

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

 

Understanding Kong Architecture 

Kong comprised of two types of component architecture:

  1. A Kong Lua application running in NGINX. It (KongLUA, and NGINX) is distributed with OpenResty.
  2. A data persistence component. Currently supported databases include PostgreSQL and Cassandra.

Kong concepts are straightforward—you define a Service to manage your API, as well as Routes to your service (entry and exit points).

You can add the following on top of services and routes:

  • 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 the Kong Enterprise Edition (EE). There are two ways to interact with Kong EE:

  1. Using the Web GUI, which is only available with the EE
  2. Using the REST API, which is common to the EE and community edition (CE)

In this article, we will work with a REST API using cURL or Postman.

Integrating AM and Kong 

Let's get started.

Assumptions

This section assumes that you 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 that in screenshots and text, we use mylab.am65.forgerockfor for our AM server hostname and kong-tr.forgerock for our Kong server hostname.

In the next steps, we will configure Kong EE as a gateway for the HTTPBin service, which will give us a really convenient REST API service for testing.

Defining a Service

To define a Service, we need 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"}'

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

Kong Should return 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
}

Defining a Route

To define a Route to access this Service, 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"]}'

Each time an HTTP request with the host header equal to httpbin.kong-tr.forgerock is received by Kong, it uses the Service named httpbin-service to process the request and proxy the response.

Kong should return 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"
}

Open Google Chrome and try to access http://httpbin.kong-tr.forgerock:8000The following page displays:

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

It returns 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"
}

Adding the OIDC Plugin to Your Service

To add the openid-connect plugin, 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 activates the openid-connect plugin with the following parameters:

  • config.client_id: The client ID of the OAuth client as defined in AM.
  • config.client_secret: The password of the OAuth client defined in AM.
  • config.ssl_very: Set this to false because we are not using HTTPS, and we don’t want to check an SSL certificate.
  • config.issuer : The URL of the well-known page. This is used by Kong for discovering. 
  • config.scope : This parameter is used to define the scope we want to use in the configuration. We defined a specific scope in AM and named it api_url.

After entering 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
}

We now 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. Following are excerpts from the Kong documentation:

Protecting Server-to-Server API Access
For server-to-server API access, we recommend using the 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 password credentials without affecting too many things.

Protecting Interactive Browser-Based API/Web Site Access
The best method is to use OpenID Connect Authentication using an 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 First Party Clients
Use an 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) available for your client that is possibly issued directly from the identity provider (for example, by using implicit flow), and want to use that token to access API protected by Kong, you should use a plugin that provides you with OpenID Connect aware stateless verification.

Accessing APIs From Basic Authentication-Aware Clients
Basic authentication is supported in many third-party clients. One such client is Microsoft Excel. The openid-connect plugin is used to supply a username and password or client id and client secret using normal basic authentication headers.

In this article,  we will test the Server-to-Server use-case and the interactive browser-based API.

Testing a Server-to-Server Use Case
Per Kong's recommendations, we will use the Client Credential grant flow. Before beginning, we recommend reading section 1.1.4, "OAuth 2.0 Client Credentials Grant," in the ForgeRock Access Management 5.5 OAuth 2.0 Guide. 

In the following grant flow, the client is the resource owner, and we need 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 responds with 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, access the API from our client. Execute the following curl command:

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

Kong verifies the access token provided and displays the API:

{
    "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.

Testing an Interactive Browser-Based API Use Case
We will use the Authorization Code grant flow to test this use case. Before beginning, we recommend reading section 1.1.4, "OAuth 2.0 Client Credentials Grant," in the ForgeRock Access Management 5.5 OAuth 2.0 Guide. 

Use Chrome to access the API http://httpbin.kong-tr.forgerock:8000/. We are immediately redirected to the AM login page:

Enter the login and password of the Resource Owner (demo/changeit). AM redirects you to the consent page for api_url scope, as shown below:

Select Allow. Kong authenticates to AM with the authorization code and asks for an access token. On success, AM returns an access token to Kong, and Kong displays the “proxied” API:

Conclusion

Congratulations on successfully configuring Kong Microservice API gateway to use ForgeRock Access Management as OpenID Connect Provider. The openid-connect plugin has many options and parameters available, and we encourage you to explore them.