I’ve been trying a few IDPs and okta seems to have the most features and the easiest to use. Developers only need to register an account and start using it. Okta also supports private_key_jwt
which is a huge plus compared to AzureAD or Auth0. Let me show you how to use it.
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
I highly recommend reading this article to understand how client authentication works.
Create testing Service and Route We need a service and route for our testing.
Create service I am using httpbin
as upstream server.
1 2 3 4 curl -X POST \ --url "http://<ADMIN_API>/services" \ --data "name=oidc-demo" \ --data "url=http://httpbin.org/anything"
Create Route Next we will create a path /demo
to access our service.
1 2 3 curl -X POST \ --url "http://<ADMIN_API>/services/oidc-demo/routes" \ --data 'paths[]=/demo'
Use Kong JWK Okta’s website says both RSA and Elliptic Curve (EC) keys are supported. We can use below command to filter RSA and EC public key from Kong’s JWK.
1 curl -s http://<ADMIN_API>/openid-connect/jwks | jq '.keys |= map(select(.kty=="RSA" or .kty=="EC"))'
For this demo, I will only use the public keys that has signature algorithm as ES256
.
1 curl -s http://<ADMIN_API>/openid-connect/jwks | jq '.keys |= map(select(.alg=="ES256"))'
We should get something similar as below.
1 2 3 4 5 6 7 8 9 10 11 12 13 { "keys" : [ { "use" : "sig" , "y" : "taKDhagu38pcK9acgqZ5kwuwE8LssSXYDH2cq9tMEJo" , "crv" : "P-256" , "kid" : "h3hlm0AXuFS8frAb817kosAy947lGWHV287C5x1IoWs" , "kty" : "EC" , "alg" : "ES256" , "x" : "h5nkM_J7s4NW8YJUnvrt_o3ZOI1geu0n0xBKa6-1d7c" } ] }
Create Okta client We will use public key we got above to create this Okta client, below is a sample call.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 curl --request POST \ --url https://<OKTA_ADMIN_DOMAIN>/oauth2/v1/clients \ --header 'Accept: application/json' \ --header 'Authorization: SSWS <OKTA_API_TOKEN>' \ --header 'Content-Type: application/json' \ --data '{ "client_name": "client secret", "response_types": [], "grant_types": [], "token_endpoint_auth_method": "private_key_jwt", "application_type": "native", "redirect_uris": [], "jwks": { "keys": [ { "use": "sig", "y": "taKDhagu38pcK9acgqZ5kwuwE8LssSXYDH2cq9tMEJo", "crv": "P-256", "kid": "h3hlm0AXuFS8frAb817kosAy947lGWHV287C5x1IoWs", "kty": "EC", "alg": "ES256", "x": "h5nkM_J7s4NW8YJUnvrt_o3ZOI1geu0n0xBKa6-1d7c" } ] } }'
For more information about creating client with private_key_jwt
, please check okta site .
Please don’t forget to assign this new application to your users, otherwise you will get 401 errors.
Enable OIDC plugin 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 curl --request POST \ --url https://<ADMIN_API>/plugins \ --header 'Content-Type: application/json' \ --data '{ "name": "openid-connect", "config": { "issuer": "https://<OKTA_DOMAIN>/oauth2/default", "client_id": [ "<CLIENT_ID>" ], "auth_methods": [ "password" ], "client_auth": [ "private_key_jwt" ], "client_alg": [ "ES256" ] } }'
Now we can visit the protected route.
1 2 3 curl --request GET \ --url https://<PROXY_NODE>/demo/anything \ --header 'Authorization: Basic base64(username:password)'
Use self generate keys If we want to create our own (IMO this is a better idea) JWKs, we can use tools like openssl. I decided to use https://mkjwk.org
for demonstration purpose. You can build your own CLI tool to use it locally. Let’s say the keys that we got today is below:
Full key (Include Private and public key):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 { "keys" : [ { "kty" : "EC" , "d" : "4PbzHut5BBd0v3xJ4-fHh1HDw4AmnHlhre6IgJir4CY" , "use" : "sig" , "crv" : "P-256" , "kid" : "Z85ape3mBdhnq4In7vkpHtGN2ijDTFNOlk_eYoxteT8" , "x" : "CvDq_1z-VrkVm3EbY1abXbI8cCRRVc6_31h4Xc4bFIk" , "y" : "-WoxLtT2M5nlHKj2kr7F1z-Yu8usTTfVGwJHX03VC4U" , "alg" : "ES256" } ] }
Public key:
1 2 3 4 5 6 7 8 9 10 11 12 13 { "keys" : [ { "kty" : "EC" , "use" : "sig" , "crv" : "P-256" , "kid" : "Z85ape3mBdhnq4In7vkpHtGN2ijDTFNOlk_eYoxteT8" , "x" : "CvDq_1z-VrkVm3EbY1abXbI8cCRRVc6_31h4Xc4bFIk" , "y" : "-WoxLtT2M5nlHKj2kr7F1z-Yu8usTTfVGwJHX03VC4U" , "alg" : "ES256" } ] }
Create Okta client We will use public key we generated above to create this Okta client, below is a sample call.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 curl --request POST \ --url https://<OKTA_ADMIN_DOMAIN>/oauth2/v1/clients \ --header 'Accept: application/json' \ --header 'Authorization: SSWS <OKTA_API_TOKEN>' \ --header 'Content-Type: application/json' \ --data '{ "client_name": "client secret", "response_types": [], "grant_types": [], "token_endpoint_auth_method": "private_key_jwt", "application_type": "native", "redirect_uris": [], "jwks": { "keys": [ { "kty": "EC", "use": "sig", "crv": "P-256", "kid": "Z85ape3mBdhnq4In7vkpHtGN2ijDTFNOlk_eYoxteT8", "x": "CvDq_1z-VrkVm3EbY1abXbI8cCRRVc6_31h4Xc4bFIk", "y": "-WoxLtT2M5nlHKj2kr7F1z-Yu8usTTfVGwJHX03VC4U", "alg": "ES256" } ] } }'
For more information about creating client with private_key_jwt
, please check okta site .
Please don’t forget to assign this new application to your users, otherwise you will get 401 errors.
Enable OIDC plugin The last step is to enable OIDC Plugin.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 curl --request POST \ --url https://<ADMIN_API>/plugins \ --header 'Content-Type: application/json' \ --data '{ "name": "openid-connect", "config": { "issuer": "https://<OKTA_DOMAIN>/oauth2/default", "client_id": [ "<CLIENT_ID>" ], "auth_methods": [ "password" ], "client_auth": [ "private_key_jwt" ], "client_jwk": [ { "kty": "EC", "d": "4PbzHut5BBd0v3xJ4-fHh1HDw4AmnHlhre6IgJir4CY", "use": "sig", "crv": "P-256", "kid": "Z85ape3mBdhnq4In7vkpHtGN2ijDTFNOlk_eYoxteT8", "x": "CvDq_1z-VrkVm3EbY1abXbI8cCRRVc6_31h4Xc4bFIk", "y": "-WoxLtT2M5nlHKj2kr7F1z-Yu8usTTfVGwJHX03VC4U", "alg": "ES256" } ], "client_alg": [ "ES256" ] } }'
Now we can visit the protected route.
1 2 3 curl --request GET \ --url https://<PROXY_NODE>/demo/anything \ --header 'Authorization: Basic base64(username:password)'