npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

sso_keycloak

v3.0.0

Published

Adaptateur Keycloak Server-Side

Readme

sso_keycloak

Module Node.js interne de protection d'applications et d'APIs via Keycloak. Il remplace keycloak-connect (officiellement déprécié) en s'appuyant sur openid-client v6 — la bibliothèque de référence recommandée par la spécification OpenID Connect.


Pourquoi ce module existe

keycloak-connect, l'ancien adaptateur officiel Keycloak pour Node.js, est déprécié. Il couplait fortement la logique d'authentification au framework Express et ne supportait pas les standards OIDC modernes (PKCE, backchannel logout RFC 7009, introspection RFC 7662).

sso_keycloak répond à trois besoins concrets :

  • Protéger une application web avec un flow login/mot de passe complet (Authorization Code + PKCE, sessions serveur, déconnexion temps réel via backchannel logout).
  • Protéger une API REST en validant les Bearer tokens via introspection Keycloak à chaque requête — ce qui garantit la révocation immédiate contrairement à la validation JWT locale.
  • Rester réutilisable sur plusieurs projets sans dupliquer la logique d'authentification ni imposer de dépendances framework.

Ce que fait le module

Deux modes d'authentification

Mode session (flow navigateur) L'utilisateur est redirigé vers Keycloak, s'authentifie avec son login/mot de passe, et revient sur l'application via un callback. Les tokens ne transitent jamais côté navigateur — ils sont stockés en session serveur (pattern BFF). Quand l'utilisateur se déconnecte de Keycloak (ou qu'un administrateur révoque sa session), le module reçoit un appel backchannel de Keycloak et détruit la session serveur immédiatement.

Mode bearer (flow API) Chaque requête doit porter un header Authorization: Bearer <token>. Le module envoie ce token à l'endpoint d'introspection de Keycloak (/token/introspect) pour vérifier qu'il est actif. La validation se fait à chaque requête — aucun token révoqué ne peut donc passer entre les mailles.

Frameworks supportés

| Mode | Express | Fastify | |---|---|---| | Session (navigateur) | ✅ | ✅ | | Bearer (API) | ✅ | ✅ |


Comment il le fait — les patterns utilisés

Architecture hexagonale (ports & adaptateurs)

Le module est structuré en trois couches qui ne se connaissent pas mutuellement :

┌─────────────────────────────────────────┐
│           Keycloak (API publique)        │
│  new Keycloak(driver, config)           │
│  kc.protect(app, routes, mode, roles)   │
└────────────────┬────────────────────────┘
                 │ orchestre
┌────────────────▼────────────────────────┐
│         HANDLERS (logique OIDC)         │
│  authCode.js  ·  introspection.js       │
│  backchannel.js                         │
└────────────────┬────────────────────────┘
                 │ traduit via
┌────────────────▼────────────────────────┐
│         ADAPTERS (traduction HTTP)      │
│  Adapter.js  ·  Drivers.js              │
└─────────────────────────────────────────┘

Handlers — Logique OIDC pure, agnostique au framework. Chaque handler reçoit un objet ctx neutre { session, headers, url, body } et retourne une décision { type: 'allow' | 'redirect' | 'deny' | 'session', ... }.

Adapters — Traduisent les objets req/res du framework en ctx, et les décisions en réponses HTTP (res.redirect(), res.status(403).end(), etc.). Un Driver par framework (EXPRESS, FASTIFY) définit comment faire cette traduction.

Keycloak — Orchestre le tout : fait la discovery OIDC, instancie les handlers, dispatche chaque requête vers la bonne stratégie d'auth selon les règles déclarées via protect().

Pattern Strategy

Les trois comportements OIDC sont encapsulés dans des objets interchangeables :

  • authorizationCode — gère le flow navigateur (startLogin, handleCallback, authenticate)
  • introspection — valide un Bearer token via l'endpoint d'introspection
  • backchannel — reçoit les notifications de déconnexion de Keycloak

Chaque handler expose une interface contractuelle. Keycloak ne sait pas lequel il utilise — il appelle toujours handler.authenticate(ctx) et réagit à la décision retournée.

Pattern Driver — extensibilité framework

Chaque framework est encapsulé dans un Driver qui implémente un contrat de 13 méthodes réparties en trois familles :

  • Traduction par requêtegetSession, getHeaders, getBody, getUrl, getSessionId, setPrincipal, redirect, deny, ok
  • Exécutionwrap(logic) adapte la logique au modèle d'exécution du framework (middleware Express vs hook Fastify)
  • SetupcreateStore(), mountSession(), mountAuthRoutes(), install() gèrent l'initialisation côté framework

Pour supporter un nouveau framework (NestJS, Koa...), il suffit d'implémenter ces 13 méthodes dans une nouvelle classe qui étend DriverContrat. Aucune modification du cœur du module n'est nécessaire.

Vérification des rôles par groupe de routes

Les rôles Keycloak requis sont déclarés par règle de protection, pas globalement. Cela permet de définir des niveaux d'accès différents sur une même application :

kc.protect(app, '/admin/*', 'session', 'admin');               // rôle admin requis
kc.protect(app, '/reports', 'bearer',  ['admin', 'analyst']);  // plusieurs rôles
kc.protect(app, '/info',    'bearer');                         // authentifié, sans rôle requis

La vérification est faite par Keycloak après que le handler a authentifié le principal — les handlers eux-mêmes ne connaissent pas les rôles.

Injection de dépendances

Le module n'embarque aucune dépendance framework. Express, express-session, @fastify/session, @fastify/cookie, @fastify/formbody sont portés par les drivers et résolus au moment du require — uniquement si le driver correspondant est utilisé. Cela évite d'installer des dépendances Fastify dans un projet Express, et inversement.

Les dépendances de test (_client, _factories, fetch) sont également injectables, ce qui permet de tester toute la logique métier sans jamais toucher Keycloak.

Store de session avec reaper

Chaque driver crée son propre store de session via createStore(). Contrairement au MemoryStore par défaut d'express-session (qui fuit la mémoire), ce store embarque un reaper — un nettoyeur périodique qui détruit automatiquement les entrées dont le cookie a expiré. Le reaper tourne en arrière-plan sans bloquer l'arrêt du process (timer.unref()).


Structure des fichiers

sso_keycloak/
├── src/
│   ├── index.js                  ← exporte { Keycloak, DRIVERS }
│   ├── Keycloak.js               ← classe principale, protect(), dispatcher
│   ├── core.js                   ← discovery OIDC, instanciation des handlers
│   ├── adapters/
│   │   ├── Adapter.js            ← guard, loginRoute, callbackRoute, backchannelRoute
│   │   ├── DriverContrat.js      ← classe abstraite — contrat des 13 méthodes
│   │   └── Drivers.js            ← ExpressDriver et FastifyDriver
│   └── handlers/
│       ├── authCode.js           ← Authorization Code + PKCE
│       ├── introspection.js      ← validation Bearer par introspection
│       └── backchannel.js        ← réception des logout Keycloak
└── test/

Flux de données — mode session

Navigateur          Express/Fastify         sso_keycloak              Keycloak
    │                      │                     │                       │
    │── GET /page ────────>│                     │                       │
    │                      │── authenticate ────>│                       │
    │                      │<─ redirect /login ──│                       │
    │<─ 302 /login ────────│                     │                       │
    │                      │                     │                       │
    │── GET /login ───────>│                     │                       │
    │                      │── startLogin ──────>│                       │
    │<─ 302 Keycloak ──────│<─ redirect kc_url ──│                       │
    │                      │                     │                       │
    │── POST login ─────────────────────────────────────────────────────>│
    │<─ 302 /callback?code= ─────────────────────────────────────────────│
    │                      │                     │                       │
    │── GET /callback ────>│                     │                       │
    │                      │── handleCallback ──>│                       │
    │                      │                     │── token exchange ────>│
    │                      │                     │<─ access_token ───────│
    │                      │<─ session { user } ─│                       │
    │<─ 302 / ─────────────│                     │                       │
    │                      │                     │                       │
    │                      │<─ POST /backchannel-logout ─────────────────│ (logout admin)
    │                      │── handle ──────────>│                       │
    │                      │                     │── destroy session     │

Flux de données — mode bearer

Client API          Express/Fastify         sso_keycloak              Keycloak
    │                      │                     │                       │
    │── GET /info ────────>│                     │                       │
    │   Authorization:     │── authenticate ────>│                       │
    │   Bearer <token>     │                     │── POST /introspect ──>│
    │                      │                     │   token=<token>       │
    │                      │                     │<─ { active: true }────│
    │                      │<─ allow ────────────│                       │
    │<─ 200 { data } ──────│                     │                       │

Responsabilités de chaque fichier clé

src/index.js

Point d'entrée unique. Exporte Keycloak (la classe principale) et DRIVERS (les drivers Express et Fastify).

src/Keycloak.js

Classe principale du module. Le constructeur prend un driver et une config, lance la discovery OIDC en arrière-plan (#ready), et crée le store de session. La méthode protect(app, routes, mode, roles) enregistre une règle de protection et installe le handler correspondant sur l'app. Un dispatcher interne consulte les règles à chaque requête, sélectionne la première qui matche l'URL, vérifie les rôles si requis, et délègue à la bonne stratégie.

src/core.js

Seule source de vérité pour la discovery OIDC. Appelle openid-client pour interroger le .well-known/openid-configuration de Keycloak, récupère les endpoints (introspection_endpoint, jwks_uri), puis instancie les trois handlers. Toutes les factories passent par ici — aucune ne duplique la discovery.

src/handlers/authCode.js

Implémente le flow Authorization Code + PKCE. Génère le code_verifier et le state, construit l'URL de redirection Keycloak, échange le code contre les tokens au callback, extrait les rôles depuis le access_token (resource_access[clientId].roles), et stocke l'utilisateur en session.

src/handlers/introspection.js

Extrait le Bearer token du header Authorization, POST vers l'endpoint d'introspection Keycloak avec les credentials client en Basic Auth, vérifie que active === true et retourne le principal avec ses rôles. La vérification du rôle requis est déléguée à Keycloak.

src/handlers/backchannel.js

Reçoit le logout_token envoyé par Keycloak lors d'une déconnexion. Vérifie la signature JWT via JWKS (jwks-rsa), extrait le sid Keycloak, retrouve l'ID de session serveur dans la map interne, et détruit la session via sessionStore.destroy(). Maintient une sessionMap (keycloak_sid → session_id) alimentée par trackSession() au moment du login.

src/adapters/DriverContrat.js

Classe abstraite qui définit le contrat des 13 méthodes qu'un driver doit implémenter. Chaque méthode non surchargée lève une erreur à l'exécution. Pour ajouter un nouveau framework, étendre cette classe suffit — aucune modification du reste du module n'est nécessaire.

src/adapters/Adapter.js + Drivers.js

L'Adapter expose quatre méthodes — guard, loginRoute, callbackRoute, backchannelRoute — qui wrappent les handlers dans des fonctions framework-compatibles. Drivers.js contient ExpressDriver et FastifyDriver, deux classes qui étendent DriverContrat et encapsulent les différences d'API entre les deux frameworks.


A lire également


Author

Roty Clément