Skip to content

Keycloak

This page explains using Keycloak to validate JWTs (JSON Web Tokens).


Good to know

If you switch from the default configuration to JSON Web Tokens, all API keys generated in the API Portal will be turned off.

Before you begin

Before getting started, make sure to read our overview about JWTs.

Please ensure you have the following:

  • A Keycloak instance with all the required permissions
  • A Keycloak realm
  • Admin permissions for the Traefik Hub workspace
  • jq
  • curl

Client configuration

In the first step, select the realm and select Create client to create a new Keycloak client.

The example uses the realm keycloak-demo and the name of the new client will be new-client.

Choose realm and create a new client

Choose realm and create a new client

Create new client scope

Configure the client

Create a client scope

A client scope is a way to limit the roles that get declared inside an access token. When a client requests that a user be authenticated, the access token they receive back will only contain the role mappings you’ve explicitly specified for the client’s scope.

This allows you to limit the permissions each individual access token has, rather than giving the client access to all the user’s permissions.

Make sure to disable the full group path setting.

Disable full group path setting

Disable full group path setting

Validate your JWT against Keycloak

Use curl to generate a JWT and validate it against your Keycloak instance.

The following example uses the password grant type as a validation method.

 curl -s \
--data-urlencode 'username=jane.doe' \
--data-urlencode 'password=&MY-SECURE-PASSWORD' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=jwt-demo' \
https://$KEYCLOAK-URL/realms/keycloak-demo/protocol/openid-connect/token
{"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIxX3lOSk8zb1d1dUptaDNEZFYyM3hubmV4WlprVXJrZ3h6Vl92b0NJR1FrIn0.eyJleHAiOjE2OTk1MTg4ODUsImlhdCI6MTY5OTUxODU4NSwianRpIjoiZjUxMWU4ZmYtMjA2MC00NTAwLTgzNDMtNTE5M2E5MjNlNmQ1IiwiaXNzIjoiaHR0cHM6Ly9rZXljbG9hay50cmFlZmlrbGFicy50ZWNoL3JlYWxtcy9rZXljbG9hay1kZW1vIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjM0MTYwNWIyLWU0NGItNGNkMC1hNDZjLWEwNjMzNWFhYTVkYiIsInR5cCI6IkJlYXJlciIsImF6cCI6Imp3dC1kZW1vIiwic2Vzc2lvbl9zdGF0ZSI6ImRlNTgyNzg4LWFhN2UtNDIzNy1iZTg1LWIzN2IxYzRmMDI1MSIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiLyoiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMta2V5Y2xvYWstZGVtbyIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Imdyb3VwcyBlbWFpbCBwcm9maWxlIHVzZXJfaWQiLCJzaWQiOiJkZTU4Mjc4OC1hYTdlLTQyMzctYmU4NS1iMzdiMWM0ZjAyNTEiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6IkphbmUgRG9lIiwiZ3JvdXBzIjpbInN1cHBvcnQiXSwicHJlZmVycmVkX3VzZXJuYW1lIjoiamFuZS5kb2UiLCJnaXZlbl9uYW1lIjoiSmFuZSIsImZhbWlseV9uYW1lIjoiRG9lIiwiZW1haWwiOiJqYW5lLmRvZUB0cmFlZmlrLWFpcmVsaW5lcy5pbyJ9.wSjYSHndQ6vX9FLUif5VuyYRfSPBOGGGamN0op-jH-qA1muPvrcBuZ0r34dE18KC_LhjgB7Y_8dCQBFUjlxLuneGQPbzNBlSL-yIlBAu2pTSti1byHqnb7EFQqW9kPXHDKZTSJtoNnYFsd9vLaAs6qx14Z0E10Tjxp8kQqzyCZ_LHkPo9hN7odxLIVth4Mwq33v4OxV_Qx3GX25960s8hUbcwOypRfQQUGGZDGAtnCScQiYKh_sLqWm3EOMaz0eJ7fc2vSywDY_mHyCVti_IFYJtypNUFP15PcD-zaP4xhjsMh5enLbGTwvwsT45ckGTrSCzqR4PNDddWltttRIaEQ","expires_in":300,"refresh_expires_in":1800,"refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI4Njc0MWNmYy01Y2VjLTQwYjctOTdjNS0wMzZmODhiZTQxODUifQ.eyJleHAiOjE2OTk1MjAzODUsImlhdCI6MTY5OTUxODU4NSwianRpIjoiMzJjNjA5MWEtYzVlOC00NmNlLWJkYmMtMDNlZWZlY2NkOTQ2IiwiaXNzIjoiaHR0cHM6Ly9rZXljbG9hay50cmFlZmlrbGFicy50ZWNoL3JlYWxtcy9rZXljbG9hay1kZW1vIiwiYXVkIjoiaHR0cHM6Ly9rZXljbG9hay50cmFlZmlrbGFicy50ZWNoL3JlYWxtcy9rZXljbG9hay1kZW1vIiwic3ViIjoiMzQxNjA1YjItZTQ0Yi00Y2QwLWE0NmMtYTA2MzM1YWFhNWRiIiwidHlwIjoiUmVmcmVzaCIsImF6cCI6Imp3dC1kZW1vIiwic2Vzc2lvbl9zdGF0ZSI6ImRlNTgyNzg4LWFhN2UtNDIzNy1iZTg1LWIzN2IxYzRmMDI1MSIsInNjb3BlIjoiZ3JvdXBzIGVtYWlsIHByb2ZpbGUgdXNlcl9pZCIsInNpZCI6ImRlNTgyNzg4LWFhN2UtNDIzNy1iZTg1LWIzN2IxYzRmMDI1MSJ9.tmN06kXt2dSEaS1_x-XGCYcBBNLQNEKu01GTmCIHmVA","token_type":"Bearer","not-before-policy":0,"session_state":"de582788-aa7e-4237-be85-b37b1c4f0251","scope":"groups email profile user_id"}

Enable JWT in Traefik Hub

If you switch from the default configuration to JWT, all API keys, generated in the API Portal will be turned off.

First, select Auth settings to go to the authentication and authorization overview.

Select Auth settings

Select Auth settings

Second, enable JWT in the API authentication.

Enable JWT

Enable JWT

Third, configure the JWT key validation method and claims.

Choose one of the following key validation techniques for your JWT.

Method Description
signingSecret The signingSecret option can be set to the secret used for signing the JWT certificates.
publicKey The publicKey option can be used as an alternative to a signing secret to verify incoming requests.
In that case, users should sign their token using a private key, and the public key can be used to verify the signature.
jwksFile The jwksFile option can be used to define a set of JWK to be used to verify the signature of JWTs.
jwksUrl The jwksUrl option can be used as an alternative to a signing secret to verify incoming requests.
It is the URL of the host serving a JWK set.
This option can either be set to a full URL, for example https://www.googleapis.com/oauth2/v3/certs.

The following configuration example will use jwksUrl to validate JWT against a Keycloak instance, using the claims groups and sub.

Setting Value Example
Token validation method JWKs URL https://$KEYCLOAK-URL/realms/keycloak-demo/protocol/openid-connect/certs
Claims configuration Groups groups
Claims configuration User ID sub

JWT configuration

JWT configuration

Once you're done, select Save.

If you want to strip the authorization header or add forward header(s), you can do that under Additional configuration.
These settings are not required.

Configure the JWT validation and claims

Configure additional settings

Once you're done, select Save.


Swagger UI

In the last part, you will configure JWT for authorization in the Swagger UI.

First, generate a fresh JWT and copy the public access token.

Run the following curl command to get your access token.

curl -s \
--data-urlencode 'username=jane.doe' \
--data-urlencode 'password=&MY-SECURE-PASSWORD' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=jwt-demo' \
https://$KEYCLOAK-URL/realms/keycloak-demo/protocol/openid-connect/token | jq .access_token
"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIxX3lOSk8zb1d1dUptaDNEZFYyM3hubmV4WlprVXJrZ3h6Vl92b0NJR1FrIn0.eyJleHAiOjE2OTk1MTg5NDYsImlhdCI6MTY5OTUxODY0NiwianRpIjoiN2U5ZTUxYjQtN2M5Ny00MjdiLWIyZWMtYzA1NGIyMGFiMDMwIiwiaXNzIjoiaHR0cHM6Ly9rZXljbG9hay50cmFlZmlrbGFicy50ZWNoL3JlYWxtcy9rZXljbG9hay1kZW1vIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjM0MTYwNWIyLWU0NGItNGNkMC1hNDZjLWEwNjMzNWFhYTVkYiIsInR5cCI6IkJlYXJlciIsImF6cCI6Imp3dC1kZW1vIiwic2Vzc2lvbl9zdGF0ZSI6IjU2MDQwZmE3LTJiZjQtNGRhZi05YjhhLWEzZjRmZjhhZWM3MSIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiLyoiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMta2V5Y2xvYWstZGVtbyIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Imdyb3VwcyBlbWFpbCBwcm9maWxlIHVzZXJfaWQiLCJzaWQiOiI1NjA0MGZhNy0yYmY0LTRkYWYtOWI4YS1hM2Y0ZmY4YWVjNzEiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6IkphbmUgRG9lIiwiZ3JvdXBzIjpbInN1cHBvcnQiXSwicHJlZmVycmVkX3VzZXJuYW1lIjoiamFuZS5kb2UiLCJnaXZlbl9uYW1lIjoiSmFuZSIsImZhbWlseV9uYW1lIjoiRG9lIiwiZW1haWwiOiJqYW5lLmRvZUB0cmFlZmlrLWFpcmVsaW5lcy5pbyJ9.lBEmAFs-fk7xhCjgMZWMDFneuNAuZUmLUy7kW-9d9UdqdXOT2E0WLPOPwgAI5OqGSV3PKXl4UGAFid21h_hbnD1ObDHLC5hTMMk9Lw6B8SoSwP_qaawJVc8ZS-xKNsj72hVRbwiIjONY85ooZXD5RaEhUr6-YSCbutdWX6japhEaEeFYpywlyEBruOpEgoYUqB_TsZdYsHssVTp5nBGjX8t6VCLCVPqIaAt6Wd0Fm4flTEw_EnjImXJpuscgiySafH51JMcXkNd9eTUmzSKz9rD2h78Hgc2tZm5Gp-CerhobF6H-JTdXa4NgmFP6UkmCQ6iLFRdvjKrc1R_9O1m3zw"

Second, copy the access token, this is everything between "".

eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIxX3lOSk8zb1d1dUptaDNEZFYyM3hubmV4WlprVXJrZ3h6Vl92b0NJR1FrIn0.eyJleHAiOjE2OTk1MTg5NDYsImlhdCI6MTY5OTUxODY0NiwianRpIjoiN2U5ZTUxYjQtN2M5Ny00MjdiLWIyZWMtYzA1NGIyMGFiMDMwIiwiaXNzIjoiaHR0cHM6Ly9rZXljbG9hay50cmFlZmlrbGFicy50ZWNoL3JlYWxtcy9rZXljbG9hay1kZW1vIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjM0MTYwNWIyLWU0NGItNGNkMC1hNDZjLWEwNjMzNWFhYTVkYiIsInR5cCI6IkJlYXJlciIsImF6cCI6Imp3dC1kZW1vIiwic2Vzc2lvbl9zdGF0ZSI6IjU2MDQwZmE3LTJiZjQtNGRhZi05YjhhLWEzZjRmZjhhZWM3MSIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiLyoiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbImRlZmF1bHQtcm9sZXMta2V5Y2xvYWstZGVtbyIsIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Imdyb3VwcyBlbWFpbCBwcm9maWxlIHVzZXJfaWQiLCJzaWQiOiI1NjA0MGZhNy0yYmY0LTRkYWYtOWI4YS1hM2Y0ZmY4YWVjNzEiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwibmFtZSI6IkphbmUgRG9lIiwiZ3JvdXBzIjpbInN1cHBvcnQiXSwicHJlZmVycmVkX3VzZXJuYW1lIjoiamFuZS5kb2UiLCJnaXZlbl9uYW1lIjoiSmFuZSIsImZhbWlseV9uYW1lIjoiRG9lIiwiZW1haWwiOiJqYW5lLmRvZUB0cmFlZmlrLWFpcmVsaW5lcy5pbyJ9.lBEmAFs-fk7xhCjgMZWMDFneuNAuZUmLUy7kW-9d9UdqdXOT2E0WLPOPwgAI5OqGSV3PKXl4UGAFid21h_hbnD1ObDHLC5hTMMk9Lw6B8SoSwP_qaawJVc8ZS-xKNsj72hVRbwiIjONY85ooZXD5RaEhUr6-YSCbutdWX6japhEaEeFYpywlyEBruOpEgoYUqB_TsZdYsHssVTp5nBGjX8t6VCLCVPqIaAt6Wd0Fm4flTEw_EnjImXJpuscgiySafH51JMcXkNd9eTUmzSKz9rD2h78Hgc2tZm5Gp-CerhobF6H-JTdXa4NgmFP6UkmCQ6iLFRdvjKrc1R_9O1m3zw

Third, browse to an API Portal and select an API, this example uses the Flight API.

Flight API

Flight API

Select the Authorize button on the right.

Paste your public token into the Value field and select Authorize.

Paste the token

Paste the token

To send requests using the Swagger UI, choose an API endpoint and select Try it out.

Select Try it out

Select Try it out

Select Execute to send requests.

Send requests through the Swagger UI

Send requests through the Swagger UI