In this article we share examples of offline token usage in Keycloak.
As mentioned previously, it is possible to generate offline either through direct access grant or authorization code flow.
Both way are going to be illustrated in this chapter
Using offline Token through direct access grant flow
Requirement
The requirement is to have:
- a client application deployed within a realm
- a user created in this realm, who has got off_line role
Token lifespan
For the example, token lifespan has been adjusted as follows:
-
SSO session Idele Timeout: 1mn
(a.k.a Refresh token validity is 1minute) - Access token: 1 min
- OffLine Tokens: 60 days
Setting the maximum invokation of refresh token
It is possible to the maximum number amount of times a refresh token can be reused, before being ineffective
This is done using:
- The revoke refresh token toggle
- indicates the maximum number of times a refresh token can be reused
If limit was to be reached, following error message would be issued:
{
"error": "invalid_grant",
"error_description": "Maximum allowed refresh token reuse exceeded"
}
Script used to offline token
set -xv
refresh_token=`curl \
-d "client_id=ldap-app" -d "client_secret=password" \
-d "username=user1" -d "password=password" \
-d "grant_type=password" \
-d "scope=openid info offline_access" \
https://localhost:8080/auth/realms/ldap-demo/protocol/openid-connect/token | jq -r '.refresh_token'`
#iter 2
curl \
-d "client_id=ldap-app" -d "client_secret=password" \
-d "grant_type=refresh_token" \
https://localhost:8080/auth/realms/ldap-demo/protocol/openid-connect/token \
-d "refresh_token=$refresh_token" | jq
sleep 200
#iter 3
curl \
-d "client_id=ldap-app" -d "client_secret=password" \
-d "grant_type=refresh_token" \
https://localhost:8080/auth/realms/ldap-demo/protocol/openid-connect/token \
-d "refresh_token=$refresh_token" | jq
sleep 200
#iter 4
curl \
-d "client_id=ldap-app" -d "client_secret=password" \
-d "grant_type=refresh_token" \
https://localhost:8080/auth/realms/ldap-demo/protocol/openid-connect/token \
-d "refresh_token=$refresh_token" | jq
Explanation of the script
Part 1
refresh_token=`curl \
-d "client_id=ldap-app" -d "client_secret=password" \
-d "username=user1" -d "password=password" \
-d "grant_type=password" \
-d "scope=openid info offline_access" \
https://localhost:8080/auth/realms/ldap-demo/protocol/openid-connect/token | jq -r '.refresh_token'`
Within this part:
- The user is connecting to keycloak through direct access grant flow
-
the request contains scope=openid info offline_access
- This allows to generate an refresh tokenof type offline
- The refresh token is extracted from the request
The refresh token issued from such a request is JWT token, and has
got
« typ »: « Offline ».
(For normal
refresh token, the typ is “Refresh”)
{
"jti": "261f8a49-d252-442d-8375-9485a755a116",
"exp": 0,
"nbf": 0,
"iat": 1550059086,
"iss": "https://localhost:8080/auth/realms/ldap-demo",
"aud": "https://localhost:8080/auth/realms/ldap-demo",
"sub": "c1e44f9e-311d-4a8b-9ce8-c69249e24fd5",
"typ": "Offline",
"azp": "ldap-app",
"auth_time": 0,
"session_state": "ab9a8db9-9220-43bf-905a-402313c69c5a",
"realm_access": {
"roles": [
"offline_access"
]
},
"scope": "openid email info offline_access profile"
}
Part 2
The script is using the refresh token generated in first step
- It is reusing the same refresh token
- Each time time, a new access token is issued
#iter 2
curl \
-d "client_id=ldap-app" -d "client_secret=password" \
-d "grant_type=refresh_token" \
https://localhost:8080/auth/realms/ldap-demo/protocol/openid-connect/token \
-d "refresh_token=$refresh_token" | jq
sleep 200
#iter 3
curl \
-d "client_id=ldap-app" -d "client_secret=password" \
-d "grant_type=refresh_token" \
https://localhost:8080/auth/realms/ldap-demo/protocol/openid-connect/token \
-d "refresh_token=$refresh_token" | jq
……
……
Revoking the offline token
The revokation of the offline token can be done in 2 places:
- Through the admin console
- By the the user himself
Revokation of the offline token through the admin UI
The admin user has select the “Revoke” action to revoke the offline token.
Through the user self service panel
The user access to the self-service panel, from where he can revoke the grant “offline access”, as action
Necessity of adding offline in client request scope
Request without client scope
Normal refresh token request
refresh_token=`curl \
-d "client_id=ldap-app" -d "client_secret=password" \
-d "username=user1" -d "password=password" \
-d "grant_type=password" \
-d "scope=openid info" \
https://localhost:8080/auth/realms/ldap-demo/protocol/openid-connect/token | jq -r '.refresh_token'`
TO be noticed:
- The payload of the refresh token is of type “Refresh”
- The “exp” (expiry date) is 60s larger than the “iat” (issuance time)
{
"jti": "b5653d56-614e-424e-9e15-5351f47d73d4",
"exp": 1550059512,
"nbf": 0,
"iat": 1550059452,
"iss": "https://localhost:8080/auth/realms/ldap-demo",
"aud": "https://localhost:8080/auth/realms/ldap-demo",
"sub": "c1e44f9e-311d-4a8b-9ce8-c69249e24fd5",
"typ": "Refresh",
"azp": "ldap-app",
"auth_time": 0,
"session_state": "e3ac603e-03c9-44e6-a75c-6841812f88d2",
"scope": "openid email info profile"
}
Request with client scope
Resuest with scope=offline to request an offline token
refresh_token=`curl \
-d "client_id=ldap-app" -d "client_secret=password" \
-d "username=user1" -d "password=password" \
-d "grant_type=password" \
-d "scope=openid info offline_access" \
https://localhost:8080/auth/realms/ldap-demo/protocol/openid-connect/token | jq -r '.refresh_token'`
TO be noticed:
- The payload of the refresh token is of type “OffLine”
- The “exp” (expiry date) is 0
{
"jti": "261f8a49-d252-442d-8375-9485a755a116",
"exp": 0,
"nbf": 0,
"iat": 1550059086,
"iss": "https://localhost:8080/auth/realms/ldap-demo",
"aud": "https://localhost:8080/auth/realms/ldap-demo",
"sub": "c1e44f9e-311d-4a8b-9ce8-c69249e24fd5",
"typ": "Offline",
"azp": "ldap-app",
"auth_time": 0,
"session_state": "ab9a8db9-9220-43bf-905a-402313c69c5a",
"realm_access": {
"roles": [
"offline_access"
]
},
"scope": "openid email info offline_access profile"
}
Keycloak offline example
Keycloak provides an offline demo example to showcase, how it is possible to used offline tokens with Java performing authorization code flow.
The example is available at:
Step1 – User needs to log to the app – An offline access token is generated
Step 2 – user logs out from app
The off line access token is still valid
step3 – the app can access to the resources using the offline access token
- New Keycloak online training - 19 janvier 2022
- Sizing Keycloak or Redhat SSO projects - 8 juin 2021
- Keycloak.X Distribution - 28 janvier 2021