OpenID Connect with Kong Ingress Controller and Amazon Cognito

Claudio Acquaviva
14 min readSep 1, 2023

API Authentication and Authorization

Authentication involves identifying the requester of information or resource and challenging that entity for authentication material or credentials. Authorization involves verifying whether that authenticated entity actually has permissions to execute a function or CRUD (create, read, update and delete) operations on data. Authentication and authorization are foundational to all security domains, including API security.

As organizations have shifted towards heavily distributed architectures and the use of cloud services, the traditional security best practice of locking down a perimeter has become less useful. The identity of the consumers is stored in Identity Access Management (IAM) and is now used heavily to control access to functionality and data, and is also an enabler of zero-trust architectures.

When considering security best practices for authentication and authorization, API and the IAM should be able to differentiate between user identities and machine identities. While it is possible to challenge a user for additional authentication material in a session, this option is not available for machine communication. The API also should support federated identity providers as most organizations have multiple security contexts and identity stores/providers.

Kong Gateway Enterprise and Authentication

Kong Gateway Enterprise provides several methods that support authentication:

Basic Authentication: This is where consumers pass username/password in headers. It is an older authentication method , not recommended for modernization. However for legacy workloads, apply Basic Auth Plugin to pass client id/client secret-based authentication where either Kong or Vault Plugin where external identity provider acts as the identity store. If it’s Kong, the id and secret are auto-generated, whereas with an external provider, you have the option to upload that id and secret. Use LDAP Authentication if LDAP-based authentication like using AWS Directory Services is still in place. This is mostly used to support legacy consumers during migration.

API Keys: This is a static key or id identifying the consumer. API keys alone are not a sufficient form of authentication and should be used primarily as one form of identification. If API keys are used, ensure that consumption is monitored and rotate API keys often. Note that API keys alone will not be sufficient, and we recommend combining with a mutual TLS . Use Key Auth Plugin if the consumers use API keys to get authenticated.

OAuth Tokens: OAuth (Open Authorization) is an open standard for access delegation, commonly used as a way for Internet users to grant websites or applications access to their information on other websites but without giving them the passwords. OAuth tokens are based on newer authentication protocols like OpenID Connect and authorization protocols where the tokens are valid for a limited period. Use OAuth 2.0 plugin when Kong acts as the identity provider supporting all OAuth 2.0 grants. Use OpenID Connect plugin when external identity providers, like Amazon Cognito, are used for OAuth 2.0. To assert the authorization, make use of consumers, roles, groups and scopes attributes.

Mutual TLS: Certificate-based authentication is more common for machine-to-machine communication and automation scenarios where it is not possible to obtain additional credentials. Use Mutual TLS plugin, if the consumers cannot support OAuth 2.0 and/or process more secured data.Consider using AWS Certificate Manager Private Certificate Authority that provides you a highly-available private CA service without the upfront investment and ongoing maintenance costs of operating your own private CA.

Finally, use Access Control List plugin in combination with the above authentication plugins to validate authorization.

Please, refer to Kong Learning Center to learn more aboute API Authentication.

Kong Gateway Enterprise and OpenID Connect

Of all authentication mechanisms listed before, OpenID Connect (OIDC) is the preferred one for advanced requirements. In fact, when applying OIDC to secure the APIs, we’re delegating the Authentication process to an external entity, called Identity Provider (IdP).

OIDC is a authentication protocol built on top of OAuth 2.0 and JWT (JSON Web Token) to add login and profile information about the identity who is logged in.

OAuth 2.0 defines grant types for different use cases. The most common ones are:

Authorization Code: for apps running on a web server, browser-based and mobile apps for user authentication

Client Credentials: for application authentication

PKCE (Proof Key for Code Exchange): an extension to the Authorization Code grant. Recommended for SPA or native applications, PKCE acts like a non hard-coded secret.

Here’s the typical topology and the Authorization Code with PKCE grant:

  1. Consumer sends a request to Kong Data Plane to consume a specific API.
  2. Since the API’s been protected with the OIDC plugin, the Data Plane redirects the consumer to the IdP. Consumer provides credentials to the Identity Provide (IdP).
  3. IdP authenticates the consumer enforcing security policies previously defined. The policies might involved several database technologies (e.g. LDAP, etc.), MFA (Multi-Fact Authentication), etc.
  4. After user authentication, IdP redirects the consumer back to the Data Plane with the Authorization Code injected inside the request.
  5. Data Plane sends a request to the IdP’s token endpoint with the Authorization Code and gets an Access Token from the IdP.
  6. Data Plane routes the request to the upstream service along with the Access Token

It’s important to notice that one of the main benefits provided by an architecture like this is to follow the Separation of Concerns principle:

Identity Provider: responsible for User and Application Authentication, Tokenization, MFA, multiples User Databases abstraction, etc.

API Gateway: responsible for exposing the Upstream Services and controlling their consumption through an extensive list of policies besides Authentication including Rate Limiting, Caching, Log Processing, etc.

Kong Gateway Enterprise and Amazon Cognito

The two next topics describe Client Credentials and Authorization Code OAuth grants implemented by Kong Gateway Enterprise and Amazon Cognito as the Identity Provider.

Before we get started follow the instructions described in the Kong Gateway Enterprise 3.4 and Amazon Elastic Kubernetes Service (EKS) 1.27 to get your Kong Gateway running on an EKS Cluster

Client Credentials

The main use cases for the Client Credentials grant is to address application authentication (and authorization) rather than user authentication. Is such scenario, authentication processes based on userid and password are not feasible. Instead, applications should deal with Client IDs and Client Secrets to authenticate and get a token.

Cognito instance — Amazon Console

The following process describes how to create a minimal Amazon Cognito User Pool, using its new console, supporting the Client Credentials Grant. Check the Amazon Cognito documentation to learn more.

  1. Go to the Amazon Cognito console. If prompted, enter your AWS credentials.

2. Choose “Create user pool”.

3. On the “Configure sign-in experience” page, choose “Email” for “Cognito user pool sign-in options”. Click "Next".

4. On the “Configure security requirements” page, choose “No MFA” for “MFA enforcement”. Click "Next".

5. On the “Configure sign-up experience” page, accept all default settings and click "Next".

6. On the “Configure message delivery” page, choose “Send email with Cognito” for “Email provider”. Click "Next".

7. On the “Integrate your app” page:

  • Type for “User pool name”: “kongpool”
  • Choose “Use the Cognito Hosted UI” for “Hosted authentication pages”.
  • Choose “Use a Cognito domain” for “Domain type”.
  • Type “https://kongdomain" for “Cognito domain” (the full domain name will be `https://kongdomain.auth.<aws-region>.amazoncognito.com`).
  • “App client name”: “kongapp”
  • Choose “Generate a client secret”
  • The URL field is not used for Client Credentials Grant so you could type anyone. Click "Next".

8. On the “Review and create” page accept all settings and click on “Create user pool”.

9. You should get redirected to the “User pools” page. Click on the "kongpool" user pool link you’ve just created.

10. Take note of the “User pool ID” (e.g. us-west-1_xyz)

11. Click on “App integration” tab.

12. On “Resource servers” section click on “Create resource server”.

13. On the “Create resource server” page

  • Type “kongresource” for both “Resource server name” and “Resource server identifier” fields.
  • Click on “Add custom scope”.
  • Type “scope1” for both “Scope name” and “Description” fields.
  • Click on “Create resource server”

14. On “App clients and analytics” section click on the “kongapp” link.

  • Take note of both “client id” and “client secret”
  • On “Hosted UI” section click on “Edit”
  • On “OAuth 2.0 grant types” choose “Client credentials” only
  • On “Custom scopes” choose “kongresource/scope1”
  • Click on “Save changes”

Cognito instance — Amazon CLI

You can create the Cognito User Pool using the CLI instead.

Create the User Pool first

aws cognito-idp create-user-pool --pool-name kongpool --username-attributes "email" --region us-west-1

You can check it with:

% aws cognito-idp list-user-pools --max-results 1 --region us-west-1
{
"UserPools": [
{
"Id": "us-west-1_ljtWqxH4r",
"Name": "kongpool",
"LambdaConfig": {},
"LastModifiedDate": "2023–10–03T10:50:31.399000–03:00",
"CreationDate": "2023–10–03T10:50:31.253000–03:00"
}
]
}

If you want to delete it run:

aws cognito-idp delete-user-pool --user-pool-id "us-west-1_ljtWqxH4r" --region us-west-1

Create a Resource for the Scope

aws cognito-idp create-resource-server --user-pool-id "us-west-1_ljtWqxH4r" --identifier "kongresource" --name "kongresource" --scopes "ScopeName=scope1,ScopeDescription=scope1" --region us-west-1

If you want to delete it:

aws cognito-idp delete-resource-server --user-pool-id "us-west-1_ljtWqxH4r" --identifier "kongresource" --region us-west-1

Create the User Pool Domain

aws cognito-idp create-user-pool-domain --user-pool-id "us-west-1_ljtWqxH4r" --domain "kongdomain" --region us-west-1

The full domain name will be `https://kongdomain.auth.<aws-region>.amazoncognito.com`.

Create the User Pool Client

aws cognito-idp create-user-pool-client \
--user-pool-id "us-west-1_ljtWqxH4r" \
--client-name "kongapp" \
--generate-secret \
--allowed-o-auth-flows-user-pool-client \
--allowed-o-auth-flows "client_credentials" \
--allowed-o-auth-scopes "kongresource/scope1" \
--region us-west-1
{
"UserPoolClient": {
"UserPoolId": "us-west-1_ljtWqxH4r",
"ClientName": "kongapp",
"ClientId": "2lqm8o7or9hfrq0t6eofhqj88i",
"ClientSecret": "157hn35o42chdpk1pnbus9ei31a4g7vrhes1t0t1qbjc1ch6cpl8",
"LastModifiedDate": "2023–10–03T10:53:17.192000–03:00",
"CreationDate": "2023–10–03T10:53:17.192000–03:00",
"RefreshTokenValidity": 30,
"TokenValidityUnits": {},
"AllowedOAuthFlows": [
"client_credentials"
],
"AllowedOAuthScopes": [
"kongresource/scope1"
],
"AllowedOAuthFlowsUserPoolClient": true,
"EnableTokenRevocation": true,
"EnablePropagateAdditionalUserContextData": false,
"AuthSessionValidity": 3
}
}

If you want to delete it:

aws cognito-idp delete-user-pool-client --user-pool-id "us-west-1_ljtWqxH4r" --client-id "16drdhq7vovanujovn8hq5fckc" --region us-west-1

Get the Issuer

To configure the Ingresses we need to know the User Pool Issuer endpoint. The Issuer endpoint follows the structure: `https://cognito-idp.<aws-region>.amazonaws.com/<user-pool-id>/.well-known/openid-configuration`.

You can hit it if you will:

http https://cognito-idp.us-west-1.amazonaws.com/us-west-1_ljtWqxH4r/.well-known/openid-configuration
HTTP/1.1 200 OK
Connection: keep-alive
Content-Length: 717
Content-Type: application/json
Date: Tue, 03 Oct 2023 13:38:10 GMT
x-amzn-RequestId: c598ac0f-ecc8–46af-8bb1–421f74377210
{
"authorization_endpoint": "https://kongdomain.auth.us-west-1.amazoncognito.com/oauth2/authorize",
"id_token_signing_alg_values_supported": [
"RS256"
],
"issuer": "https://cognito-idp.us-west-1.amazonaws.com/us-west-1_wPyXhXSlx",
"jwks_uri": "https://cognito-idp.us-west-1.amazonaws.com/us-west-1_wPyXhXSlx/.well-known/jwks.json",
"response_types_supported": [
"code",
"token"
],
"scopes_supported": [
"openid",
"email",
"phone",
"profile"
],
"subject_types_supported": [
"public"
],
"token_endpoint": "https://kongdomain.auth.us-west-1.amazoncognito.com/oauth2/token",
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post"
],
"userinfo_endpoint": "https://kongdomain.auth.us-west-1.amazoncognito.com/oauth2/userInfo"
}

Create an Ingress

To exercise the grant we’re going to protect an external Kubernetes Service with Client Credentials grant.

To get start, let’s define the Service. It abstracts the public http://httpbin.org echo service.

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: ext-service
namespace: kong
spec:
ports:
- protocol: TCP
port: 80
type: ExternalName
externalName: httpbin.org
EOF

We can expose the Service with an Ingress like this:

cat <<EOF | kubectl apply -f -
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: oidcroute
namespace: kong
annotations:
konghq.com/strip-path: "true"
spec:
ingressClassName: kong
rules:
- http:
paths:
- path: /oidcroute
pathType: ImplementationSpecific
backend:
service:
name: ext-service
port:
number: 80
EOF

You can consume the Ingress sending a request:

% http <_DataPlane_PublicIP_>/oidcroute/get
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 634
Content-Type: application/json
Date: Fri, 01 Sep 2023 13:47:40 GMT
Server: gunicorn/19.9.0
Via: kong/3.4.0.0-enterprise-edition
X-Kong-Proxy-Latency: 0
X-Kong-Upstream-Latency: 119

{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Host": "a2b7df4965b6c44009b1068a026994ce-271c73dd9744141c.elb.us-west-1.amazonaws.com",
"User-Agent": "HTTPie/3.2.2",
"X-Amzn-Trace-Id": "Root=1-64f1eb7c-6a79b654364517396c40af5c",
"X-Forwarded-Host": "a2b7df4965b6c44009b1068a026994ce-271c73dd9744141c.elb.us-west-1.amazonaws.com",
"X-Forwarded-Path": "/oidcroute/get",
"X-Forwarded-Prefix": "/oidcroute"
},
"origin": "192.168.46.103, 54.153.31.135",
"url": "http://a2b7df4965b6c44009b1068a026994ce-271c73dd9744141c.elb.us-west-1.amazonaws.com/get"
}

Create a Kong Plugin

Now, let’s protect the Ingress with an OpenID Connect based Authentication process. Declare and submit a KongPlugin like this. Use the user-pool-id you saved previously.

cat <<EOF | kubectl apply -f -
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: oidcplugin
namespace: kong
config:
issuer: "https://cognito-idp.<aws-region>.amazonaws.com/<user-pool-id>/.well-known/openid-configuration"
scopes: [kongresource/scope1]
plugin: openid-connect
EOF

Now, patch the ingress definition with the plugin:

kubectl annotate ingress oidcroute -n kong konghq.com/plugins=oidcplugin

Consume the Ingress

We need to provide a client id and secret to consume the ingress. If we provide wrong credentials we get a specific error:

% http <_DataPlane_PublicIP_>/oidcroute/get -a 1:1
HTTP/1.1 401 Unauthorized
Connection: keep-alive
Content-Length: 26
Content-Type: application/json; charset=utf-8
Date: Fri, 01 Sep 2023 13:51:11 GMT
Server: kong/3.4.0.0-enterprise-edition
WWW-Authenticate: Bearer realm="cognito-idp.us-west-1.amazonaws.com", error="invalid_token"
X-Kong-Response-Latency: 291

{
"message": "Unauthorized"
}

Use the client id and secret, separated by a ":", that Cognito issued previously to send another request. For example:

% http <_DataPlane_PublicIP_>/oidcroute/get -a 73o8h99at7jtnevvn4b60uslb0:1kl6kn1q8cg2snvtuj7achkksrafo2tvstds8fv4m7qiuf0iis5o
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 1527
Content-Type: application/json
Date: Fri, 01 Sep 2023 13:53:38 GMT
Server: gunicorn/19.9.0
Set-Cookie: session=AQAAiS4LP1zveju8uo4-LLK61JAHBVzDKsJYFu0DVRTxKofi7PFkAAAAAAA8BQC5-OyFQ8E_gbN1W9sSVaCWAAAAYrrN0LKvFqKE5GDI12e3pgfbVsfRuu9_1q-OrHZwE1Cggqs-RXbznzcNuRmSzkLbfxdtckEkCv6FWFowP400GNR_U7xxClHrfj3hM8i3bmn8wG-TTqReC0A1XMAjedyYLFik9IZ85BoNulYBLJNBGpEQtjNN14q31avUl46sGROC09MpgZlMPrtRKOQkVfHN8usY2260wZLOlt7zCSFpoDLkIahiMGh1CDD6-6My8NopSLrvhXcJlJ5MBbwMJe-7jSfqBpDrINEnc8mP2eRkgsVll0utqQcy9m2Pm5682g_ke8U66Mu-ClwBUQXyZwJmdTNMN4SbJrvlrRD6fe7W9p5bC1ykmigSpuxpWeSDQa_Yw1lLR0NdpTlFh5Vf6TwNBT8eCc3Xt7R4jv-J7kNJ-GAHy-1tvqb3xHefNHfzpm1q_vr929I54rxF7TUiMKNFzoJBW0zwFKSZxhATpbjsC05zY8iVkNx0kutUeT44suwBV2adLx9U4VJLG0AQAPEOJgvkQvSyMcqIWaLMROngfVC2Y-UDjz9YznI7OsfIA7Aa0s2hrtecjrqzenXy0RtXpmy17v3HvyZmPINRHx1sQiG_97txig5XrA4KFg3corRxY9PDu2ee5hOnb2qyIx_wVfP-aFPeN-sZYpzcV_9g4owlI6kCdVVk3-oamzi5VcUU2aXKu4Kqa98ZbuV0QfnLeSDd85QXbSKE_IydYv52QTi4N5wxvlVrjhSq0xPtQMt5EHGCBtmt8guEILJMv_g2iHvYQasQxrMCL0MtTKCykhxZFG0FEF0ochH9Iu3TecqptXqPo0wloU8nOSGaC2RBDm4wHkNpB6jWc8JnS6lxpfqdACUvgoFgjbBBQvfIaahEyURn4EgXnAXpVfTgMxbxpf_rhX37Mz89LMNkUdENWeUJnnF8e1YOGp3j-Bzl_T_QOKOcPXvlhZfR0SG_wmXDeJZ0LGX7oaClcfhb4f_K8VZsAvXkcPTjAdAUbDAt9Ty1jOYt79EvxZI5m9ABpFZPXlHbUd7-7O9p0EXBpUPeoSL-dgJRhQ9oKgZprRcfRmxjkNBepkq1i2UaY01Q1mwah9OuM7AiAAr4c6mwrfI47G1_BukF_dxPxS2EAak0_-bC_re1K3od1e3VQzBiz3fQhs2fC_3qxQqPPA0PO4fP369s46Z4OZjQOYIY8eHeEFMulf6rNkr_SEjTykIdLlo46QLq9PZncmAM_Auw-DqY--KEYjBgYb9hZGxKFr6l1eq1MFJp6QGvdppdBpsQ6W4siyCaDYkOIQ9jxt2ao46yP6VU-rcefYm3HNGOZoVnTPK-_beGLoTAItvc5pXJAEXR7VLkv5DietBMBX-XlR; Path=/; SameSite=Lax; HttpOnly
Via: kong/3.4.0.0-enterprise-edition
X-Kong-Proxy-Latency: 231
X-Kong-Upstream-Latency: 119

{
"args": {},
"headers": {
"Accept": "*/*",
"Accept-Encoding": "gzip, deflate",
"Authorization": "Bearer eyJraWQiOiJjcWRjZHZISEZxS2VxRUdicndCVThaRmkyUGhZVythSEtuT0pqSVJzXC8yYz0iLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiI3M284aDk5YXQ3anRuZXZ2bjRiNjB1c2xiMCIsInRva2VuX3VzZSI6ImFjY2VzcyIsInNjb3BlIjoia29uZ3Jlc291cmNlXC9zY29wZTEiLCJhdXRoX3RpbWUiOjE2OTM1NzY0MTgsImlzcyI6Imh0dHBzOlwvXC9jb2duaXRvLWlkcC51cy13ZXN0LTEuYW1hem9uYXdzLmNvbVwvdXMtd2VzdC0xX2JoZ2lKbGtRdSIsImV4cCI6MTY5MzU4MDAxOCwiaWF0IjoxNjkzNTc2NDE4LCJ2ZXJzaW9uIjoyLCJqdGkiOiJlZDY1NjIwMC0wYTAzLTQyYjQtOWYxYy00ZDdkMzViOWRmZjciLCJjbGllbnRfaWQiOiI3M284aDk5YXQ3anRuZXZ2bjRiNjB1c2xiMCJ9.T0C3KxLbSg4PAEZ3-9VyLuRZO-7hfGPOyu_Zm6iYiqIR73yg7ZRUKgsfPgI3qCaxSIKGmA0v21co-dEl0MfV9Z4CaEr6BAMW8v20wDQkPgfHWpG4Q4X9MsBG09r36CSGRsHZNLo7StCZxMqek-Cb7IEAxV9emVhBG6GM9Wy-HreZdxdKr3jBFL_SUlReDporg0e4KS0EI3Z2HsGGj_ZsUuwdICa2evTuXtrvlXCHuKn6YX5QtPxh9zV-cQ4UgicIJH5Kah-sbuPgRSZcaSvyXgNYT9lnJTd85siDkAPL4M31_xB-Iyfs0_zJm9FfoqvhN162ap0YV4pyj4Hgukse_g",
"Host": "a2b7df4965b6c44009b1068a026994ce-271c73dd9744141c.elb.us-west-1.amazonaws.com",
"User-Agent": "HTTPie/3.2.2",
"X-Amzn-Trace-Id": "Root=1-64f1ece2-6182013174a01583682b976f",
"X-Forwarded-Host": "a2b7df4965b6c44009b1068a026994ce-271c73dd9744141c.elb.us-west-1.amazonaws.com",
"X-Forwarded-Path": "/oidcroute/get",
"X-Forwarded-Prefix": "/oidcroute"
},
"origin": "192.168.46.103, 54.153.31.135",
"url": "http://a2b7df4965b6c44009b1068a026994ce-271c73dd9744141c.elb.us-west-1.amazonaws.com/get"
}

Authorization Code

The Authorization Code grant is used by web and native apps to authenticate user. In order to allow users to provide their credentials (userid/pwd, OTP, etc.), the grant should provide integration with the IdP UI.

Cognito instance — Amazon Console

Just like we did for the Client Credentials grant, the following process describes how to create a minimal Amazon Cognito User Pool for the Authorization Code Grant. Check the Amazon Cognito documentation to learn more.

  1. Go to the Amazon Cognito console. If prompted, enter your AWS credentials.
  2. Delete the existing "kongpool" user pool.
  3. Choose “Create user pool”.
  4. On the “Configure sign-in experience” page, choose “Email” for “Cognito user pool sign-in options”. Click "Next".
  5. On the “Configure security requirements” page, choose “No MFA” for “MFA enforcement”. Click "Next".
  6. On the “Configure sign-up experience” page, accept all default settings and click "Next".
  7. On the “Configure message delivery” page, choose “Send email with Cognito” for “Email provider”. Click "Next".
  8. On the “Integrate your app” page, choose
  • “User pool name”: “kongpool”
  • Choose “Use the Cognito Hosted UI” for “Hosted authentication pages”.
  • Choose “Use a Cognito domain” for “Domain type”.
  • Type “https://kongdomain" for “Cognito domain” (the full domain name will be `https://kongdomain.auth.<aws-region>.amazoncognito.com`).
  • “App client name”: “kongapp”
  • Choose “Generate a client secret”
  • The URL field in “Allowed callback URLs” section is used by Cognito to control the incoming URL where the Authentication process got started. After authentication, Cognito redirects the use to this URL. This should be set with the application’s URL where an authenticated user is required. In order case, since we’re going to protect the same ingress, it should be set as `https://<_DataPlane_PublicIP_>/oidcroute/get`. Note that Cognito requires a https based URL.

8. Click on “Advanced app client settings”. The two main settings are:

  • “OAuth 2.0 grant types”: it’s already set to support the Authorization Code Grant.
  • “OpenID Connect scopes”: it’s also already set with “OpenID” scope, required by the grant.
  • Accept all settings and click "Next".

9. On the “Review and create” page accept all settings and click on “Create user pool”.

10. You should get redirected to the “User pools” page. Click on the user pool link you’ve just created.

11. Take note of the “User pool ID” (e.g. us-west-1_xyz)

Cognito instance — Amazon CLI

Again, you can use the CLI to create both User and Identity Pools:

Cognito User Pool

From a local terminal, start creating the User Pool with standard settings. Please check the Amazon Cognito User Pool documentation to learn more.

aws cognito-idp create-user-pool \
--pool-name kongpool \
--username-attributes "email" \
--email-configuration=EmailSendingAccount=COGNITO_DEFAULT \
--auto-verified-attributes "email" \
--user-attribute-update-settings=AttributesRequireVerificationBeforeUpdate=email \
--region us-west-1

Retrieve the User Pool Id with:

aws cognito-idp list-user-pools --max-results 1 --region us-west-1 | jq -r ".UserPools[].Id"
us-west-1_x5o33sKzx

Create User Pool Domain

Next, we have to define a domain for our User Pool:

aws cognito-idp create-user-pool-domain --user-pool-id us-west-1_x5o33sKzx --domain kongdomain --region us-west-1

Note that the full domain name will be `https://kongdomain.auth.<aws-region>.amazoncognito.com`

Create User Pool Client

Last, we create our first User Pool Client with “kongapp” as client name. Cognito will then create a ClientId and ClientSecret for us.

When the Cognito User Pool is available we need to login to it and get the tokens. The Callback URL parameter refers to the URL our application is use to handle the OAuth Grant, "https://<_DataPlane_PublicIP_>/oidcroute/get".

aws cognito-idp create-user-pool-client \
--user-pool-id us-west-1_x5o33sKzx \
--client-name kongapp \
--generate-secret \
--allowed-o-auth-flows-user-pool-client \
--allowed-o-auth-flows "code" \
--allowed-o-auth-scopes "openid" \
--callback-urls "https://<_DataPlane_PublicIP_>/oidcroute/get" \
--supported-identity-provider "COGNITO" \
--region us-west-1

You can retrieve both “Client ID” and “Client Secret” with:

% aws cognito-idp list-user-pool-clients --user-pool-id us-west-1_x5o33sKzx --region us-west-1 | jq -r ".UserPoolClients[].ClientId"
6bq9e2sofich25hsps3bvbp2tp
% aws cognito-idp describe-user-pool-client --user-pool-id us-west-1_x5o33sKzx --client-id 6bq9e2sofich25hsps3bvbp2tp --region us-west-1 | jq -r ".UserPoolClient.ClientSecret"
1iufdqtqv5atr5b54ns5i3c72nto9a43qk6grqurb6gijermeot0

Cognito Identity Pool creation

Now we need to create the Cognito Identity Pool. The User Pool Id and ClientId parameters are used to connect both Pools. Please check the Amazon Cognito Identity Pool documentation to learn more.

aws cognito-identity create-identity-pool \
--identity-pool-name kongidpool \
--no-allow-unauthenticated-identities \
--allow-classic-flow \
--cognito-identity-providers=ProviderName=cognito-idp.us-west-1.amazonaws.com/us-west-1_x5o33sKzx,ClientId=6bq9e2sofich25hsps3bvbp2tp,ServerSideTokenCheck=false \
--region us-west-1

Retrieve the Identity Pool Id with:

$ aws cognito-identity list-identity-pools --max-results 1 --region us-west-1 | jq -r ".IdentityPools[].IdentityPoolId"
us-west-1:efb34068–5fa4–48c7-b422–13efcd48e2a4

Create a Kong Plugin

Now, we’re going to protect the same Ingress with the Authorization Code Grant. You can delete the current Kong Plugin with:

kubectl delete kongplugin oidcplugin -n kong

You can remove the annotation from the ingress with:

kubectl annotate ingress oidcroute -n kong konghq.com/plugins-

The new Kong Plugin should be similar to this:

cat <<EOF | kubectl apply -f -
apiVersion: configuration.konghq.com/v1
kind: KongPlugin
metadata:
name: oidcplugin
namespace: kong
config:
client_id: [<your client id>]
client_secret: [<your client secret]
issuer: "https://cognito-idp.<aws-region>.amazonaws.com/<user-pool-id>/.well-known/openid-configuration"
redirect_uri: ["https://<_DataPlane_PubliIP_>/oidcroute/get"]
plugin: openid-connect
EOF

Now, patch the ingress definition with the plugin:

kubectl patch ingress oidcroute -n kong -p '{"metadata":{"annotations":{"konghq.com/plugins":"oidcplugin"}}}'

Consume the Ingress

Redirect your browser to the URL you defined in the ingress your protecting with OpenID Connect plugin. For example:

https://<_DataPlane_PublicIP_>/oidcroute/get

Since you don’t have any token in your request, Kong Data Plane redirects you to the Amazon Cognito UI:

Click on `Sign Up` to register as a Cognito user:

You will receive an email with confirmation code. Enter it in the next page:

After registration and authentication, Cognito redirects you back to Kong Data Plane with the Authorization Code injected. Kong Data Plane connects to Cognito, using Client Credentials Grant to exchange the Authorization Code with the actual Access Token. The request is routed to the Upstream based on `http://httpbin.org` public echo system which return all data related to the request. You can see the Access Token’s been injected by the Data Plane.

Further Reading

How OAuth2 Authorization Works: Kong API Gateway 4 Step Tutorial

--

--