Skip to content

fix(server-nestjs/keycloak): refresh admin client token before it expires#2301

Merged
KepoParis merged 1 commit into
mainfrom
fix/keycloak-admin-token-refresh
Jul 15, 2026
Merged

fix(server-nestjs/keycloak): refresh admin client token before it expires#2301
KepoParis merged 1 commit into
mainfrom
fix/keycloak-admin-token-refresh

Conversation

@KepoParis

Copy link
Copy Markdown
Contributor

Issues liées

Issues numéro: N/A


Quel est le comportement actuel ?

Le client d'administration Keycloak s'authentifie une seule fois au démarrage du serveur (onModuleInit) et ne rafraîchit jamais son access token. Le token expire après ~60 secondes (durée de vie par défaut dans Keycloak), donc tout appel à l'API admin effectué ensuite (par exemple ensureProjectGroups lors de la réconciliation des groupes projet) échoue avec un 401, remonté par la librairie sous la forme d'une erreur opaque : Network response was not OK.

Quel est le nouveau comportement ?

  • KeycloakClientService ré-authentifie le client admin toutes les 58 secondes (juste en dessous de la durée de vie par défaut du token), via un intervalle démarré dans onModuleInit et nettoyé dans onModuleDestroy.
  • La requête de token est épinglée sur le realm master (où vit l'utilisateur admin) : la librairie résout l'endpoint de token à partir de client.realmName, qui est basculé vers le realm projet après le démarrage. Le realm projet est restauré après chaque authentification, y compris en cas d'échec.
  • Un échec de rafraîchissement est journalisé sans interrompre la boucle : la tentative suivante a lieu au tick suivant.
  • Ajout de keycloak-client.service.spec.ts (7 tests) couvrant le cycle de vie : authentification initiale, rafraîchissement périodique, épinglage du realm, résilience aux échecs, arrêt à la destruction du module et absence de configuration.

Cette PR introduit-elle un breaking change ?

Non. Aucun changement d'API ni de configuration. À noter : si la durée de vie de l'access token du realm master est configurée en dessous de 60 s sur une instance Keycloak, il faudra abaisser ADMIN_TOKEN_REFRESH_INTERVAL_MS en conséquence.

Autres informations

Cause racine confirmée dans le code de @keycloak/keycloak-admin-client@24.0.5 : auth() stocke le token une seule fois sans aucun mécanisme de rafraîchissement, et utilise realmName pour construire l'endpoint de token.

@KepoParis KepoParis added this to the 9.20.0 milestone Jul 10, 2026
@KepoParis KepoParis self-assigned this Jul 10, 2026
@KepoParis KepoParis added the bug Something isn't working label Jul 10, 2026
@github-actions github-actions Bot added the built label Jul 10, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Hey !

The security scan report for the current pull request is available here.

@shikanime shikanime left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just use @nestjs/scheduler like the reconciler

@shikanime

Copy link
Copy Markdown
Member

Generally we also refresh using the already available access token https://github.com/keycloak/keycloak/tree/main/js/libs/keycloak-admin-client#keycloak-admin-client

@KepoParis KepoParis force-pushed the fix/keycloak-admin-token-refresh branch from ff5bf21 to bb6314e Compare July 13, 2026 09:48
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Hey !

The security scan report for the current pull request is available here.

@KepoParis

Copy link
Copy Markdown
Contributor Author

Thanks for the review @shikanime — both points are addressed in bb6314e:

  • @nestjs/schedule: the manual setInterval/clearInterval and the OnModuleDestroy hook are gone. The refresh is now a refreshAdminToken() method decorated with @Interval(ADMIN_TOKEN_REFRESH_INTERVAL_MS) (ScheduleModule.forRoot() is already registered in main.module.ts), guarded so it no-ops until the initial authentication succeeded.
  • Refresh with the existing token: the periodic refresh now uses the refresh_token grant with the client's stored refresh token, through the lib's own auth() — it posts the same grant_type=refresh_token request to the token endpoint as the README's openid-client snippet, without adding a dependency (auth() accepts grantType: 'refresh_token' and stores the rotated refreshToken after every call, so each tick uses the fresh one). One addition over the README snippet: the refresh token is bound to an SSO session capped by SSO Session Max (10h by default), so a failed refresh falls back to a full password re-authentication and the client recovers instead of failing forever.

The spec was reworked accordingly: it drives the @Interval through ScheduleModule with fake timers, and uses msw against a real KcAdminClient (same approach as #2305) to assert the wire-level grant sequence (password, then refresh_token, fallback to password on failure) and the refresh-token rotation.

@KepoParis KepoParis requested a review from shikanime July 13, 2026 09:58
@KepoParis KepoParis force-pushed the fix/keycloak-admin-token-refresh branch from bb6314e to a1744d2 Compare July 13, 2026 09:58
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Hey !

The security scan report for the current pull request is available here.

Comment thread apps/server-nestjs/src/modules/keycloak/keycloak-client.service.ts Outdated
shikanime
shikanime previously approved these changes Jul 13, 2026
Comment thread apps/server-nestjs/src/modules/keycloak/keycloak-client.service.ts
@KepoParis KepoParis force-pushed the fix/keycloak-admin-token-refresh branch 2 times, most recently from 9a0f97f to f2c1021 Compare July 14, 2026 17:15
@KepoParis KepoParis requested a review from shikanime July 14, 2026 17:16
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Hey !

The security scan report for the current pull request is available here.

StephaneTrebel
StephaneTrebel previously approved these changes Jul 15, 2026

@StephaneTrebel StephaneTrebel left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, avec des questions pour la suite

Comment thread apps/server-nestjs/src/modules/keycloak/keycloak-client.service.ts
Comment thread apps/server-nestjs/src/modules/keycloak/keycloak-client.service.ts Outdated
Comment thread apps/server-nestjs/src/modules/keycloak/keycloak-client.service.ts Outdated
Comment thread apps/server-nestjs/src/modules/keycloak/keycloak-client.service.ts
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Hey !

The security scan report for the current pull request is available here.

@KepoParis KepoParis force-pushed the fix/keycloak-admin-token-refresh branch from 03d73c5 to b52ab5c Compare July 15, 2026 08:58
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Hey !

The security scan report for the current pull request is available here.

@cloud-pi-native-sonarqube

Copy link
Copy Markdown

@KepoParis KepoParis requested a review from StephaneTrebel July 15, 2026 09:15
@KepoParis KepoParis added this pull request to the merge queue Jul 15, 2026
Merged via the queue into main with commit cf26176 Jul 15, 2026
34 checks passed
@KepoParis KepoParis deleted the fix/keycloak-admin-token-refresh branch July 15, 2026 12:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working built

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants