From 2f76230daf22e4f1f60c7831bd1722afd56c1f79 Mon Sep 17 00:00:00 2001 From: Jorge Lobo <12179834+jorgeavlobo@users.noreply.github.com> Date: Fri, 23 Oct 2026 00:52:03 +0100 Subject: [PATCH] [Web] Keycloak: skip introspection of expired admin token --- data/web/inc/functions.inc.php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/data/web/inc/functions.inc.php b/data/web/inc/functions.inc.php index 09e8ec193..556e7ffdf 100644 --- a/data/web/inc/functions.inc.php +++ b/data/web/inc/functions.inc.php @@ -3104,7 +3104,17 @@ function identity_provider($_action = null, $_data = null, $_extra = null) { case "get-keycloak-admin-token": // get access_token for service account of mailcow client if ($iam_settings['authsource'] !== 'keycloak') return false; - if (isset($iam_settings['access_token'])) { + // only introspect a cached access_token that is not (about to be) expired, + // introspecting an expired token makes Keycloak log INTROSPECT_TOKEN_ERROR + $token_not_expired = false; + if (!empty($iam_settings['access_token'])) { + $token_parts = explode('.', $iam_settings['access_token']); + if (count($token_parts) === 3) { + $token_payload = json_decode(base64_decode(strtr($token_parts[1], '-_', '+/')), true); + $token_not_expired = is_array($token_payload) && isset($token_payload['exp']) && intval($token_payload['exp']) > time() + 30; + } + } + if ($token_not_expired) { // check if access_token is valid $url = "{$iam_settings['server_url']}/realms/{$iam_settings['realm']}/protocol/openid-connect/token/introspect"; $req = http_build_query(array( @@ -3155,6 +3165,8 @@ function identity_provider($_action = null, $_data = null, $_extra = null) { ':key' => 'access_token', ':value' => $res['access_token'] )); + // keep the cached token in sync, so subsequent calls in the same run reuse it + $iam_settings['access_token'] = $res['access_token']; return $res['access_token']; break; }