- java.lang.Object
-
- java.lang.Enum<EventType>
-
- org.keycloak.events.EventType
-
- All Implemented Interfaces:
- Serializable, Comparable<EventType>
public enum EventType extends Enum<EventType>
- Author:
- Stian Thorgersen
-
-
Enum Constant Summary
Enum Constants
Enum Constant and Description CLIENT_DELETE
CLIENT_DELETE_ERROR
CLIENT_INFO
CLIENT_INFO_ERROR
CLIENT_INITIATED_ACCOUNT_LINKING
CLIENT_INITIATED_ACCOUNT_LINKING_ERROR
CLIENT_LOGIN
CLIENT_LOGIN_ERROR
CLIENT_REGISTER
CLIENT_REGISTER_ERROR
CLIENT_UPDATE
CLIENT_UPDATE_ERROR
CODE_TO_TOKEN
CODE_TO_TOKEN_ERROR
CUSTOM_REQUIRED_ACTION
CUSTOM_REQUIRED_ACTION_ERROR
EXECUTE_ACTION_TOKEN
EXECUTE_ACTION_TOKEN_ERROR
EXECUTE_ACTIONS
EXECUTE_ACTIONS_ERROR
FEDERATED_IDENTITY_LINK
FEDERATED_IDENTITY_LINK_ERROR
GRANT_CONSENT
GRANT_CONSENT_ERROR
IDENTITY_PROVIDER_FIRST_LOGIN
IDENTITY_PROVIDER_FIRST_LOGIN_ERROR
IDENTITY_PROVIDER_LINK_ACCOUNT
IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR
IDENTITY_PROVIDER_LOGIN
IDENTITY_PROVIDER_LOGIN_ERROR
IDENTITY_PROVIDER_POST_LOGIN
IDENTITY_PROVIDER_POST_LOGIN_ERROR
IDENTITY_PROVIDER_RESPONSE
IDENTITY_PROVIDER_RESPONSE_ERROR
IDENTITY_PROVIDER_RETRIEVE_TOKEN
IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR
IMPERSONATE
IMPERSONATE_ERROR
INTROSPECT_TOKEN
INTROSPECT_TOKEN_ERROR
INVALID_SIGNATURE
INVALID_SIGNATURE_ERROR
LOGIN
LOGIN_ERROR
LOGOUT
LOGOUT_ERROR
PERMISSION_TOKEN
PERMISSION_TOKEN_ERROR
REFRESH_TOKEN
REFRESH_TOKEN_ERROR
REGISTER
REGISTER_ERROR
REGISTER_NODE
REGISTER_NODE_ERROR
REMOVE_FEDERATED_IDENTITY
REMOVE_FEDERATED_IDENTITY_ERROR
REMOVE_TOTP
REMOVE_TOTP_ERROR
RESET_PASSWORD
RESET_PASSWORD_ERROR
RESTART_AUTHENTICATION
RESTART_AUTHENTICATION_ERROR
REVOKE_GRANT
REVOKE_GRANT_ERROR
SEND_IDENTITY_PROVIDER_LINK
SEND_IDENTITY_PROVIDER_LINK_ERROR
SEND_RESET_PASSWORD
SEND_RESET_PASSWORD_ERROR
SEND_VERIFY_EMAIL
SEND_VERIFY_EMAIL_ERROR
TOKEN_EXCHANGE
TOKEN_EXCHANGE_ERROR
UNREGISTER_NODE
UNREGISTER_NODE_ERROR
UPDATE_CONSENT
UPDATE_CONSENT_ERROR
UPDATE_EMAIL
UPDATE_EMAIL_ERROR
UPDATE_PASSWORD
UPDATE_PASSWORD_ERROR
UPDATE_PROFILE
UPDATE_PROFILE_ERROR
UPDATE_TOTP
UPDATE_TOTP_ERROR
USER_INFO_REQUEST
USER_INFO_REQUEST_ERROR
VALIDATE_ACCESS_TOKEN
Deprecated.
VALIDATE_ACCESS_TOKEN_ERROR
Deprecated.
VERIFY_EMAIL
VERIFY_EMAIL_ERROR
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods
Modifier and Type Method and Description boolean
isSaveByDefault()
Determines whether this event is stored when the admin has not set a specific set of event types to save.
static EventType
valueOf(String name)
Returns the enum constant of this type with the specified name.
static EventType[]
values()
Returns an array containing the constants of this enum type, in
the order they are declared.-
Methods inherited from class java.lang.Enum
clone, compareTo, equals, finalize, getDeclaringClass, hashCode, name, ordinal, toString, valueOf
-
Methods inherited from class java.lang.Object
getClass, notify, notifyAll, wait, wait, wait
-
-
-
-
Enum Constant Detail
-
LOGIN
public static final EventType LOGIN
-
LOGIN_ERROR
public static final EventType LOGIN_ERROR
-
REGISTER
public static final EventType REGISTER
-
REGISTER_ERROR
public static final EventType REGISTER_ERROR
-
LOGOUT
public static final EventType LOGOUT
-
LOGOUT_ERROR
public static final EventType LOGOUT_ERROR
-
CODE_TO_TOKEN
public static final EventType CODE_TO_TOKEN
-
CODE_TO_TOKEN_ERROR
public static final EventType CODE_TO_TOKEN_ERROR
-
CLIENT_LOGIN
public static final EventType CLIENT_LOGIN
-
CLIENT_LOGIN_ERROR
public static final EventType CLIENT_LOGIN_ERROR
-
REFRESH_TOKEN
public static final EventType REFRESH_TOKEN
-
REFRESH_TOKEN_ERROR
public static final EventType REFRESH_TOKEN_ERROR
-
VALIDATE_ACCESS_TOKEN
@Deprecated public static final EventType VALIDATE_ACCESS_TOKEN
Deprecated.
-
VALIDATE_ACCESS_TOKEN_ERROR
@Deprecated public static final EventType VALIDATE_ACCESS_TOKEN_ERROR
Deprecated.
-
INTROSPECT_TOKEN
public static final EventType INTROSPECT_TOKEN
-
INTROSPECT_TOKEN_ERROR
public static final EventType INTROSPECT_TOKEN_ERROR
-
FEDERATED_IDENTITY_LINK
public static final EventType FEDERATED_IDENTITY_LINK
-
FEDERATED_IDENTITY_LINK_ERROR
public static final EventType FEDERATED_IDENTITY_LINK_ERROR
-
REMOVE_FEDERATED_IDENTITY
public static final EventType REMOVE_FEDERATED_IDENTITY
-
REMOVE_FEDERATED_IDENTITY_ERROR
public static final EventType REMOVE_FEDERATED_IDENTITY_ERROR
-
UPDATE_EMAIL
public static final EventType UPDATE_EMAIL
-
UPDATE_EMAIL_ERROR
public static final EventType UPDATE_EMAIL_ERROR
-
UPDATE_PROFILE
public static final EventType UPDATE_PROFILE
-
UPDATE_PROFILE_ERROR
public static final EventType UPDATE_PROFILE_ERROR
-
UPDATE_PASSWORD
public static final EventType UPDATE_PASSWORD
-
UPDATE_PASSWORD_ERROR
public static final EventType UPDATE_PASSWORD_ERROR
-
UPDATE_TOTP
public static final EventType UPDATE_TOTP
-
UPDATE_TOTP_ERROR
public static final EventType UPDATE_TOTP_ERROR
-
VERIFY_EMAIL
public static final EventType VERIFY_EMAIL
-
VERIFY_EMAIL_ERROR
public static final EventType VERIFY_EMAIL_ERROR
-
REMOVE_TOTP
public static final EventType REMOVE_TOTP
-
REMOVE_TOTP_ERROR
public static final EventType REMOVE_TOTP_ERROR
-
GRANT_CONSENT
public static final EventType GRANT_CONSENT
-
GRANT_CONSENT_ERROR
public static final EventType GRANT_CONSENT_ERROR
-
UPDATE_CONSENT
public static final EventType UPDATE_CONSENT
-
UPDATE_CONSENT_ERROR
public static final EventType UPDATE_CONSENT_ERROR
-
REVOKE_GRANT
public static final EventType REVOKE_GRANT
-
REVOKE_GRANT_ERROR
public static final EventType REVOKE_GRANT_ERROR
-
SEND_VERIFY_EMAIL
public static final EventType SEND_VERIFY_EMAIL
-
SEND_VERIFY_EMAIL_ERROR
public static final EventType SEND_VERIFY_EMAIL_ERROR
-
SEND_RESET_PASSWORD
public static final EventType SEND_RESET_PASSWORD
-
SEND_RESET_PASSWORD_ERROR
public static final EventType SEND_RESET_PASSWORD_ERROR
-
SEND_IDENTITY_PROVIDER_LINK
public static final EventType SEND_IDENTITY_PROVIDER_LINK
-
SEND_IDENTITY_PROVIDER_LINK_ERROR
public static final EventType SEND_IDENTITY_PROVIDER_LINK_ERROR
-
RESET_PASSWORD
public static final EventType RESET_PASSWORD
-
RESET_PASSWORD_ERROR
public static final EventType RESET_PASSWORD_ERROR
-
RESTART_AUTHENTICATION
public static final EventType RESTART_AUTHENTICATION
-
RESTART_AUTHENTICATION_ERROR
public static final EventType RESTART_AUTHENTICATION_ERROR
-
INVALID_SIGNATURE
public static final EventType INVALID_SIGNATURE
-
INVALID_SIGNATURE_ERROR
public static final EventType INVALID_SIGNATURE_ERROR
-
REGISTER_NODE
public static final EventType REGISTER_NODE
-
REGISTER_NODE_ERROR
public static final EventType REGISTER_NODE_ERROR
-
UNREGISTER_NODE
public static final EventType UNREGISTER_NODE
-
UNREGISTER_NODE_ERROR
public static final EventType UNREGISTER_NODE_ERROR
-
USER_INFO_REQUEST
public static final EventType USER_INFO_REQUEST
-
USER_INFO_REQUEST_ERROR
public static final EventType USER_INFO_REQUEST_ERROR
-
IDENTITY_PROVIDER_LINK_ACCOUNT
public static final EventType IDENTITY_PROVIDER_LINK_ACCOUNT
-
IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR
public static final EventType IDENTITY_PROVIDER_LINK_ACCOUNT_ERROR
-
IDENTITY_PROVIDER_LOGIN
public static final EventType IDENTITY_PROVIDER_LOGIN
-
IDENTITY_PROVIDER_LOGIN_ERROR
public static final EventType IDENTITY_PROVIDER_LOGIN_ERROR
-
IDENTITY_PROVIDER_FIRST_LOGIN
public static final EventType IDENTITY_PROVIDER_FIRST_LOGIN
-
IDENTITY_PROVIDER_FIRST_LOGIN_ERROR
public static final EventType IDENTITY_PROVIDER_FIRST_LOGIN_ERROR
-
IDENTITY_PROVIDER_POST_LOGIN
public static final EventType IDENTITY_PROVIDER_POST_LOGIN
-
IDENTITY_PROVIDER_POST_LOGIN_ERROR
public static final EventType IDENTITY_PROVIDER_POST_LOGIN_ERROR
-
IDENTITY_PROVIDER_RESPONSE
public static final EventType IDENTITY_PROVIDER_RESPONSE
-
IDENTITY_PROVIDER_RESPONSE_ERROR
public static final EventType IDENTITY_PROVIDER_RESPONSE_ERROR
-
IDENTITY_PROVIDER_RETRIEVE_TOKEN
public static final EventType IDENTITY_PROVIDER_RETRIEVE_TOKEN
-
IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR
public static final EventType IDENTITY_PROVIDER_RETRIEVE_TOKEN_ERROR
-
IMPERSONATE
public static final EventType IMPERSONATE
-
IMPERSONATE_ERROR
public static final EventType IMPERSONATE_ERROR
-
CUSTOM_REQUIRED_ACTION
public static final EventType CUSTOM_REQUIRED_ACTION
-
CUSTOM_REQUIRED_ACTION_ERROR
public static final EventType CUSTOM_REQUIRED_ACTION_ERROR
-
EXECUTE_ACTIONS
public static final EventType EXECUTE_ACTIONS
-
EXECUTE_ACTIONS_ERROR
public static final EventType EXECUTE_ACTIONS_ERROR
-
EXECUTE_ACTION_TOKEN
public static final EventType EXECUTE_ACTION_TOKEN
-
EXECUTE_ACTION_TOKEN_ERROR
public static final EventType EXECUTE_ACTION_TOKEN_ERROR
-
CLIENT_INFO
public static final EventType CLIENT_INFO
-
CLIENT_INFO_ERROR
public static final EventType CLIENT_INFO_ERROR
-
CLIENT_REGISTER
public static final EventType CLIENT_REGISTER
-
CLIENT_REGISTER_ERROR
public static final EventType CLIENT_REGISTER_ERROR
-
CLIENT_UPDATE
public static final EventType CLIENT_UPDATE
-
CLIENT_UPDATE_ERROR
public static final EventType CLIENT_UPDATE_ERROR
-
CLIENT_DELETE
public static final EventType CLIENT_DELETE
-
CLIENT_DELETE_ERROR
public static final EventType CLIENT_DELETE_ERROR
-
CLIENT_INITIATED_ACCOUNT_LINKING
public static final EventType CLIENT_INITIATED_ACCOUNT_LINKING
-
CLIENT_INITIATED_ACCOUNT_LINKING_ERROR
public static final EventType CLIENT_INITIATED_ACCOUNT_LINKING_ERROR
-
TOKEN_EXCHANGE
public static final EventType TOKEN_EXCHANGE
-
TOKEN_EXCHANGE_ERROR
public static final EventType TOKEN_EXCHANGE_ERROR
-
PERMISSION_TOKEN
public static final EventType PERMISSION_TOKEN
-
PERMISSION_TOKEN_ERROR
public static final EventType PERMISSION_TOKEN_ERROR
-
-
Method Detail
-
values
public static EventType[] values()
Returns an array containing the constants of this enum type, in
the order they are declared. This method may be used to iterate
over the constants as follows:for (EventType c : EventType.values()) System.out.println(c);
- Returns:
- an array containing the constants of this enum type, in the order they are declared
-
valueOf
public static EventType valueOf(String name)
Returns the enum constant of this type with the specified name.
The string must match exactly an identifier used to declare an
enum constant in this type. (Extraneous whitespace characters are
not permitted.)- Parameters:
name
— the name of the enum constant to be returned.- Returns:
- the enum constant with the specified name
- Throws:
IllegalArgumentException
— if this enum type has no constant with the specified nameNullPointerException
— if the argument is null
-
isSaveByDefault
public boolean isSaveByDefault()
Determines whether this event is stored when the admin has not set a specific set of event types to save.
- Returns:
-
-
Я пытаюсь обновить токен, но Keycloak возвращает ошибку 400 Bad Request
со следующим сообщением:
{
"error": "invalid_grant",
"error_description": "Invalid refresh token"
}
Я успешно получаю токен обновления в таком запросе:
curl --location --request POST 'http://localhost:8080/auth/realms/my_realm/protocol/openid-connect/token'
--header 'Content-Type: application/x-www-form-urlencoded'
--data-urlencode 'username=my_user'
--data-urlencode 'password=my_password'
--data-urlencode 'grant_type=password'
--data-urlencode 'client_id=my_client_id'
--data-urlencode 'client_secret=my_client_secret'
Итак, я получаю ответ JWT с токеном доступа и токеном обновления. Оба они кажутся действительными, когда я загружаю их в jwt.io.
Но когда я пытаюсь использовать токен обновления, я получаю предыдущую ошибку. Запрос такой:
curl --location --request POST 'http://localhost:8080/auth/realms/my_realm/protocol/openid-connect/token'
--header 'Content-Type: application/x-www-form-urlencoded'
--data-urlencode 'client_id=my_client_id'
--data-urlencode 'grant_type=refresh_token'
--data-urlencode 'refresh_token=my_refresh_token'
--data-urlencode 'client_secret=my_client_secret'
В журнале Keycloak нет ни малейшего представления о том, в чем проблема.
Что может быть причиной? Или, по крайней мере, есть способ получить дополнительную информацию о причине ошибки из ответа или журнала Keycloak?
РЕДАКТИРОВАТЬ:
Я реализовал SPI поставщика хранилища пользователей, поэтому он выполняет аутентификацию во внешней базе данных, но не управляет пользователями в Keycloak. Требуется ли, чтобы пользователь-владелец токена существовал в Keycloak, чтобы обновление токена работало?
Спасибо.
1 ответ
Лучший ответ
Наконец, это была проблема в моем SPI поставщика хранилища для пользователей. Необходимо было реализовать следующий метод, так как Keycloak берет идентификатор пользователя из токена обновления и ищет пользователя по такому идентификатору:
public UserModel getUserById(String id, RealmModel realm) {
StorageId storageId = new StorageId(id);
String username = storageId.getExternalId();
return getUserByUsername(username, realm);
}
0
Paco Abato
7 Окт 2020 в 11:50
Views: 20,602
I introduced Refresh Token grant type in OAuth 2.0 in the tutorial about Grant types in OAuth 2.0. In this tutorial, I will show you how we get a new access token using refresh token with Keycloak!
First, I will create a new client in Keycloak with the Authorization Code grant type as an example:
Perform get access token for this client, you will see the following results:
{ «access_token»: «eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJwUEstV2pnSUJuZzRjRmc1VktLRG9jQ0Q5V3VJUElzWERSSDhjSm5fRHQwIn0.eyJleHAiOjE2NjYwNTI5MTksImlhdCI6MTY2NjA1MjYxOSwiYXV0aF90aW1lIjoxNjY2MDUyNjExLCJqdGkiOiJiZGU5NDRiZC04YzUyLTQxMjYtOGE4YS0xOGY2MjZkNDgxYTAiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvcmVhbG1zL2h1b25nZGFuamF2YSIsImF1ZCI6ImFjY291bnQiLCJzdWIiOiJjZjQ3NTBlMC01NjlkLTQyMWUtYTM3MC0zMzRhMGY1MGUwYmYiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJodW9uZ2RhbmphdmFfYXV0aG9yaXphdGlvbl9jb2RlIiwibm9uY2UiOiI4MHZ3NXIxdjJ3YSIsInNlc3Npb25fc3RhdGUiOiIzMTg4ZWY1My1iMTM0LTRiNjktYTQ0NS0zZDk3OGM4ZDE4MzYiLCJhY3IiOiIxIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iLCJkZWZhdWx0LXJvbGVzLWh1b25nZGFuamF2YSJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoib3BlbmlkIHByb2ZpbGUgZW1haWwiLCJzaWQiOiIzMTg4ZWY1My1iMTM0LTRiNjktYTQ0NS0zZDk3OGM4ZDE4MzYiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsIm5hbWUiOiJLaGFuaCBOZ3V5ZW4iLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJodW9uZ2RhbmphdmEiLCJnaXZlbl9uYW1lIjoiS2hhbmgiLCJmYW1pbHlfbmFtZSI6Ik5ndXllbiIsImVtYWlsIjoiaHVvbmdkYW5qYXZhLmNvbUBnbWFpbC5jb20ifQ.FGTrJqD69-KiYu5oQOftQJ_gQ9EuBskQX7ofHUOAXB1XkYs_X1OxbEZZlt1wN0nLpET160sq1TJPH2LF_MNXZilR8FnC_r0RyfaNq5le-BIN-Fg2Q9wc2NfKwE-nERyJzU64Rvo87YZRyAyQB-h1V39Xdm0yJhpdo0SG6tKCAj-fDGiUVSlQB0zZUMdXihK3zdo8MCaAPjRpRbP-l2nsCUCgX7kcGMTZwcgIPoa0pjXg1rO-8PK418pgOE8sYR5upSPoRH2B_ve0NI2B5UaIa1ncoAmreaVx56R3YvPimhkcTjPr8LsuGSba0SsshWk_-xmuwy0qatVJVuSYJMfHaQ», «expires_in»: 300, «refresh_expires_in»: 1800, «refresh_token»: «eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI5M2JkNjBiMC04MDkyLTQzNDctYjkyMS02OTI3Nzc2MWMyOTMifQ.eyJleHAiOjE2NjYwNTQ0MTksImlhdCI6MTY2NjA1MjYxOSwianRpIjoiNTI4YzJmOTMtNWRhOC00ODUzLTlkZDUtZmE5NjM0NDU4ZjNkIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9odW9uZ2RhbmphdmEiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvcmVhbG1zL2h1b25nZGFuamF2YSIsInN1YiI6ImNmNDc1MGUwLTU2OWQtNDIxZS1hMzcwLTMzNGEwZjUwZTBiZiIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJodW9uZ2RhbmphdmFfYXV0aG9yaXphdGlvbl9jb2RlIiwibm9uY2UiOiI4MHZ3NXIxdjJ3YSIsInNlc3Npb25fc3RhdGUiOiIzMTg4ZWY1My1iMTM0LTRiNjktYTQ0NS0zZDk3OGM4ZDE4MzYiLCJzY29wZSI6Im9wZW5pZCBwcm9maWxlIGVtYWlsIiwic2lkIjoiMzE4OGVmNTMtYjEzNC00YjY5LWE0NDUtM2Q5NzhjOGQxODM2In0.U_gBHcEBjwsAoFuIfwHnIhInHGw5f6M0b-zTPvDepIg», «token_type»: «Bearer», «id_token»: «eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJwUEstV2pnSUJuZzRjRmc1VktLRG9jQ0Q5V3VJUElzWERSSDhjSm5fRHQwIn0.eyJleHAiOjE2NjYwNTI5MTksImlhdCI6MTY2NjA1MjYxOSwiYXV0aF90aW1lIjoxNjY2MDUyNjExLCJqdGkiOiJmNzRhMDczOS03YWE1LTQ5NWMtYmFlZi1jZThhN2RkNDZmYmIiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvcmVhbG1zL2h1b25nZGFuamF2YSIsImF1ZCI6Imh1b25nZGFuamF2YV9hdXRob3JpemF0aW9uX2NvZGUiLCJzdWIiOiJjZjQ3NTBlMC01NjlkLTQyMWUtYTM3MC0zMzRhMGY1MGUwYmYiLCJ0eXAiOiJJRCIsImF6cCI6Imh1b25nZGFuamF2YV9hdXRob3JpemF0aW9uX2NvZGUiLCJub25jZSI6Ijgwdnc1cjF2MndhIiwic2Vzc2lvbl9zdGF0ZSI6IjMxODhlZjUzLWIxMzQtNGI2OS1hNDQ1LTNkOTc4YzhkMTgzNiIsImF0X2hhc2giOiJ1LVJzdEtxVEloWUZoUnRJSDJ6Zld3IiwiYWNyIjoiMSIsInNpZCI6IjMxODhlZjUzLWIxMzQtNGI2OS1hNDQ1LTNkOTc4YzhkMTgzNiIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwibmFtZSI6IktoYW5oIE5ndXllbiIsInByZWZlcnJlZF91c2VybmFtZSI6Imh1b25nZGFuamF2YSIsImdpdmVuX25hbWUiOiJLaGFuaCIsImZhbWlseV9uYW1lIjoiTmd1eWVuIiwiZW1haWwiOiJodW9uZ2RhbmphdmEuY29tQGdtYWlsLmNvbSJ9.ngowkiPnnnc1ivNf3fbA0F3jXS3hSdsjZPbL5InLJriuszPUWd8KGqQESLCduRb4sBFntz1AOqa_mX5HH846IP2WEBybK9NFN6HOCFutdpulL1h_aFeRqw4faMxYdr3zh1pqENf76pcu9YyF0QjJ42Lk0LjogyEXWfMiXBgcSSU5wwgHfBu7NKt_gyCznnMm-cZygH5PrM-ql0fs54OGnFe6rnlyrT3Oo_78PypZv6NIDooYw_JtyzSrmf1SS3uEGtObCKHyINHo-Kzk7QgAsz_cScW6l0EAGFeOt-mJwC_3m4gmRuXQBVjEHDri9isKaufYJVKLL7HeOxf9jkU1VQ», «not-before-policy»: 0, «session_state»: «3188ef53-b134-4b69-a445-3d978c8d1836», «scope»: «openid profile email» } |
The content of the refresh token if I decode it using https://jwt.io/ is basically as follows:
Please pay attention to the “typ” claim in the content of the refresh token above! Here, the value of this claim is Refresh, we also have some other types of refresh tokens, such as Offline,…
The refresh token will always be returned along with the access token, so we can get a new access token without the user having to log in again.
To get a new access token with a refresh token, in the request to get the access token, you just need to pass grant_type=refresh_token, the value of the refresh token that we had in the previous request to get the access token, client ID and client secret.
With this example of mine, you can get a new access token by requesting the following:
As you can see, with the Refresh Token grant type, we don’t need the user to log in anymore.
A refresh token will always have an expiration time, the default of Keycloak is 30 minutes! Every time a new access token is issued, the refresh token will be re-issued, and you can use the latest one for a longer expiration time.
We can use a refresh token to request the access token multiple times. However, you can limit the number of times a refresh token can be used, by going to the Tokens tab, Refresh tokens section of Realm Settings, configuring the Revoke Refresh Token field. If you turn on this field, you can configure how many times a refresh token is reused:
By default, you can only use the refresh token once!
I tried this with Keycloak 4.4.0.Final and 4.6.0.Final. I checked the keycloak server log and I saw the following warning messages in the console output.
10:33:22,882 WARN [org.keycloak.events] (default task-1) type=REFRESH_TOKEN_ERROR, realmId=master, clientId=security-admin-console, userId=null, ipAddress=127.0.0.1, error=invalid_token, grant_type=refresh_token, client_auth_method=client-secret
10:40:41,376 WARN [org.keycloak.events] (default task-5) type=LOGOUT_ERROR, realmId=demo, clientId=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJqYTBjX18xMHJXZi1KTEpYSGNqNEdSNWViczRmQlpGS3NpSHItbDlud2F3In0.eyJqdGkiOiI1ZTdhYzQ4Zi1mYjkyLTRkZTYtYjcxNC01MTRlMTZiMmJiNDYiLCJleHAiOjE1NDM0MDE2MDksIm5iZiI6MCwiaWF0IjoxNTQzNDAxMzA5LCJpc3MiOiJodHRwOi8vMTI3Lj, userId=null, ipAddress=127.0.0.1, error=invalid_client_credentials
So how did build the HTTP request? First, I retrieved the user principal from the HttpSession and cast to the internal Keycloak instance types:
KeycloakAuthenticationToken keycloakAuthenticationToken = (KeycloakAuthenticationToken) request.getUserPrincipal();
final KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal)keycloakAuthenticationToken.getPrincipal();
final RefreshableKeycloakSecurityContext context = (RefreshableKeycloakSecurityContext) keycloakPrincipal.getKeycloakSecurityContext();
final AccessToken accessToken = context.getToken();
final IDToken idToken = context.getIdToken();
Second, I created the logout URL as in the top stack overflow answer (see above):
final String logoutURI = idToken.getIssuer() +"/protocol/openid-connect/logout?"+
"redirect_uri="+response.encodeRedirectURL(url.toString());
And now I then build the rest of the HTTP request like so:
KeycloakRestTemplate keycloakRestTemplate = new KeycloakRestTemplate(keycloakClientRequestFactory);
HttpHeaders headers = new HttpHeaders();
headers.put("Authorization", Collections.singletonList("Bearer "+idToken.getId()));
headers.put("Content-Type", Collections.singletonList("application/x-www-form-urlencoded"));
And also build the body content string:
StringBuilder bodyContent = new StringBuilder();
bodyContent.append("client_id=").append(context.getTokenString())
.append("&")
.append("client_secret=").append(keycloakCredentialsSecret)
.append("&")
.append("user_name=").append(keycloakPrincipal.getName())
.append("&")
.append("user_id=").append(idToken.getId())
.append("&")
.append("refresh_token=").append(context.getRefreshToken())
.append("&")
.append("token=").append(accessToken.getId());
HttpEntity<String> entity = new HttpEntity<>(bodyContent.toString(), headers);
// ...
ResponseEntity<String> forEntity = keycloakRestTemplate.exchange(logoutURI, HttpMethod.POST, entity, String.class); // *FAILURE*
As you can observed, I attempted many variations of theme, but I kept getting invalid user authentication.
Oh yeah. I injected the keycloak credentials secret from the application.properties
into object instance field with @Value
@Value("${keycloak.credentials.secret}")
private String keycloakCredentialsSecret;
Any ideas from Java Spring Security experienced engineers?
ADDENDUM
I created a realm in KC called ‘demo’ and a client called ‘web-portal’
with the following parameters:
Client Protocol: openid-connect
Access Type: public
Standard Flow Enabled: On
Implicit Flow Enabled: Off
Direct Access Grants Enabled: On
Authorization Enabled: Off
Here is the code that rebuilds the redirect URI, I forgot to include it here.
final String scheme = request.getScheme(); // http
final String serverName = request.getServerName(); // hostname.com
final int serverPort = request.getServerPort(); // 80
final String contextPath = request.getContextPath(); // /mywebapp
// Reconstruct original requesting URL
StringBuilder url = new StringBuilder();
url.append(scheme).append("://").append(serverName);
if (serverPort != 80 && serverPort != 443) {
url.append(":").append(serverPort);
}
url.append(contextPath).append("/offline-page.html");
That’s all