feat: improve authenticateClient by avoiding unnecessary token creation#2443
Conversation
WalkthroughChecks existing Keycloak access tokens by decoding the JWT to read the Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @controlplane/src/core/services/Keycloak.ts:
- Around line 41-48: The current check in the Keycloak token validation treats
tokens missing an exp claim as valid indefinitely; update the logic in the
Keycloak method that inspects this.client.accessToken (where decodeJwt is used)
so that absence of exp does NOT cause an early return—only return early when exp
exists and exp*1000 > Date.now(); if exp is missing or expired, fall through to
re-authenticate. Ensure you only change the conditional that now reads something
like `if (!exp || exp * 1000 > Date.now()) return;` to require exp to be present
and valid (e.g., `if (exp && exp * 1000 > Date.now()) return;`), so tokens
without expiration force re-authentication.
🧹 Nitpick comments (2)
controlplane/src/core/services/Keycloak.ts (2)
50-58: Token refresh logic looks correct.The refresh token flow properly uses the
refresh_tokengrant type and maintains consistency with the password flow by using the sameadmin-cliclient ID.Consider adding logging when token refresh succeeds to aid in debugging and monitoring:
🔎 Suggested logging enhancement
if (this.client.refreshToken) { await this.client.auth({ grantType: 'refresh_token', refreshToken: this.client.refreshToken, clientId: 'admin-cli', }); - + this.logger.debug('Successfully refreshed Keycloak admin access token'); return; }
59-61: Add logging in the error handler for better observability.The silent catch block makes it difficult to diagnose token refresh failures. Adding debug-level logging would help identify whether the issue is token decode failure, refresh token expiration, or network problems.
🔎 Proposed enhancement
- } catch { - // ignore + } catch (error) { + this.logger.debug(error, 'Token validation or refresh failed, falling back to password grant'); }
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
controlplane/src/core/services/Keycloak.ts
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: wilsonrivera
Repo: wundergraph/cosmo PR: 2100
File: docker/keycloak/realm.json:1768-1774
Timestamp: 2025-08-04T17:50:30.553Z
Learning: The sso-cookie-authenticator in Keycloak authentication flows must use "REQUIRED" as the requirement setting, not "ALTERNATIVE". This is due to implementation constraints of the custom SSOCookieAuthenticator class.
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: build_push_image
- GitHub Check: build_test
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: Analyze (go)
🔇 Additional comments (2)
controlplane/src/core/services/Keycloak.ts (2)
44-48: Consider the security implications of not verifying JWT signatures.
decodeJwtonly decodes the token without cryptographic signature verification. While this may be acceptable for the admin client's self-managed token (since Keycloak will validate it on actual API calls), it does mean that a tampered or forged token could bypass the expiration check.Please confirm that skipping signature verification aligns with your security requirements. If this token could be exposed or if there's a risk of tampering, consider using
jwtVerifyfrom jose instead, which validates signatures against a JWKS endpoint.
6-6: No action needed. Thejosedependency is properly declared incontrolplane/package.jsonas"jose": "^5.2.4". The import statement at line 6 is valid and will not cause runtime failures.Likely an incorrect or invalid review comment.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2443 +/- ##
==========================================
+ Coverage 1.49% 62.77% +61.28%
==========================================
Files 292 296 +4
Lines 46983 41440 -5543
Branches 431 4266 +3835
==========================================
+ Hits 703 26015 +25312
+ Misses 45997 15404 -30593
+ Partials 283 21 -262
🚀 New features to boost your workflow:
|
…eycloak-admin-authentication-on
…eycloak-admin-authentication-on
…eycloak-admin-authentication-on
…eycloak-admin-authentication-on
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@controlplane/src/core/services/Keycloak.ts`:
- Around line 59-60: The catch block in Keycloak (method handling token
lifecycle) currently logs "Failed to refresh the existing access token..." even
when decodeJwt throws; update the logic in the function that calls decodeJwt and
refreshExistingToken to differentiate failures: wrap decodeJwt(...) in its own
try/catch and log a decode-specific warning (e.g., "Failed to decode JWT, will
request a new token") and only catch refresh errors from
refreshExistingToken(...) with the existing refresh message, or adjust the
logger.warn message to generically cover both cases; reference decodeJwt and
refreshExistingToken (and the Keycloak class logger.warn) when making the
changes so the logs accurately reflect whether decoding or refreshing failed.
♻️ Duplicate comments (1)
controlplane/src/core/services/Keycloak.ts (1)
44-47: Don’t treat missingexpas valid indefinitely.Line 45 returns early when
expis missing, which makes non-expiring tokens effectively permanent. That’s the same concern raised earlier; it’s still present. Please requireexpto be present and valid before returning.✅ Proposed fix
- const { exp } = decodeJwt(this.client.accessToken); - if (!exp || exp * 1000 > Date.now()) { - // Either the access token never expires or it hasn't expired + const { exp } = decodeJwt(this.client.accessToken); + if (exp && exp * 1000 > Date.now()) { + // Access token is valid and hasn't expired return; }
…eycloak-admin-authentication-on
…eycloak-admin-authentication-on
…eycloak-admin-authentication-on
The goal of this PR is to avoid creating Keycloak access tokens every time we call
authenticateClient.To achieve this we are taking a look at the existing token (if any) and, if that token still valid, we don't do anything, however, if there is a refresh token, we try to refresh the current access token with it and if everything fails or is unavailable, we fallback to the original flow or creating a new access token.
Summary by CodeRabbit
Bug Fixes
New Features
Behavior
✏️ Tip: You can customize this high-level summary in your review settings.
Checklist