Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Negative refresh token expiration (exp timestamp in the past) #11990

@alexwais

Description

@alexwais

Describe the bug

I came across a strage behavior (seemingly a bug) regarding the refresh token expiration.

Under some (unknown) circumstances, the refresh_token issued by Keycloak contains an exp claim timestamp that is before the iat timestamp, e.g.:

  "exp": 1652513276,
  "iat": 1652529209,

This happens when retrieving an access token + refresh token from the https://kc-host/realms/my-realm/protocol/openid-connect/token endpoint using the authorization_code grant type.

Likewise, the response from the token endpoint contains a negative refresh_expires_in value:

{
   "access_token": "eyJhbGciOiJ...",
   "expires_in": 300,
   "refresh_expires_in": -15933,
   "refresh_token": "eyJhbGciOiJ...",
   "token_type": "Bearer",
   "id_token":" eyJhbGciOiJ...",
   "not-before-policy": 0,
   "session_state": "fa8347b3-4717-4bd6-a061-99f087aebf7e",
   "scope": "openid"
}

Other timestamps/expirations are fine however (e.g., access token and KEYCLOAK_IDENTITY cookie in the same request showed OK).

I stumbled across this issue by recognizing a frequent "hard" refresh of my frontend client, since the regular refresh of the access token fails (due to the invalid refresh token, obviously). This led me to investigating said problem with the refesh token.

Noteworthy:

  • When logging out of the Keycloak session completely, the problem is gone on next login (exp timestamp with proper offset in the future)
  • We use an IdP during login (Azure AD)

Version

17.0.1

Expected behavior

Refresh token expiration to be positive and as configured, e.g.:
"refresh_expires_in": 1800

Actual behavior

Refresh token expiration is negative, e.g.:
"refresh_expires_in": -15933

How to Reproduce?

Token lifespan relevant realm config:

  "revokeRefreshToken": false,
  "refreshTokenMaxReuse": 0,
  "accessTokenLifespan": 300,
  "accessTokenLifespanForImplicitFlow": 900,
  "ssoSessionIdleTimeout": 43200,
  "ssoSessionMaxLifespan": 86400,
  "ssoSessionIdleTimeoutRememberMe": 0,
  "ssoSessionMaxLifespanRememberMe": 0,
  "offlineSessionIdleTimeout": 2592000,
  "offlineSessionMaxLifespanEnabled": false,
  "offlineSessionMaxLifespan": 5184000,
  "clientSessionIdleTimeout": 1800,
  "clientSessionMaxLifespan": 36000,
  "clientOfflineSessionIdleTimeout": 0,
  "clientOfflineSessionMaxLifespan": 0,
  "accessCodeLifespan": 60,
  "accessCodeLifespanUserAction": 300,
  "accessCodeLifespanLogin": 1800,

Realm client config:

    {
      "clientId": "my-client",
      "description": "",
      "rootUrl": "redacted",
      "adminUrl": "",
      "baseUrl": "/",
      "surrogateAuthRequired": false,
      "enabled": true,
      "alwaysDisplayInConsole": true,
      "clientAuthenticatorType": "client-secret",
      "redirectUris": redacted,
      "webOrigins": [
        "+"
      ],
      "notBefore": 0,
      "bearerOnly": false,
      "consentRequired": false,
      "standardFlowEnabled": true,
      "implicitFlowEnabled": false,
      "directAccessGrantsEnabled": false,
      "serviceAccountsEnabled": false,
      "publicClient": true,
      "frontchannelLogout": false,
      "protocol": "openid-connect",
      "attributes": {
        "saml.assertion.signature": "false",
        "saml.force.post.binding": "false",
        "saml.multivalued.roles": "false",
        "saml.encrypt": "false",
        "oauth2.device.authorization.grant.enabled": "false",
        "backchannel.logout.revoke.offline.tokens": "false",
        "saml.server.signature": "false",
        "saml.server.signature.keyinfo.ext": "false",
        "use.refresh.tokens": "true",
        "exclude.session.state.from.auth.response": "false",
        "oidc.ciba.grant.enabled": "false",
        "saml.artifact.binding": "false",
        "backchannel.logout.session.required": "true",
        "client_credentials.use_refresh_token": "false",
        "saml_force_name_id_format": "false",
        "saml.client.signature": "false",
        "tls.client.certificate.bound.access.tokens": "false",
        "saml.authnstatement": "false",
        "display.on.consent.screen": "false",
        "saml.onetimeuse.condition": "false"
      },
      "fullScopeAllowed": false,
      "nodeReRegistrationTimeout": -1
    },

Client application:

  • Chrome on macOS

Anything else?

Interestingly, until now, I only happened to experience this problem with Chrome on macOS. No issue so far on Chrome on Windows.
On the macOS machine, however, it also does not occur consistently. We have multiple Keycloak instances (for different environments), with the exact same Keycloak deployed (17.0.1) and identical realm configurations. On some instances I face the problem, while others are fine. Also, the problem is not always present.

I somewhat feel like there's a correlation that this problem occurs when I continue using the affected web frontend after the macbook was on standby/hibernate for a couple of hours.

Metadata

Metadata

Assignees

Labels

area/oidcIndicates an issue on OIDC areakind/bugCategorizes a PR related to a bugteam/rh-iam

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions