Chapter 9. API Security
In a microservices architecture, the API Gateway acts as a security endpoint. It is crucial to protect the access to those microservices so only authorized applications, usually referred to as clients , are able to interact with those services. It is required to provide to end users, or resource owners , a mechanism to protect their valuable data from unwanted access.
During the last few years, the banking industry has invested a lot of time and resources to create and expose APIs. There are several initiatives, such as the CMA’s Open Banking in the UK, or the Payments Service Directive 2 (PSD2) in the European Union, that are pushing banks to securely expose their customer’s information using APIs. The mechanisms used to protect those APIs will be explained in this chapter.
Kong provides several plugins that can be used to protect APIs. In the next sections, we cover how to use those plugins to add a strong security layer on top of the microservices.
All of the code for the sample project in this book can be found here .
Key authentication
It is common that only a set of identified client applications will be allowed to interact with APIs exposed on Kong. If a bank uses Kong to expose a Payments API, the bank will probably use an on-boarding process to identify which third-parties want to use that Payments API on their applications. Some examples of such third-parties are retailers, small FinTech companies, etc. That on-boarding process will have several steps, including online forms, that will be used to ask the third-party what type of application it is planning to write. The on-boarding process will most likely include offline and back office steps, such as asking those companies to sign documents like contracts or a code of ethics agreement.
Finally, as the last step of that process, the bank will provide to the third-party a secret key that will be used to interact with the API. That key will only identify the client application, but not to the resource owner. To protect the information of the resource owner, a different mechanism called OAuth2 will be used, which is covered next.
Another interesting use of API keys is for analytics and billing purposes. One of the main benefits of an API Management tool is that it provides a convenient way to detect if an API is being used very often, or on the other hand, if it is becoming obsolete because its usage is declining. That tracking can be done on a consumer-by-consumer basis. You can create reports filtered by consumers using their API key to detect exactly what is the historical usage of an API by a particular customer. If there is a contract in which the usage of that API is being billed monthly, the use of the API key is crucial in order to be able to determine how many API calls the customer has completed in a specific period of time.
Creating consumers
Once the company has identified who the third-parties are that want to consume the API, those third-parties must be added to the database of Kong, as Consumers. That operation will be done using Kong’s Admin API, interacting with the resource Consumer.
In Chapter 5, “Meet the Kong,” some verbs associated with Consumer were shown. This table adds more verbs associated with API Security (OAuth2, JWT and API Keys):
Verb
Path
Description
GET
/consumers/{username or id}/oauth2
List the client applications belonging to that consumer. Those applications will have a client_id and a client_secret.
POST
/consumers/{username or id}/oauth2
Creates a new Application for a consumer. During the creation of the application, a redirect URI is specified, which will be useful in some OAuth flows.
GET
/consumers/{username or id}/key-auth
Lists the API keys a Consumer has.
POST
/consumers/{username or id}/key-auth
Creates a new API key for this Consumer.
GET
/consumers/{username or id}/jwt
Lists the credentials associated to this Consumer that will be used to validate JWT.
POST
/consumers/{username or id}/jwt
Creates new credentials for this Consumer to validate JWT.
The first step to create a Consumer is to POST a new resource to the collection Consumers, as it was explained in Chapter 5. For example, the following curl command will be used to create a new Consumer called PurpleMall :
$ curl -X POST http://localhost:8001/consumers/ --data "username=PurpleMall"
Kong will return an HTTP 201 Created in case the operation was successful, with this JSON object as a response:
{
    "created_at":1517083052000,
    "username":"PurpleMall",
    "id":"3f3d9926-349c-4bb2-b378-9b6a6f40a1cb"
}
It is possible to check if the Consumer was created by using a GET operation to retrieve the collection of Consumers:
$ curl -X GET http://localhost:8001/consumers/
That operation will return this collection. The ID of the consumer, or alternatively the name, will be used later to create an API key for that customer:
{
    "total": 1,
    "data": [
        {
            "created_at":1517083052000,
            "username":"PurpleMall",
            "id":"3f3d9926-349c-4bb2-b378-9b6a6f40a1cb"
        }
    ]
}
If the API is going to be used by other third-parties, it is needed to create new Consumers by repeating the previous POST operation.
Protecting an API with the Key Authentication plugin
Once we have created at least one Consumer, the next step is to protect the API by using the Key Authentication plugin. The parameters of this plugin are:
Parameter
Type
Default
Description
config.key_names
Optional
apikey
It is an array of comma separated parameter names. The Key Authentication plugin will look at either headers or query parameters that match a name found in this list, to find a suitable key.
config.hide_credentials
Optional
false
It is a boolean. A value of true will hide to the microservices the key, so they will be unaware of the existence of this parameter.
There are more parameters such as config.key_in_body, config.anonymous or config.run_on_preflight. Please refer to the documentation of the plugin to learn more about those parameters.
Before activating the Key Authentication plugin, it is necessary to detect which ID of the API is going to be protected. That can be done by querying the APIs collection using this command:
$ curl -X GET http://localhost:8001/apis/
Alternatively, it is possible to use the name of the API, which probably is easier and more convenient. Assuming that the name of the API that is going to be protected is ‘movies’ this curl instruction will enable the Key Authentication plugin on that API:
$ curl -X POST http://localhost:8001/apis/movies/plugins \
    --data "name=key-auth" \
    --data "config.key_names=apikey"
The output of that instruction will be:
{
    "created_at":1517083831000,
    "config":
    {
        "key_names": ["apikey"],
        "key_in_body":false,
        "anonymous":"",
        "run_on_preflight":true,
        "hide_credentials":false
    },
    "id":"d8b5f6ff-9b25-4f84-8b5e-c7cc71548a2f",
    "name":"key-auth",
    "api_id":"bd5baf99-7838-46b7-a81f-575a3726c7b5",
    "enabled":true
}
Once a particular API has been configured to use the Key Authentication plugin, that API becomes unusable unless the client applications include a header named apikey with a valid value.
Assigning keys to client applications
Once the plugin is enabled, the next step is to create a key for the consumer. Other API Management tools usually provide a pair of keys: an apikey and an apisecret . In case the API Management tool supports it, it is usually a good practice to use a pair of keys instead of just one. Later on in the OAuth2 section it will be shown that usually, most OAuth2 flows depend on the existence of a client_id and optionally a client_secret.
The API key is associated with a Consumer. Using the ID of the Consumer created previously (or the name of the Consumer), the following curl will create a new API key (the trailing -d parameter is needed, in order to guarantee that the body of the request is empty):
curl -X POST http://localhost:8001/consumers/3f3d9926-349c-4bb2-b378-9b6a6f40a1cb/key-auth -d ''
The output of that command is:
{
    "id":"9a5fa2b0-d6dd-476a-9d61-0d55a9c7c6ff",
    "created_at":1517087501000,
    "key":"bP0MSdFkkUNz4NQQNzVw2iQ25c3XbotN",
    "consumer_id":"3f3d9926-349c-4bb2-b378-9b6a6f40a1cb"
}
The most important part of the output of that command is the key field. That information must be sent to the third-party using any safe mechanism. It is not recommended to use email to exchange keys; most high-end API Management tools provide a Developer Portal that can be used as a self-service for Consumers to generate their own API keys on the fly.
Using a key to invoke an API
Once the Consumer has the API keys and the API has been configured with the Key Authentication plugin in order to interact with the API, it will need to pass an additional header named apikey with the value of the API key:
$ curl -X GET http://localhost:8000/movies -H 'apikey: bP0MSdFkkUNz4NQQNzVw2iQ25c3XbotN'
If the header “apikey” is present, Kong will accept the request and return an appropriate answer. If the header is not present or the value is wrong, Kong will return an HTTP 401 Unauthorized error or an HTTP 403 Forbidden error.
Alternatively, it is possible to pass the apikey in the query string. However, it is not a recommended practice, because the query string usually contains parameters related to pagination, sorting, etc. Security parameters are expected to be found on headers.
Protecting APIs with OAuth2
API keys are a simple and convenient way to protect API usage from unsolicited applications and consumers. But, detecting if a client application is allowed or not to use an API is not enough. An authorization model is needed in which end users (the resource owners ) will be able to allow or disallow a client application to get access to their personal data or to perform operations on behalf of them.
OAuth2 is an authorization standard commonly used to grant to websites or applications access to the resource owners information. The first version (OAuth) was released in 2010 (RFC 5849). Two years later, in 2012, a second version of the standard (OAuth2; RFC 6749) was released, making the first version of OAuth obsolete. In 2014, OpenID Connect was published, as an authentication layer on top of OAuth2.
Although most end users are unaware of the existence of OAuth2, the truth is that it is the protocol running behind the scenes whenever a user authorizes an application to log in using the Facebook or Linkedin credentials, or when those users allow another application to get access to their profile on those social networks. In that case, a concrete grant type of OAuth2 called Authorization Code is used to allow the end users to log in to a website using their Facebook or Linkedin credentials.
UK Open Banking relies on OAuth2 as its foundational framework for API Security. As OAuth2 is just an authorization framework, and not an authentication framework, Open Banking also recommends the use of OpenID Connect on top of OAuth2.
OAuth2 is used by resource owners to provide to client applications temporary access to their personal data. At the heart of the protocol, it is the creation of temporary access tokens , thus avoiding the need to provide the password to those client applications. A token may be seen like a temporary opaque ID that can be used by a third-party client application to retrieve information that belongs to a resource owner or to perform operations in the name of the resource owner.
There are four different ways to grant access to those client applications using OAuth2:
Additionally, the specification includes two other flows:
Kong has plugins that implement OAuth2 and OpenID Connect. On the next sections, it will be explained how to use those plugins to implement the different OAuth2 grant types.
Enabling OAuth2 plugin
The OAuth 2.0 Authentication plugin has different parameters, summarized in this table:
Parameter
Type
Default
Description
name
Mandatory
Must be oauth2
config.scopes
Optional
It is an array of scopes separated by commas. A scope is typically a permission, the specific detail of which type of data belonging to the resource owner the client application wants to read or write. It may be as well an operation, for example, the ability to make a payment using an account belonging to the resource owner.
config.token_expiration
Optional
7200
The number of seconds this token will be valid. It is recommended to use very low values, for example just a couple of minutes. A value of 300 seconds will be acceptable. By using Refresh Tokens, the client application can extend the life of the token.
config. enable_authorization_code
Optional
false
If enabled, the plugin of OAuth2 will be able to use the Authorization Code grant.
config. enable_client_credentials
Optional
false
If enabled, the plugin of OAuth2 will be able to use the Client Credentials grant.
config. enable_implicit_grant
Optional
false
If enabled, the plugin of OAuth2 will be able to use the Implicit Grant.
config. enable_password_grant
Optional
false
If enabled, the plugin of OAuth2 will be able to use the Resource Owner Password Credentials grants.
config. hide_credentials
Optional
false
If enabled, Kong will remove the OAuth2 headers when invoking the underlying microservices. It is recommended to enable this parameter.
config. refresh_token_ttl
Optional
1209600
The number of seconds the refresh token will be valid. It is recommended to think carefully about a proper value of this parameter in terms of business implications and security. A very low value (just a couple of days) will affect the user experience of the client applications (users will be asked too often to enter their credentials) and a value too high may have security implications.
There are more parameters such as config.mandatory_scope, config.auth_header_name, etc. Please, refer to the documentation of the plugin to learn more.
The following curl instruction could be used to protect an API called movies to use the authorization code grant, to set a lifetime of the tokens of 4 minutes (240 seconds), and to establish ‘ratings’ and ‘revenue’ as valid scopes:
$ curl -X POST http://localhost:8001/apis/movies/plugins \
    --data "name=oauth2" \
    --data "config.enable_authorization_code=true" \
    --data "config.token_expiration=240" \
    --data "config.scopes=ratings,revenue"
Once the plugin is enabled, a Provision Key will be created. That provision key will be used to interact with the OAuth2 endpoints of the API and is supposed to be secret, so only internal applications, usually login forms, will know its value.
As it was described in the section Key Authentication , it is necessary to create a Consumer to identify the third-party that will consume the API. That can be done by issuing a curl command like this:
$ curl -X POST http://localhost:8001/consumers/ --data "username=DisruptiveFinTech"
A Consumer may have different applications consuming an API. So, for example, a FinTech company may have a Data Aggregation application (subscribed to the Accounts API of several banks) but it may have as well a Quick Payments applications (subscribed to the Payments API). Those applications will be associated with a Consumer using a curl command like this:
$ curl -X POST http://localhost:8001/consumers/DisruptiveFinTech/oauth2 \
    --data "name=DataAggregation" \
    --data "redirect_uri=http://getkong.org"
The output of curl will be:
{
    "client_id":"jJGCzhxxEst50sOtywFOINSRvfh48dNB",
    "created_at":1517168275000,
    "id":"7ed353cd-a6ba-4d6c-9146-fb3c781e2ebe",
    "redirect_uri":["http:\/\/www.getkong.org"],
    "name":"DataAggregation",
    "client_secret":"AuIgB5NM4zU57EjHfP8su6bfA3EtnZny",
    "consumer_id":"fee36abc-2474-49b7-addd-7d6b56a16316"
}
That curl command is using the parameter name to set the name of the consuming application (in this case DataAggregation). The parameter redirect_uri is also set to establish the URL that will be used to redirect the browser of the users once they have entered their credentials on the login page of their bank and have allowed the FinTech company to access their data.
That curl command will generate as well a pair of keys (client_id and client_secret) that will be used to interact with the OAuth2 endpoints. In case the API is going to be consumed by third-parties, the values of those fields must be provided to those companies in a secure way (avoiding email).
Once the plugin is enabled on an API, two new resources will be available via POST for that API. Those endpoints must be accessed using HTTPS (for example, the port 8443) because it is a requirement of OAuth2:
The parameters used to interact typically with oauth2/authorize are:
Parameter
Type
Description
client_id
Mandatory
The ID of the application that wants to obtain an access token.
response_type
Mandatory
The type of response of this endpoint. It will typically be ‘code’.
scope
Mandatory
It is an array of scopes separated by commas.
provision_key
Mandatory
The provision key created when the OAuth2 plugin was enabled for this API.
authenticated_userid
Mandatory
The user id of the resource owner.
The parameters used to interact typically with oauth2/token are:
Parameter
Type
Description
client_id
Mandatory
The ID of the application that wants to obtain an access token.
client_secret
Optional
The secret of the application that wants to obtain an access token.
grant_type
Mandatory
The type of grant: authorization_code, password, client_credentials, implicit or refresh_token.
scope
Mandatory
It is an array of scopes separated by commas.
provision_key
Optional
The provision key created when the OAuth2 plugin was enabled to this API.
authenticated_userid
Optional
The user id of the resource owner.
refresh_token
Optional
If grant_type is refresh_token, this parameter will hold the refresh token
code
Optional
If grant_type is authorization_code, this parameter will hold the code.
In the next sections, the different OAuth2 grant flows are explained.
Client Credentials grant
It is common in internal environments that a consuming application needs to interact with an API in order to retrieve static information such as the list of branches of a bank, the list of supply chain providers, the list of countries in which a company is operating, etc. In that case, that information is not associated with a particular resource owner, so it does not make any sense to interact with that API using the username and password of a particular customer or employee.
For these cases, it is very common to use a simplistic OAuth2 flow called Client Credentials . The credentials that will be used to obtain an OAuth2 access token will just be the ID of the client application. There is no need to involve any resource owner to provide their credentials, because this flow is intended to be used by consuming applications that just need to see general information or to perform operations not directly associated to a particular person.
This curl command will be used to enable the OAuth 2.0 Authentication plugin to use a Client Credentials flow on the Movies API:
$ curl -X POST http://localhost:8001/apis/movies/plugins \
    --data "name=oauth2" \
    --data "config.enable_client_credentials=true"
The Client Credentials is so simple that it does not even require the plugin to establish a set of known scopes.
As described in the previous section (Enabling OAuth2 plugin) after the plugin has been configured, it is required to execute two additional steps:
A consuming application will be able to obtain OAuth2 access tokens using this curl command (in this case, the SSL port 8443 is used). The parameters client_id an client_secret are obtained after creating an application for a particular Consumer:
$ curl -X POST https://localhost:8443/movies/oauth2/token \
    --data "client_id=fIeXHLeDwSuaCoHA0Bh2IgUeurBmX3Gt" \
    --data "client_secret=hhRrADE3XYjZp6DBU5ezRVpDt1gkkfK6" \
    --data "grant_type=client_credentials"
The response of curl will be:
{
    "token_type": "bearer",
    "access_token": "mzi3CJMautR8OTDWT5gwVKdWrGosSaCO",
    "expires_in": 7200
}
Sometimes it is needed to use the parameter -k in curl, in order to avoid SSL validation problems.
The access_token returned by curl can be used to interact with the Movies API, passing the token in an Authorization header:
curl -X GET http://localhost:8000/movies \
  -H 'Authorization: Bearer mzi3CJMautR8OTDWT5gwVKdWrGosSaCO'
If the Authorization header is missing, or the token is used after its expiration time, Kong will return an HTTP 401 Unauthorized .
Authorization Code grant
The Authorization Code is the most complex OAuth2 flow. It is commonly used when the client application that consumes an API maintained by a third-party and running in a separate infrastructure. Typically, a FinTech company will be using this OAuth2 flow to gain access to information of customers of banks, in a scenario like this:
This diagram describes the Authorization code flow:
Authorization Code
The first step to enable this OAuth2 flow with Kong is to configure the OAuth 2.0 Authentication plugin to use an Authorization Code flow. This curl command sets a couple of scopes (ratings and revenue), and instructs the OAuth2 server to require at least one scope accepted by the resource owner:
$ curl -X POST http://localhost:8001/apis/movies/plugins \
    --data "name=oauth2" \
    --data "config.scopes=ratings,revenue" \
    --data "config.mandatory_scope=true" \
    --data "config.enable_authorization_code=true"
The output of curl will include a provision_key that will be used later in a login form to be able to interact with the oauth2/authorize endpoint. As in any other OAuth2 flow, it is mandatory to perform two additional steps, as described in the “Enabling OAuth2 plugin” section:
To use the Authorization Code flow, you need:
In order to be able to test the grant, it is highly recommended to install any kind of mock login form that is able to simulate that the user is logged in, that the user has granted the scopes to the third-party, and that this third-party is getting the authorization code. Fortunately, in the GitHub repository of Kong there is a sample app (kong-oauth2-hello-world) that provides all of these artifacts. The steps required to install the kong-oauth2-hello-world are:
The sample app uses basically the oauth2/authorize endpoint to generate a temporary code. This is an illustrative example of the API call that the sample app kong-oauth2-hello-world is doing in its code:
$ curl -X POST https://localhost:8443/movies/oauth2/authorize \    
    --data "client_id=fIeXHLeDwSuaCoHA0Bh2IgUeurBmX3Gt" \
    --data "response_type=code" \
    --data "scope=ratings" \
    --data "provision_key=Nt1iqLe6CPN9Gv91L1nPDLlvzJgYZ5K2" \
    --data "authenticated_userid=userid123"
Once the app gets the authorization code, it will redirect to any mock URL. That URL will contain in the query string the temporary code. That code can be used to manually interact with the oauth2/token endpoint to get the access token:
$ curl -X POST https://localhost:8443/movies/oauth2/token \
    --data "client_id=fIeXHLeDwSuaCoHA0Bh2IgUeurBmX3Gt" \
    --data "client_secret=hhRrADE3XYjZp6DBU5ezRVpDt1gkkfK6" \
    --data "grant_type=authorization_code" \
    --data "code=GGp0UiTPVtpZbfme3bdaypzrUDLNTQln"
The output of curl will be:
{
    "refresh_token": "1XwV5uuzdaX6InOM7otIxKKVklOmDy5F",
    "token_type": "bearer",
    "access_token": "oNMSjPVAVCmQVSQqt2jESOrQ2eHHWKRB",
    "expires_in": 7200
}
Sometimes it is needed to use the parameter -k in curl, in order to avoid SSL certification problems.
That access_token can be used to interact with the Movies API, passing the token in an Authorization header:
curl -X GET http://locaslhost:8000/movies \
  -H 'Authorization: Bearer oNMSjPVAVCmQVSQqt2jESOrQ2eHHWKRB'
If the Authorization header is missing, or the token is used after its expiration time, Kong will return an HTTP 401 Unauthorized .
Resource Owner Password Credentials grant
There are some scenarios in which the client application has been created by the same organization that created the API. In that case, if both the client application and the API are running in the same infrastructure, it may be interesting if the client application uses its own login form to authenticate the user, so those credentials can be used as well to interact with the OAuth server directly, obtaining an access token.
This OAuth2 flow can be used if the following conditions are met:
This OAuth2 flow is commonly used as well in Single Sign-On scenarios. For example, it may be the case in which the consuming application has some kind of SSO token (a cookie, a corporate token, etc) that could be used to obtain an OAuth2 Access Token. Obviously, it needs another artifact that must be able to determine if the SSO token is still valid.
To use this flow with Kong, it is needed to create an artifact, or custom OAuth proxy, that will sit between the client application and the OAuth2 endpoint. This artifact will receive the user id and the password of the resource owner. The artifact will validate also the credentials (probably using an LDAP server) and finally will interact with the OAuth2/token endpoint to get an OAuth2 access token that will be sent back to the client application. To make sure that only this artifact is able to obtain this access token, the provision_key will be used as a parameter to the OAuth2/token call.
The following diagram describes the flow:
Password Credentials
This curl instruction will be used to configure the Resource Owner Password Credentials flow:
$ curl -X POST http://localhost:8001/apis/movies/plugins \
    --data "name=oauth2" \
    --data "config.scopes=ratings,revenue" \
    --data "config.enable_password_grant=true"
As with the other grants, these steps must be done, as described in section “Enabling OAuth2 plugin”:
To get an OAuth2 Access Token, the artifact between the client application and the OAuth2 endpoint will do this call:
$ curl -X POST https://localhost:8443/movies/oauth2/token \
    --data "client_id=fIeXHLeDwSuaCoHA0Bh2IgUeurBmX3Gt" \
    --data "client_secret=hhRrADE3XYjZp6DBU5ezRVpDt1gkkfK6" \
    --data "grant_type=password" \
    --data "provision_key=Nt1iqLe6CPN9Gv91L1nPDLlvzJgYZ5K2" \
    --data "scope=revenue" \
    --data "authenticated_userid=userid123"
The output of curl will be:
{
    "refresh_token": "1XwV5uuzdaX6InOM7otIxKKVklOmDy5F",
    "token_type": "bearer",
    "access_token": "oNMSjPVAVCmQVSQqt2jESOrQ2eHHWKRB",
    "expires_in": 7200
}
Sometimes it is needed to use the parameter -k in curl, in order to avoid SSL certification problems.
That access_token can be used to interact with the Movies API, passing the token in an Authorization header:
curl -X GET http://localhost:8000/movies \
  -H 'Authorization: Bearer oNMSjPVAVCmQVSQqt2jESOrQ2eHHWKRB'
If the Authorization header is missing, or the token is used after its expiration time, Kong will return an HTTP 401 Unauthorized .
Implicit grant
The Implicit grant is an OAuth2 flow very similar to the Authorization Code. This flow will typically be used by Single Page Applications (SPA) written in JavaScript that do not have the ability to safely store any secret. In that case, this flow is not returning a refresh_token, because the client application will not be able to store it.
Another main difference between the Implicit grant and the Authorization code grant is that in the Implicit grant, the Oauth2 endpoint is returning directly the access token in the redirection URL, and not a temporary code.
This is the less secure OAuth2 grant, so only very short-lived access tokens should be issued.
Refresh Token grant
The typical OAuth2 access token will have an expiration time of around 5 minutes (300 seconds). If a client application uses an expired access token, Kong will return an HTTP 401 Unauthorized with this response:
{
    "error_description": "The access token is invalid or has expired",
    "error": "invalid_token"
}
In order to obtain a new access token, a possibility could be to request that the resource owners provide their credentials again. Obviously, once resource owners grant a third-party access to their personal data, they expect the client applications to use that information autonomously, not having to be asked for their credentials again and again.
OAuth2 provides an additional grant, or Refresh Token, that can be used to request new access tokens on behalf of the user. The endpoint that will be used to generate a new access token will be oauth2/token and it will use these parameters:
This sample curl shows how to get a refresh token:
$ curl -X POST https://localhost:8443/movies/oauth2/token \
    --data "grant_type=refresh_token" \
    --data "client_id=fIeXHLeDwSuaCoHA0Bh2IgUeurBmX3Gt" \
    --data "client_secret=hhRrADE3XYjZp6DBU5ezRVpDt1gkkfK6" \
    --data "refresh_token=1XwV5uuzdaX6InOM7otIxKKVklOmDy5F"
Upon completion, the OAuth2 server would return a JSON object like this:
{
    "refresh_token": "5WrRlyTeYh7V2ELWQTtbpG6cUEAoRL8e",
    "token_type": "bearer",
    "access_token": "CpeACPZLMhUiMK5tOA9Xcy22Qn1qitHS",
    "expires_in": 7200
}
The new refresh token will be different from the previous one. If the client application tries to invoke the grant passing a refresh token that was used previously, Kong will return an HTTP 400 Bad Request with this error:
{
    "error_description": "Invalid refresh_token",
    "error": "invalid_request"
}
OpenID Connect
As described in the previous section, OAuth2 is an authorization framework that can be very useful for resource owners to grant temporary access to their personal information and to perform operations on their behalf. However, OAuth2 does not provide any facility to identify the end-user who has granted the permissions, because OAuth2 is not intended to be used to provide identity information.
OpenID Connect was born in 2014 as an identity layer on top of OAuth2. Unlike OAuth2, OpenID Connect provides to client applications a mechanism to identify to the resource owners who has granted permissions.
OpenID Connect has gained traction due to the release of the UK Open Banking APIs. These APIs are based on OAuth2, but the specification explicitly mandates the use of OpenID Connect on top of OAuth2 to be able to add identity information.
New and modified flows
This table summarizes the flows that OpenID Connect has modified or added on top of OAuth2:
Flow
New or Modified
Description
Authorization Code
Modified
In OpenID Connect, the scope must contain the value openid although other scopes may be present. The response of the /token endpoint will include an additional field (id_token) that contains a JWT with identity information.
Implicit Flow
Modified
The scope must contain the value openid although other scopes may be present. The response of the /authorize endpoint will include an additional field (id_token) that contains a JWT with identity information.
Hybrid Flow
New
This flow is designed to provide flexibility; some of the tokens are returned in the /token endpoint or in the /authorize. The response of the /authorize endpoint depends on the value of the parameter response_type, which can be one of these three choices: 1) code id_token ; 2) code token ; 3) code id_token token.
The ID Token
The ID Token is a JSON Web Token (JWT) that contains a set of claims with identity information. The standard claims are:
Claim
Type
sub
string
name
string
given_name
string
family_name
string
middle_name
string
nickname
string
preferred_username
string
profile
string
picture
string
website
string
email
string
email_verified
boolean
gender
string
birthdate
string
zoneinfo
string
locale
string
phone_number
string
phone_number_verified
boolean
address
JSON object
updated_at
number
New endpoints
OpenID Connect introduces a new endpoint: /userinfo. This new endpoint can be used either with GET or with POST and accepts an Authorization header that must contain an OAuth2 Access Token. The output of the endpoint will be a JSON object containing identity information of the resource owner. The fields of that JSON object will have the same names shown in the list of standard claims of the previous section.
Kong support for OpenID Connect
Although Kong Community Edition does not include yet support for OpenID Connect, Kong Enterprise Edition includes a suite of plugins to support OpenID Connect. The plugins included in the suite are:
Protecting APIs with JWT
OAuth2 and OpenID Connect are the most common authorization and authentication frameworks that are used today to protect APIs. However, in some scenarios, it may make sense to use JSON Web Tokens (JWT - pronounced: “jot”) as an alternate way to protect an API. For example:
Kong supports out-of-the-box the validation of JWT tokens. Unfortunately, it does not yet support the generation of JWT; in case that functionality is needed in a project, it may be necessary to write a custom Kong plugin.
What is a JSON Web Token?
JSON Web Tokens (JWT) are defined in the RFC 7519. It is basically a compact and self-contained way to carry a set of claims between two parts. A JWT is composed of three Base64 strings separated by dots:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjMiLCJuYW1
lIjoiSm9hbyBNYXJ0aW5zIn0.TfesTf-QPpJu2y01B6rwQfKuiQTDza32Y3
QPXWcx7Nk
The three strings are:
When an API Gateway or a microservice is invoked using a JWT included in an Authorization header, it will be necessary to perform the following validations:
Enabling the JWT plugin
The following instruction will enable the JWT plugin in the Movies API. The plugin will verify as well that the JWT has not expired yet. The plugin will be configured to use the claim ‘iss’ to identify the key that will be used to validate the token:
$ curl -X POST http://localhost:8001/apis/movies/plugins \
    --data "name=jwt" \
    --data "config.claims_to_verify=exp"
Upon completion of that instruction, any invocation to the Movies API will return an HTTP 401 Unauthorized , unless a valid JWT is passed in an Authorization header and at least a Consumer has been configured with a key that can be used to validate that token.
Adding JWT credentials to a Consumer
Before trying to use the Movies API with a JWT, it is necessary to associate with an existing Consumer a credential that will be used to validate the JWT.
Kong supports different algorithms to validate JWT:
The following instruction creates an HS256 credential for a Consumer named PurpleMall:
$ curl -X POST http://localhost:8001/consumers/PurpleMall/jwt \
    --header "Content-Type: application/x-www-form-urlencoded"
The output of that command will be:
{
    "created_at":1517350724000,
    "id":"2ea7c77c-a627-4917-ada6-1bb052a46e03",
    "algorithm":"HS256",
    "key":"gn0DactcwAMKUmA0iuPVXkBnVjxxIbVW",
    "secret":"Rz3fBOsIWvdoT3SAf1LLc2xIUkLEE1iC",
    "consumer_id":"d25a7eaf-c44b-4425-97c5-4683f989b355"
}
The most important attributes of that output are:
Putting it all together
Once the API and the Consumer have been properly configured, the next step is to create a valid JWT for testing purposes. There are many online JWT generators available. A simple search on Google would provide different useful websites that provide the ability to generate and validate JWT. Probably the best known is JWT.IO, a simple website that provides a useful interface to validate and to generate JWT.
The steps required to generate a JWT are:
Step
Description
Navigate with your browser to https://jwt.io/
A page with a sample JWT will appear. The JWT will be shown on the left side as Encoded, and in the right side as Decoded.
Maintain the Header as it is.
Maintain the algorithm to be HS256.
Add a “iss” claim in the payload
The value should be the same than the “key” property associated with the JWT credential in the Consumer
Add a “exp” claim in the payload
The value should be the current time (in seconds) plus 60 or 90 seconds
Type the secret in the signature
The value should be the same than the “secret” property associated with the JWT credential in the Consumer
Copy the Encoded JWT to the clipboard
It should contain the claims and a signature based on the secret.
This is a sample of the header and payload:
{
  "alg": "HS256",
  "typ": "JWT"
}
{
  "sub": "1234",
  "name": "Simon Warren",
  "iss" : "gn0DactcwAMKUmA0iuPVXkBnVjxxIbVW",
  "exp" : 1517951758
}
This is a sample of that JWT, but encoded (secret: Rz3fBOsIWvdoT3SAf1LLc2xIUkLEE1iC):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0Iiwi
bmFtZSI6IlNpbW9uIFdhcnJlbiIsImlzcyI6ImduMERhY3Rjd0FNS1VtQ
TBpdVBWWGtCblZqeHhJYlZXIiwiZXhwIjoxNTE3OTUxNzU4fQ.s9nHjy6
knQMllCSN-Fqd9q165-pQwjBhi2bueTE-UzM
To interact with the Movies API, the JWT must be included in an Authorization header:
$ curl -X GET http://localhost:8000/movies \
   --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0Iiwi
bmFtZSI6IlNpbW9uIFdhcnJlbiIsImlzcyI6ImduMERhY3Rjd0FNS1VtQ
TBpdVBWWGtCblZqeHhJYlZXIiwiZXhwIjoxNTE3OTUxNzU4fQ.s9nHjy6
knQMllCSN-Fqd9q165-pQwjBhi2bueTE-UzM"
If the JWT was signed properly, the API would return an HTTP 200 with the response. In case the token JWT has expired, Kong will return an HTTP 401 Unauthorized with a message explaining that the JWT expired. In case the claim “iss” contains a value different than the key associated with the Consumer, Kong would return an HTTP 403 Forbidden , with a message saying that no credentials were found for that given ‘iss’.
CORS
Cross-Origin Resource Sharing (CORS) is a W3C Recommendation that allows browsers to interact with resources that are available on domains outside the domain from which the page was downloaded. Although it is common in a web page to include images and style sheets of other domains, any modern browser will protect the execution of JavaScript that tries to interact with APIs belonging to other domains.
CORS is based on response headers. Basically, when CORS is enabled in a particular API served from an API Gateway, a response header called Access-Control-Allow-Origin will be added to the response.
That header can be configured with different values. Some examples assuming that CORS is enabled in an API Gateway in the domain a.com:
To use CORS in Kong, it is enough to enable the plugin on any API, for example in Movies:
curl -X POST http://localhost:8001/apis/movies/plugins/ \
  --header 'content-type: application/x-www-form-urlencoded' \
  --data "name=cors"
The output of curl will be:
{
    "created_at":1517354766000,
    "config":
    {
        "credentials":false,
        "preflight_continue":false
    },
    "id":"4e9f2a99-dee0-4c3d-a193-46a8f6ff0fab",
    "name":"cors",
    "api_id":"bd5baf99-7838-46b7-a81f-575a3726c7b5",
    "enabled":true
}
Any call to a Movies API will now include these headers. From now on, a new header (Access-Control-Allow-Origin) will be included in the response:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 622
Connection: keep-alive
X-Powered-By: Express
X-Content-Type-Options: nosniff
ETag: W/"26e-4lY2V90zz3jPaMtUYoDpyO0zGQw"
Date: Tue, 30 Jan 2018 23:28:13 GMT
Access-Control-Allow-Origin: *
X-Kong-Upstream-Latency: 2
X-Kong-Proxy-Latency: 1
Via: kong/0.11.2
Conclusion
API Security is a complex topic, since APIs can be protected with a variety of choices, including keys, OAuth, OpenID Connect, JWT, and CORS. Kong Gateway includes out-of-the-box different plugins that can be used to create a robust security architecture.
You have now reached the end of this book. We hope you have learned everything you need to take advantage of Kong.