Keycloak refresh token error

Deprecated.
  • 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 name
        NullPointerException — 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

Понравилась статья? Поделить с друзьями:
  • Keycloak cors error
  • Keyboardinterrupt python ошибка
  • Keyboardinterrupt python как исправить
  • Keyboard interface error press f1 to resume как исправить
  • Keyboard interface error please enter setup to recover bios setting