krasamo / keycloak-typescript

MIT License
7 stars 3 forks source link

Token Refresh Fails Due to SSO Session Max Timeout Leading App To Crash #42

Open ScheckGuillermo opened 18 hours ago

ScheckGuillermo commented 18 hours ago

Problem:

During the initialization process, the initializeManager method on TokenManager requests an accessToken and refresh token using the grant type password:

this.clientSecret = keycloakLogin.clientSecret;
    this.clientId = keycloakLogin.clientId;
    const body = {
      client_id: this.clientId,
      username: keycloakLogin.username,
      password: keycloakLogin.password,
      client_secret: this.clientSecret,
      grant_type: 'password'
    };
    const apiConfig = {
      url: this.url,
      method: 'POST',
      headers: {},
      body: querystring.stringify(body)
    };
    await this.makeRefreshRequest(apiConfig);

Then, it sets up a setInterval to ensure there is always a valid access token for API operations:

//Create timed task to refresh token
    const marginInSeconds = 60;
    if (
      this.accessTokenExpireTime != undefined &&
      this.refreshTokenExpireTime != undefined
    ) {
      const accessTokenInterval = this.accessTokenExpireTime - marginInSeconds;
      const refreshTokenInterval =
        this.refreshTokenExpireTime - marginInSeconds;
      if (accessTokenInterval > refreshTokenInterval) {
        setInterval(() => {
          this.refreshAccessToken();
        }, refreshTokenInterval * 1000);
      } else {
        setInterval(() => {
          this.refreshAccessToken();
        }, accessTokenInterval * 1000);
      }
    }

The problem arises due to how Keycloak handles refresh token expiry, which is tied to the SSO Session Max timeout. When the SSO Session Max timeout is reached, the refresh token becomes invalid regardless of its original expiration time. The SSO Session Max timeout represents the maximum session lifespan, and once exceeded, any tokens associated with that session become invalid. The library currently assumes that using the refresh_token grant type will always return consistent expiration times for both access and refresh tokens. However, due to the SSO Session Max settings, these tokens’ validity is capped by the session’s maximum lifespan. Eventually, the server attempts to renew tokens using an expired refresh token, resulting in a 400 error from Axios, which can cause the entire server to crash because there is no error handling in place.

Proposed Solutions::

  1. Implement Error Handling: The token refresh logic should include error handling to catch failures when refreshing tokens. If a refresh token has expired, the library should re-authenticate using the password grant type to obtain new tokens.
  2. Dynamic Expiration Handling: Instead of assuming fixed expiration times, the library should calculate token refresh intervals based on the actual expires_in and refresh_expires_in values returned by Keycloak, which may change due to SSO session settings.
  3. Request offline_access Scope: Modify the initial password grant request to include the offline_access scope. This will provide an offline refresh token, which is not subject to the SSO session idle timeout and can be used to obtain new access tokens even after the session has expired.

Steps to Reproduce:

  1. Adjust Keycloak Session Timeouts:
    • Set SSO Session Max timeout to 2 minutes.
    • Set SSO Session Idle timeout to 5 minutes (or any value greater than the SSO Session Max).
    • Set Access Token Lifespan to 1 minute.
  2. Initialize the Keycloak Manager
    • Use the keycloak-typescript library to initialize the manager in your backend service as usual.
  3. Wait for Session Max Timeout to Exceed
    • Allow the service to run for more than 2 minutes to exceed the SSO Session Max timeout.
  4. Trigger Token Refresh
    • Wait until the next scheduled token refresh occurs (after the access token expires in 1 minute).
  5. Observe the Error:
    • Notice that the server attempts to refresh tokens using an expired refresh token.
    • A 400 error is returned from Axios due to the invalid refresh token.
    • Without error handling, this unhandled exception may cause the server to crash.
euscanga-krasamo commented 16 hours ago

Hello @ScheckGuillermo, thanks again for sharing the steps to reproduce and the detailed version of the issue. We are going to assign this task to @KLloydNA. :D