Introduction to KeyCloak's OAuth 2.0 functionalities.

Quang PhanQuang Phan
10 min read

In my previous article, we talked about how we can utilize KeyCloak as an IAM solution to protect our API endpoints. In this blog, we will explore some of the fundamental functionalities of KeyCloak such as:

  • Client Credential Grant

  • Password Grant

  • Refresh Token & Rotation

  • Introspect Endpoint

  • Token Revocation

  • Key Rotation

These features are essential for managing authentication and authorization in modern applications. I will go through each in term of how you can set it up via KeyCloak Admin UI and how to test each using Postman.

I assume that you already have KeyCloak running in your local environment. If you do not, please refer to my previous blog. It shows you the installation steps as well as the initial step to create your KeyCloak realm.

KeyCloak also has an OpenId endpoints where if you simple perform a GET request to http://localhost:8080/realms/MyRealm/.well-known/openid-configuration it will display a lot of useful information.

{
    "issuer": "http://localhost:8080/realms/MyRealm",
    "authorization_endpoint": "http://localhost:8080/realms/MyRealm/protocol/openid-connect/auth",
    "token_endpoint": "http://localhost:8080/realms/MyRealm/protocol/openid-connect/token",
    "introspection_endpoint": "http://localhost:8080/realms/MyRealm/protocol/openid-connect/token/introspect",
    "userinfo_endpoint": "http://localhost:8080/realms/MyRealm/protocol/openid-connect/userinfo",
    "end_session_endpoint": "http://localhost:8080/realms/MyRealm/protocol/openid-connect/logout",
    "frontchannel_logout_session_supported": true,
    "frontchannel_logout_supported": true,
    "jwks_uri": "http://localhost:8080/realms/MyRealm/protocol/openid-connect/certs",
    "check_session_iframe": "http://localhost:8080/realms/MyRealm/protocol/openid-connect/login-status-iframe.html"
}

We will be using some of these to initiate the token request with KeyCloak.

Client Credential Grant

The Client Credential Grant is a flow in OAuth 2.0 that allows a client (such as a backend service) to authenticate itself directly with the authorization server and obtain an access token. This flow is typically used for machine-to-machine (M2M) communication where the client is acting on its own behalf, rather than on behalf of a user.

  1. On left hand side, click Clients and click on Create client button.

  2. Create a new client with a unique client id private-test-client with the client type OpenID Connect and click Next.

  3. Set Client authentication to ON, uncheck Direct access grants and check Service accounts roles. Direct access grants is just another word for the authorization code grant access which we are not interested in now. Service accounts roles is another name for the Client Credential Grant. Click Next & Save

  4. You should be in the client setting page, click on Credentials tab and you should see the Client Secret, copy it so that you can use in your Postman test.

  5. You can create a POST request using the following information:

     curl -X POST "http://localhost:8080/realms/MyRealm/protocol/openid-connect/token" \
          -H "Content-Type: application/x-www-form-urlencoded" \
          -d "grant_type=client_credentials" \
          -d "scope=openid" \
          -d "client_id=private-test-client" \
          -d "client_secret=TPcxZK4nT9wgUU5w1hRhuidZOGNORQJ5"
    

    This is my equivalent setup in Postman.

    If things run smoothly, congratulations! You have just obtained your first JWT token from Keycloak in the Postman response. The JSON content of the response should look similar to the example below.

{
    "access_token": "eyJhbGc.....",
    "expires_in": 300,
    "refresh_expires_in": 0,
    "token_type": "Bearer",
    "id_token": "eyJhbGciOiJSUzI1NiI....",
    "not-before-policy": 0,
    "scope": "openid "
}

Password Grant

The Password Grant flow allows a client to obtain an access token by directly submitting the user's credentials (username and password) to the authorization server. This flow is suitable for trusted applications, such as mobile apps or web applications, where the client can securely handle the user's credentials.

  1. On left hand side, click Users and click on Add user button.

  2. Give a unique user name test123 and fill out the rest of the fields and click Create

  3. On the user details screen, click on Credentials tab and set a password for this user.

  4. Next we will create a client, click on Clients on the left menu and select Create to add a new client.

  5. Put password-grant-client as the Client ID and openid-connect as the Client Protocol and click Next.

  6. Set Client authentication to ON, check Standard flow and Direct access grants. Click Next and Save

  7. On the Client details screen, click on Credentials tab, here you will see the client secret. This secret is used by the client to authenticate with the KeyCloak server.

  8. You can create a POST request using the following information:

     curl -X POST "http://localhost:8080/realms/MyRealm/protocol/openid-connect/token" \
          -H "Content-Type: application/x-www-form-urlencoded" \
          -d "grant_type=client_credentials" \
          -d "scope=openid" \
          -d "client_id=private-test-client" \
          -d "client_secret=TPcxZK4nT9wgUU5w1hRhuidZOGNORQJ5"
    

    This is my equivalent setup in Postman. As you can see, in this flow we need to provide the Client ID, Client Secret, Username, and Password. Please note that the grant_type dictates the type of request this is.

    The Postman response should be very similar to the response from the Client Credential Grant request. It should at the very least provide you with a JWT token. If you copy and paste this token value into the jwt.io site, you can see the details of the token reflecting the identity you used to request it.

{
  "exp": 1729715788,
  "iat": 1729715488,
  "jti": "c2acfad4-5e7b-4fe7-ac56-5609538eaaaa",
  "iss": "http://localhost:8080/realms/MyRealm",
  "aud": "account",
  "sub": "4d6a6028-4af1-47b0-987a-79d896474120",
  "typ": "Bearer",
  "azp": "password-grant-client",
  "sid": "c5833348-8ff2-4b8c-a535-a599f88d6c86",
  "acr": "1",
  "allowed-origins": [
    "/*"
  ],
  "scope": "openid",
  "email_verified": false,
  "name": "test test",
  "preferred_username": "test123",
  "given_name": "test",
  "family_name": "test",
  "email": "test123@test.com"
}

Refresh Token

A refresh token is a special token that can be used to obtain a new access token without requiring the user to re-authenticate. This is particularly useful for maintaining a seamless user experience by allowing the client to refresh the access token when it expires, without interrupting the user's session. According to the OAuth 2.0 standard, refresh tokens should not be used with the Client Credential Grant, and that’s why Keycloak does not support it for this flow. Both the Authorization Code Grant and Password Grant offer token refresh capabilities. We will use the previous Password Grant setup above.

💡
A machine-machine communication meant to be stateless between requests, unlike a user session (authorization code flow) so the JWT token is MEANT to be short live in a client credential flow for security reason. So instead of refresh, we should request a new token instead of refreshing one.

When you execute the request for a token using the Password Grant, you will see a refresh_token value in the response JSON. In the JSON data, you can see that our JWT token is only valid for the next 5 minutes, but our refresh token is valid for the next 30 minutes.

{
    "access_token": "eyJhbGciOiJSU......",
    "expires_in": 300,
    "refresh_expires_in": 1800,
    "refresh_token": "eyJhbGciOiJIUzUxMiIsInR5cCI....",
    "token_type": "Bearer",
    "id_token": "eyJhbGciOiJSUzI1NiIsIn...",
    "not-before-policy": 0,
    "session_state": "f00e840a-467e-48a6-b2f4-f10d6ce37578",
    "scope": "openid profile smile-scope registration-service-scope email"
}

herefore, when the access token expires, the client uses the refresh token to request a new access token from the authorization server. The authorization server verifies the refresh token and, if valid, issues a new access token (and possibly a new refresh token). This process can be repeated until the refresh token itself expires or is revoked.

curl -X POST "http://localhost:8080/realms/MyRealm/protocol/openid-connect/token" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "grant_type=refresh_token" \
     -d "refresh_token=eyJhbGciOiJIUz........" \
     -d "client_id=password-grant-client" \
     -d "client_secret=j2e9P87Uzo4E9EnLQrWlgN183OMUzpAm"

In my case when making this call from Postman, I receive a new access JWT Token and the same refresh token back with a renew expiration time for the next 30 minutes.

💡
Keycloak has an option that allows you to rotate refresh tokens so that with each refresh token request, you receive a new refresh token value. By implementing refresh token rotation, you can significantly enhance the security and manageability of your authentication system.

Introspect End Point

The introspection endpoint in OAuth 2.0 and OpenID Connect is a special endpoint provided by the authorization server, Keycloak, that allows clients to query the status and details of a token. This endpoint can be used to check whether a token (access token or refresh token) is active, expired, revoked, or otherwise invalid. The URL has the format of http://<keycloak-server>/realms/<realm-name>/protocol/openid-connect/token/introspect.

curl -X POST "http://localhost:8080/realms/MyRealm/protocol/openid-connect/token/introspect" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "token=<your-token>" \
     -d "client_id=<your-client-id>" \
     -d "client_secret=<your-client-secret>"

If the token is expired, revoked or invalid then the response simply show active: false

When the token is valid, you’d see the token details as a response from invoking this end point using your token and the client id and client secret.

Token Revocation

Token Revocation is a mechanism that allows an authorization server to invalidate an access token or refresh token. This is important for security, as it enables the server to revoke tokens that are no longer needed or that have been compromised, ensuring that unauthorized access is prevented. The endpoint URL for this function has the format of http://<keycloak-server>/realms/<realm-name>/protocol/openid-connect/revoke

To test this out, I simply generate a Client Credential Grant tok

en.

When testing this token against the introspective end point of KeyCloak, I can see it is valid because the returns displays the details of the access token:

Next I will attempt to revoke this token using KeyCloak’s revoke endpoint

Upon calling the introspection endpoint, you will see that the token is now invalid even though the access token has not expired yet.

💡
One thing to remember is that to revoke a token, you must supply the same client ID and client secret that were used to generate the token you want to revoke.

Key Rotation

Key Rotation is the process of periodically changing the cryptographic keys used to sign and verify tokens. By regularly rotating keys, the exposure time of any single key is limited, reducing the risk of long-term damage if a key is compromised. This practice ensures that even if a key is compromised, it will soon be replaced, minimizing potential damage.

By default KeyCloak provide 5 default Keys, to see them click on Realm settings and click on Keys tab.

In the context of key rotation and token management, keys can be categorized as active, passive, or disabled. Active keys are the ones currently used to sign new tokens. When a new token is issued, it is signed with an active key, ensuring that the token is valid and can be verified by clients and services.

Passive keys are keys that were previously used to sign tokens but are no longer used for signing new ones. However, they are still kept in the system to verify tokens that were issued while they were active. This means that even after a key is no longer used to sign new tokens, it can still be used to check the validity of older tokens until they expire.

Disabled keys are keys that are no longer used for any purpose. They have been fully retired from the system and are not used to sign new tokens or verify old ones. These keys are typically removed from the system to prevent any accidental use.

During the key rotation process, a new key is generated and set as the active key. The previous active key becomes passive, allowing it to verify existing tokens. Passive keys are kept until all tokens signed with them have expired, at which point they are disabled and removed from the system.

To manage keys, click on Realm Settings, Keys. By default KeyCloak uses rsa-generated key provider. When you send a request such as Client Credential Grant, the token is return, if you put your token into jwt.io, you’d see the Key Id value (kid).

If you look under the Key list, you’d see this value matches against the key with rs-generated provider.

Now, lets assume we want to retire this current key rsa-generated and use a new one calls new-rsa-generated.

  1. Click on Realm settings, click on Keys tab, click on Add providers and select rsa-generated.

  2. Set Active to OFF while leaving Enabled to ON and click Save. This will ensure JWT token signed by this key will still be valid.

  3. Click on Realm settings, click on Keys tab, click on Add providers and click on Add provider button

  4. Under the Add provider window select rsa-generated. Give new-rsa-generated as Name, Priority set to 100 which is the same as the provider we disabled in step 2. The rest of the settings should be as followed

  5. Click back on Keys tab and select Key list, you should see the new provider on the list

So at this point if you invoke KeyCloak token endpoint, your generated token will ahve the new key id which matches against the newly created Key Provider.

At this point, we have two tokens generated: one uses the new and active new-rsa-generated key, while the prior token uses the rsa-generated key, which is in passive mode but still enabled. If you check both tokens via the introspection endpoint and they are not expired yet, then both tokens remain valid.

However, if we disable the passive rsa-generated key, the token signed by that key will immediately be invalidated.

This key rotation mechanism allows us to switch out old keys while still ensuring that all non-expired tokens remain valid. It helps maintain uptime in terms of authorization and authentication for other services. By rotating keys, we enhance security by limiting the exposure of any single key and ensuring that compromised keys are quickly replaced. Additionally, this process supports compliance with security best practices and regulatory requirements, providing a robust and secure authentication system.

0
Subscribe to my newsletter

Read articles from Quang Phan directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Quang Phan
Quang Phan