Extra Use Cases for Kong OIDC Plugin

Kong’s OIDC plugin is so powerful and complicated (It has nearly 200 parameters…) that it can do a lot more when users know what combination of configs they need to use. In today’s post, I will show you a few use cases that might help you use this plugin better.

Please note I was using the latest 2.4.1.1 version of Kong Gateway (Enterprise), some of the config might not be available in the version you are using.

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

Use claim from IDP token to upstream header

If you want to map a value from IDP token to upstream header, you can use config.upstream_headers_names and config.upstream_headers_claims.

Let’s say we have below claim in our JWT token payload.

1
2
3
4
5
{
"payload": {
"kong-test": "to-upstream"
}
}

The goal is to map value to-upstream to header kong-test and send it to upstream server. We can set OIDC plugin as below. This is using password flow.

1
2
3
4
5
6
7
8
9
10
curl --request POST \
--url http://kong.li.lan:8001/plugins \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data name=openid-connect \
--data config.issuer=https://<keycloak>/auth/realms/demo \
--data config.client_id=<client_id> \
--data config.client_secret=<client_secret> \
--data config.auth_methods=password \
--data config.upstream_headers_claims=kong-test \
--data config.upstream_headers_names=test_kong

When we call our api endpoint with username and password we should see below header sent in the request sent to upstream server.

1
"Test-Kong": "to-upstream",

If the value is an object. For example, if I want to pass the information of employee to upstream in a header, it will be base64 encoded.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"payload": {
"employee": {
"favourites": {
"beverage": "coffee"
},
"groups": [
"default",
"it"
],
"name": "li"
}
}
}

Let’s enable the plugin as below,

1
2
3
4
5
6
7
8
9
10
curl --request POST \
--url http://kong.li.lan:8001/plugins \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data name=openid-connect \
--data config.issuer=https://<keycloak>/auth/realms/demo \
--data config.client_id=<client_id> \
--data config.client_secret=<client_secret> \
--data config.auth_methods=password \
--data config.upstream_headers_claims=employee \
--data config.upstream_headers_names=x-employee-info

Now when you authenticate, your upstream should have below header.

1
2
3
"X-Employee-Info": [
"eyJuYW1lIjoibGkiLCJmYXZvdXJpdGVzIjp7ImJldmVyYWdlIjoiY29mZmVlIn0sImdyb3VwcyI6WyJkZWZhdWx0IiwiaXQiXX0="
]

Check Token received

Sometimes we might have issue setting up OIDC plugin but we don’t see a lot info from logs. For example in the log you can see kong failed to find the consumer for consumer mapping and you are pretty sure token should have this information. (This could happen when you forget to include a scope in your request so the claim does not get added.)

The best way to debug in this situations is to check the token sent back from your IDP. To do that, we need to set config.login_action=response and config.login_tokens=tokens.

Example:

  1. Enable OIDC plugin with authorization_code flow.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    curl --request POST \
    --url http://kong.li.lan:8001/plugins \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --data name=openid-connect \
    --data config.issuer=https://<keycloak>/auth/realms/demo \
    --data config.client_id=<client_id> \
    --data config.client_secret=<client_secret> \
    --data config.auth_methods=authorization_code \
    --data config.login_action=response \
    --data config.login_tokens=tokens
  2. Use browser to access https://<kong_proxy>/<oidc_protected_route>

  3. Log in and we will see the token from IDP.

Required Multiple Claims

If your application requires certain claim with certain value on the JWT token sent from your IDP, you can use below 4 pairs to validate those JWT tokens.

  • config.groups_claim and config.groups_required
  • config.scopes_claim and config.scopes_required
  • config.roles_claim and config.roles_required,
  • config.audience_claim and config.audience_required

The name of the parameter does not matter, you can them on any claims. There are some guide line about using them though.

  1. You can validate at most 4 claims at the same time.
  2. You can validate multiple values of the same claim in or and and logic.
  3. You can traverse array/object for the claim name.

For example, let’s say I will get below employee claim on the JWT token.

1
2
3
4
5
6
7
8
9
10
"employee": {
"name": "li",
"groups": [
"default",
"it"
],
"favourites": {
"beverage": "coffee"
}
}

Traverse claim array

If I only want to give access to an employee whose favourite beverage is coffee, I can enable OIDC plugin as below.

1
2
3
4
5
6
7
8
9
10
11
12
curl --request POST \
--url http://kong.li.lan:8001/plugins \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data name=openid-connect \
--data config.issuer=https://<keycloak>/auth/realms/demo \
--data config.client_id=<client_id> \
--data config.client_secret=<client_secret> \
--data config.auth_methods=password \
--data config.scopes_required=coffee \
--data config.scopes_claim=employee \
--data config.scopes_claim=favourites \
--data config.scopes_claim=beverage

As you can see I am setting "scopes_claim":["employee","favourites","beverage"] as an array and config.scopes_required=coffee. Kong will traverse JWT claim and verify if beverage is the equal to coffee. If the return JWT does not have beverage=coffee, user will get HTTP/1.1 403 Forbidden.

Validate multiple values for a particular claim

  • AND Logic
    If I want to give access to user who is in both default and it group, we can enable OIDC plugin as below.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    curl --request POST \
    --url http://kong.li.lan:8001/plugins \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --data name=openid-connect \
    --data config.issuer=https://<keycloak>/auth/realms/demo \
    --data config.client_id=<client_id> \
    --data config.client_secret=<client_secret> \
    --data config.auth_methods=password \
    --data 'config.scopes_required=default it' \
    --data config.scopes_claim=employee \
    --data config.scopes_claim=groups
  • OR logic
    If I want to give access to user from either default or it group, we can enable OIDC plugin as below.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    curl --request POST \
    --url http://kong.li.lan:8001/plugins \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --data name=openid-connect \
    --data config.issuer=https://<keycloak>/auth/realms/demo \
    --data config.client_id=<client_id> \
    --data config.client_secret=<client_secret> \
    --data config.auth_methods=password \
    --data 'config.scopes_required=default' \
    --data 'config.scopes_required=it' \
    --data config.scopes_claim=employee \
    --data config.scopes_claim=groups

The key is for config.scopes_required, array indices is OR logic and space separated on the same array indices is AND logic.

Pass extra query argument to authoriazation endpoint

When you are using authorization code flow, unrelated query arguments on the redirect url will be removed. You can add extra query arguments in authorization endpoint query string with below settings.

Add dynamic value

Let’s say you want to pass a query argument to idp to specify who is accessing the server. For example, you can pass username or email to IDP by setting config.authorization_query_args_client.

1
2
3
4
5
6
7
8
9
curl --request POST \
--url http://kong.li.lan:8001/plugins \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data name=openid-connect \
--data config.issuer=https://<keycloak>/auth/realms/demo \
--data config.client_id=<client_id> \
--data config.client_secret=<client_secret> \
--data config.auth_methods=authorization_code \
--data config.authorization_query_args_client=username

After that, when we go to https://<kong_proxy>/<protected_route>?username=admin We should see username is filled with admin.

Add static value

If you want to add static query arguments, you can use config.authorization_query_args_names and config.authorization_query_args_values to add multiple value pairs.

1
2
3
4
5
6
7
8
9
10
curl --request POST \
--url http://kong.li.lan:8001/plugins \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data name=openid-connect \
--data config.issuer=https://<keycloak>/auth/realms/demo \
--data config.client_id=<client_id> \
--data config.client_secret=<client_secret> \
--data config.auth_methods=authorization_code \
--data config.authorization_query_args_names=user \
--data config.authorization_query_args_values=demo

Now when we go to https://<kong_proxy>/<protected_route> We should see user=demo on the authorization url.

That’s all I want to share today. In my next post I will show you how to use client auth with OIDC.