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.
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.
Read more
- Learn more about client scopes and Keycloak.
Make sure to disable the 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.
Second, enable JWT in the API authentication.
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 |
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.
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.
Select the Authorize button on the right.
Paste your public token into the Value field and select Authorize.
To send requests using the Swagger UI, choose an API endpoint and select Try it out.
Select Execute to send requests.