From c577f21e91ac3d9e290644550925325890a7dbb5 Mon Sep 17 00:00:00 2001 From: Ben Immanuel Date: Fri, 1 Aug 2025 13:57:30 +0100 Subject: [PATCH] Support non-JWT refresh tokens in Keycloak. While the Keycloak Server does issue refresh tokens with JWT payload, many other OIDC servers do not, and the OIDC spec does not require this. And currently Keycloak always tries to parse refresh token as JWT, which breaks login flow for such servers. Closes #149 Signed-off-by: Ben Immanuel --- lib/keycloak.d.ts | 6 ++++++ lib/keycloak.js | 12 +++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/keycloak.d.ts b/lib/keycloak.d.ts index 9f5e07c..c204c76 100644 --- a/lib/keycloak.d.ts +++ b/lib/keycloak.d.ts @@ -218,6 +218,12 @@ export interface KeycloakInitOptions { */ messageReceiveTimeout?: number + /** + * Configures whether the adapter should include the refresh token in the JWT. + * @default true + */ + refreshTokenIsJWT?: boolean + /** * When onLoad is 'login-required', sets the 'ui_locales' query param in compliance with section 3.1.2.1 * of the OIDC 1.0 specification. diff --git a/lib/keycloak.js b/lib/keycloak.js index c59e5f7..bf19491 100755 --- a/lib/keycloak.js +++ b/lib/keycloak.js @@ -85,6 +85,8 @@ export default class Keycloak { /** @type {string=} */ scope; messageReceiveTimeout = 10000; + /** @type {boolean=} */ + refreshTokenIsJWT; /** @type {string=} */ idToken; /** @type {KeycloakTokenParsed=} */ @@ -272,6 +274,12 @@ export default class Keycloak { this.messageReceiveTimeout = initOptions.messageReceiveTimeout; } + if (typeof initOptions.refreshTokenIsJWT === 'boolean') { + this.refreshTokenIsJWT = initOptions.refreshTokenIsJWT; + } else { + this.refreshTokenIsJWT = true; + } + await this.#loadConfig(); await this.#check3pCookiesSupported() await this.#processInit(initOptions); @@ -1535,7 +1543,9 @@ export default class Keycloak { if (refreshToken) { this.refreshToken = refreshToken; - this.refreshTokenParsed = decodeToken(refreshToken); + if (this.refreshTokenIsJWT === true) { + this.refreshTokenParsed = decodeToken(refreshToken); + } } else { delete this.refreshToken; delete this.refreshTokenParsed;