Nous allons aujourd’hui nous attarder sur comment sécuriser une api Node.js avec Keycloak. Dans ce tutoriel, vous découvrirez comment créer votre propre API avec Node.js. Nous utiliserons le framework Express pour la construction de notre API (et quelques autres outils)

FICHE TECHNIQUE

Keycloak : 7.0.0.Final

Node : 10.15.3.RELEASE

Express : 4.17.1

Pré-requis

Cas concret

Imaginons que nous devions créer une API pour une application permettant de gérer plusieurs types d’utilisateurs. Cette application offre donc la possibilité de créer des groupes au sein desquels les utilisateurs auront différents droits.

L’utilisateur non inscrit n’accédera qu’à une partie de notre API L’utilisateur inscrit accédera à une partie supplémentaire de l’API et enfin l’administrateur lui aura l’accès à toute l’API.   

Nous avons deux types de droits : USER et ADMIN.    

Notre API disposera donc de 3 niveaux : les routes pour les personnes non inscrites, les routes pour les utilisateurs inscrits ( USER ) et enfin les routes pour les administrateurs ( ADMIN )

Les sources du projet sont disponibles ici

Mise en oeuvre

Etape 1 – initialisation de notre projet Node.js

Notre API Node.js suivra les standards ES6. La mise en place de Babel sera donc nécessaire.

Pour cette premiére étape nous allons créer un nouveau dossier pour notre API. Pour cela rendons-nous dans notre terminal :

mkdir api-keycloak && cd api-keycloak

Après cela nous pouvons initialiser notre projet avec :

npm init

Une série d’information sur notre projet nous sera demandé afin de générer un fichier package.json:

{
  "name": "api-keycloak",
  "version": "1.0.0",
  "description": "how to securise a Node API with Keycloak",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "christophe PARMENTIER",
  "license": "ISC"
}

Nous avons défini le point d’entrée de notre API "main":"index.js" à la racine de notre projet. Nous allons donc créer un fichier index.js :

touch index.js

Nous pouvons dès à présent ouvrir notre éditeur favori.

Etape 2 – Installation des modules requis

Cette étape consiste à installer les modules javascript ( dependencies et devDependencies ) nécessaire à la création de notre API.

Voici la liste des dependencies :

  • express : un micro-framework pour Node.js qui facilitera la création de notre API.
  • body-parser : qui nous permettra de manipuler les requêtes que nous effectuerons.

Allons-y :

npm install --save express body-parser

À ce niveau notre package.json ressemble à ceci :

{
  "name": "api-keycloak",
  "version": "1.0.0",
  "description": "how to securise a Node API with Keycloak",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "christophe PARMENTIER",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1"
  }
}

Maintenant la liste des devDependencies :

  •  babel-cli : permet de compiler des fichiers avec une ligne de commande
  •  babel-preset-es2015 : ce qui nous permettra d’écrire notre API en ES6 !
npm install save-dev babel-cli babel-preset-es2015

Notre package.json:

{
  "name": "api-keycloak",
  "version": "1.0.0",
  "description": "how to securise a Node API with Keycloak",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "christophe PARMENTIER",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1"
  },
  "devDependencies": {
    "babel-cli": "^6.26.0",
    "babel-preset-es2015": "^6.24.1"
  }
}

Préparer notre projet à ES6.

  • À la racine de notre projet, créons un nouveau fichier de configuration Babel qui permettra à notre API de gérer l’ES6 :
touch .babelrc
  • Ouvrons ce fichier et insérons ce code :
{
  "presets": ["es2015"]
}
  • Dans notre package.json, ajoutons un nouveau script :
"scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "babel-node --presets es2015 index.js"
  },

Notre projet est prêt a être implémenté en ES6 !

Etape 3 – Création de notre server Node.js et de notre premiere route

Après tous ces préparatifs, entrons dans le vif du sujet : la création de notre serveur qui permettra de partager l’API.

  • Ajoutons le contenu suivant au fichier index.js:
// Nous importons Express dans notre application
import express from 'express';

// Notre Application utilisera express grace à cette ligne
const app = express();

// Voici notre 1ere route
app.get('/', function(req, res) {
  res.send('Welcome to home page');
});

// Si tout se passe bien notre application écoutera sur le port 3000
app.listen(3000, err => {
  if (err) {
    console.error(err);
  }
  {
    console.log(`APP Listen to port : 3000`);
  }
});

Ces quelques lignes nous serviront à lancer notre serveur et afficher notre première page ! Pour cela dans notre terminal, faisons un : npm start

Notre console indique que notre APP est lancée et elle écoute sur le PORT 3000 : APP Listen to port : 3000

Lançons notre navigateur préféré et rendons-nous sur http://localhost:3000/

Ce dernier doit normalement afficher : Welcome to home page

C’est encore loin de ressembler à une API mais c’est un bon début !

Etape 4 – Mettre en place notre API

Maintenant créons nos 3 routes. Nous pouvons enlever la 1ere route que nous avons écrit à l’étape 4 :

app.get('/', function(req, res) {
  res.send('Welcome to home page');
});
  • Importons body-parser : import bodyParser from 'body-parser';
  • et autorisons notre application à utiliser body-parser :app.use(bodyParser.json());
  • Créons la première route publique pour notre API (toutes nos routes commenceront par /api/) :
app.get('/api/unsecured', function(req, res) {
  res.json({ message: 'This is an unsecured endpoint payload' });
});

Notre fichier index.js à ce stade du tutoriel :

import express from 'express';
import bodyParser from 'body-parser';

const app = express();
app.use(bodyParser.json());

app.get('/api/unsecured', function(req, res) {
  res.json({ message: 'This is an unsecured endpoint payload' });
});

app.listen(3000, err => {
  if (err) {
    console.error(err);
  }
  {
    console.log(`APP Listen to port : 3000`);
  }
});

Après avoir éteint et relancé notre serveur avec npm start la route /api/public retournera donc une res ( réponse ) au format JSON avec un message, qui est : voila notre route public.

Nous savons comment créer une route, voila maintenant les deux autres routes :

app.get('/api/user', function(req, res) {
  res.json({ message: 'This is an USER endpoint payload' });
});

app.get('/api/admin', function(req, res) {
  res.json({ message: 'voila notre route admin' });
});

Notre fichierindex.js :

import express from 'express';
import bodyParser from 'body-parser';

const app = express();
app.use(bodyParser.json());

app.get('/api/unsecured', function(req, res) {
  res.json({ message: 'This is an unsecured endpoint payload' });
});

app.get('/api/user', function(req, res) {
  res.json({ message: 'This is an USER endpoint payload' });
});

app.get('/api/admin', function(req, res) {
  res.json({ message: 'This is an ADMIN endpoint payload' });
});

app.listen(3000, err => {
  if (err) {
    console.error(err);
  }
  {
    console.log(`APP Listen to port : 3000`);
  }
});

Nos routes ne sont, pour le moment, pas protégées et tout le monde peut y accéder. c’est le moment de mettre en place Keycloak.

Etape 5 – Mise en place de Keycloak

Nous allons avoir besoin de deux nouvelles dependencies :

Allons-y :

npm install express-session keycloak-connect

Nos dépendances installées, il est temps de mettre en place tout cela, dans notre fichier index.js :

import express from 'express';
import bodyParser from 'body-parser';
// 1
import session from 'express-session';
import Keycloak from 'keycloak-connect';

const app = express();
app.use(bodyParser.json());

// 2
const memoryStore = new session.MemoryStore();

app.use(
  session({
    secret: 'secretKey',
    resave: false,
    saveUninitialized: true,
    store: memoryStore
  })
);

// 3
const keycloak = new Keycloak({
  store: memoryStore
});

app.use(
  keycloak.middleware({
    logout: '/logout',
    admin: '/'
  })
);

app.get('/api/unsecured', function(req, res) {
  res.json({ message: 'This is an unsecured endpoint payload' });
});

// 4
app.get('/api/user', keycloak.protect('realm:user'), function(req, res) {
  res.json({ message: 'This is an USER endpoint payload' });
});

app.get('/api/admin', keycloak.protect('realm:admin'), function(req, res) {
  res.json({ message: 'This is an ADMIN endpoint payload' });
});

app.listen(3000, err => {
  if (err) {
    console.error(err);
  }
  {
    console.log(`APP Listen to port : 3000`);
  }
});

Explications du code :

  1. Nous commençons par importer express-session et keycloak-connect
  2. Déclarer un nouvelle instance de session.MemoryStore() et mise en place du middleware session dans notre application :
    app.use(
      session({
        secret: 'secretKey',
        resave: false,
        saveUninitialized: true,
        store: memoryStore
      })
    );
  3. Déclarer un nouvelle instance de keycloak-connect avec le store précédemment instancié. Nous autorisons notre application à utiliser le middleware Keycloak et enfin nous déclarons avec le middleware Keycloak une route /logout qui nous permettra de nous déconnecter.
  4. Nous utilisons keycloak.protect() pour définir les droits de chaque utilisateur sur nos routes.

À ce moment si vous décidez de redémarrer votre serveur, vous risquez de tomber sur une erreur de ce type :

Error: ENOENT: no such file or directory, open ‘\*/api-keycloak/keycloak.json’

keycloak-connect fonctionne avec un fichier keycloak.json que nous devons générer à partir de l’administration de notre Keycloak.

Etape 6 – Créer notre ‘keycloak.json’

Vous pouvez vous référer à nos articles pour l‘installation de Keycloak et le paramétrage d’un domaine Keycloak. Ici nous utiliserons Keycloak en local

La configuration du domaine est disponible à la racine du projet.

  • Sur la console d’administration de Keycloak, créons notre nouveau realm nommé API-KEYCLOAK
 
  • Allons dans le menu Clients et créons un nouveau client :
 

Attention de bien mettre le Valid Redirect URIs sur http://localhost:3000/* et de mettre l’Access Type sur bearer-only.

  •  Ensuite rendons-nous dans l’onglet Installation et sélectionnons Keycloak OIDC JSON :
  • Créons un fichier keycloak.json à la racine de notre projet pour y copier le snippet issu de l’administration Keycloak :
{
  "realm": "API-KEYCLOAK",
  "bearer-only": true,
  "auth-server-url": "http://localhost:8080/auth",
  "ssl-required": "external",
  "resource": "api-nodejs",
  "confidential-port": 0
}

Maintenant nous pouvons lancer notre serveur sans crainte d’avoir une erreur.

À ce niveau assurez-vous d’avoir un utilisateur USER et un utilisateur ADMIN (pour rappel … notre guide complet)

Etape 7 – Tester la sécurité de votre API

La sécurité est en place, il serait maintenant interessant de tester que ce que nous venons de mettre en place est fonctionnel. Vous rappelez vous du client Keycloak api-nodejs que nous avons créé ? il est temps de s’en servir en tant que client de notre API.

Pour tester rapidement les accès, nous allons utiliser le flow Resource Owner Password Credentials de OAuth, comme décrit dans la documentation Keycloak.

Si vous utilisez Postman, la collection est disponible à la racine du projet.

  • Pour une demande de token d’utilisation lié à l’utilisateur « user-ineat »
curl -X POST \
  http://localhost:8080/auth/realms/API-KEYCLOAK/protocol/openid-connect/token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'username=user_ineat&password=password&grant_type=password&client_id=api-nodejs'

N’oubliez pas de changer votre client_id ainsi que votre username et password.

  • Pour une demande de token d’utilisation lié à l’utilisateur « admin-ineat »
curl -X POST \
  http://localhost:8080/auth/realms/API-KEYCLOAK/protocol/openid-connect/token \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'username=admin_ineat&password=password&grant_type=password&client_id=api-nodejs'

N’oubliez pas de changer votre client_id ainsi que votre username et password.

La réponse de Keycloak devrait ressembler à ce payload :

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIxb1FqVFJScWVmWFZzaFJvQURLWkNRRTBaZ2ZWQzVnUk10NENxNEdBSVA0In0.eyJqdGkiOiJiMzg3MzFiYy00NTkzLTRmNzItOGMzNC1lMWRlOWM5MTg5ZTQiLCJleHAiOjE1NzE2NzA2MTIsIm5iZiI6MCwiaWF0IjoxNTcxNjcwMzEyLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvQVBJLUtFWUNMT0FLIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjNhZWM1ZmRjLTU2YjctNDBmZS04OTAwLTBmYTA4YmVkNzdmZCIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFwaS1ub2RlanMiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiI2YjIzYzFkZi02NDUwLTRmY2EtODNmOC1iNmRkNWYwMjhlMWMiLCJhY3IiOiIxIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iLCJVU0VSIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ1c2VyX2luZWF0IiwiZW1haWwiOiJ1c2VyX2luZWF0QGluZWF0LWNvbnNlaWwuZnIifQ.FE2Vq_GpH622-zlLQ2ucsu-YuEzuIkoiK9OyQNzfNuqiG7WPZuEQFW1b1a9X_Urg5HPo0htZ0bNh4V5pQ4G1vwyn3djf24by1RBh4W1x4FPk8M5zW3Uw7SZDi4ffUw8qH_HyS2KiYm1_BHVJuIAySgSdj1XcQNPQMR-gyKpZzwDNJB9FEbZJdXJbVLuyZ_deM_xGCrs9Pi5KbezIInCQSg5uzM78-yEq2-jrP-KtCqtyZUbRsZfeStZf0jGVZNz7kqI9EfG-RSiqeDSvKC1b4X8MYKXZOy9Y53w9FoCxXfQiXgDrWJIvpNY2gsIo874ks0QpPSM8EmpAV232ra6jEQ",
    "expires_in": 300,
    "refresh_expires_in": 1800,
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI2MGY2NzgyNC00NDU2LTQ1YTEtOTM5MC1kNGZlZGEwYmE0OWEifQ.eyJqdGkiOiJiMjFjYzg3Yy05ZjU3LTQ1MTUtOGQyOC0xYmUwNzJlMDI0YzkiLCJleHAiOjE1NzE2NzIxMTIsIm5iZiI6MCwiaWF0IjoxNTcxNjcwMzEyLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvQVBJLUtFWUNMT0FLIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL0FQSS1LRVlDTE9BSyIsInN1YiI6IjNhZWM1ZmRjLTU2YjctNDBmZS04OTAwLTBmYTA4YmVkNzdmZCIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJhcGktbm9kZWpzIiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiNmIyM2MxZGYtNjQ1MC00ZmNhLTgzZjgtYjZkZDVmMDI4ZTFjIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iLCJVU0VSIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIn0.kv5SwclYn2vOF78bpXhNkFdNmUOduwBhs-2_qg-Lmuk",
    "token_type": "bearer",
    "not-before-policy": 0,
    "session_state": "6b23c1df-6450-4fca-83f8-b6dd5f028e1c",
    "scope": "email profile"
}

C’est l’access_token renvoyé par Keycloak que vous devez utiliser lors de l’appel à l’api. Il correspond au fameux bearer attendu dans le header Authorization.

Vous pouvez décoder le contenu d’un token JWT sur le site officiel de la spec (via le decoder) … ce qui parait alors plus clair :

Essayez à présent de contacter chacun des endpoints avec votre bearer user. Seul le endpoint /api/admin devrait vous renvoyer un code HTTP 403 :

curl -X GET \
  http://localhost:3000/api/admin \
  -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIxb1FqVFJScWVmWFZzaFJvQURLWkNRRTBaZ2ZWQzVnUk10NENxNEdBSVA0In0.eyJqdGkiOiJiMzg3MzFiYy00NTkzLTRmNzItOGMzNC1lMWRlOWM5MTg5ZTQiLCJleHAiOjE1NzE2NzA2MTIsIm5iZiI6MCwiaWF0IjoxNTcxNjcwMzEyLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvQVBJLUtFWUNMT0FLIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjNhZWM1ZmRjLTU2YjctNDBmZS04OTAwLTBmYTA4YmVkNzdmZCIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFwaS1ub2RlanMiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiI2YjIzYzFkZi02NDUwLTRmY2EtODNmOC1iNmRkNWYwMjhlMWMiLCJhY3IiOiIxIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iLCJVU0VSIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ1c2VyX2luZWF0IiwiZW1haWwiOiJ1c2VyX2luZWF0QGluZWF0LWNvbnNlaWwuZnIifQ.FE2Vq_GpH622-zlLQ2ucsu-YuEzuIkoiK9OyQNzfNuqiG7WPZuEQFW1b1a9X_Urg5HPo0htZ0bNh4V5pQ4G1vwyn3djf24by1RBh4W1x4FPk8M5zW3Uw7SZDi4ffUw8qH_HyS2KiYm1_BHVJuIAySgSdj1XcQNPQMR-gyKpZzwDNJB9FEbZJdXJbVLuyZ_deM_xGCrs9Pi5KbezIInCQSg5uzM78-yEq2-jrP-KtCqtyZUbRsZfeStZf0jGVZNz7kqI9EfG-RSiqeDSvKC1b4X8MYKXZOy9Y53w9FoCxXfQiXgDrWJIvpNY2gsIo874ks0QpPSM8EmpAV232ra6jEQ' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
Access denied

A contrario, si un profil USER tente de contacter /api/user :

curl -X GET \
  http://localhost:3000/api/user \
  -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIxb1FqVFJScWVmWFZzaFJvQURLWkNRRTBaZ2ZWQzVnUk10NENxNEdBSVA0In0.eyJqdGkiOiJiZmNkMTZiNS04Y2ZiLTRmYWMtODUxMi1jMDUxMjZiMjE1ZGMiLCJleHAiOjE1NzE2NzE5MzIsIm5iZiI6MCwiaWF0IjoxNTcxNjcxNjMyLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvQVBJLUtFWUNMT0FLIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjNhZWM1ZmRjLTU2YjctNDBmZS04OTAwLTBmYTA4YmVkNzdmZCIsInR5cCI6IkJlYXJlciIsImF6cCI6ImFwaS1ub2RlanMiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiJlYTY0ZmVkOS0zZTU2LTQwNzUtYThmNC0wMDFlM2NhNTY2OTciLCJhY3IiOiIxIiwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iLCJVU0VSIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJ1c2VyX2luZWF0IiwiZW1haWwiOiJ1c2VyX2luZWF0QGluZWF0LWNvbnNlaWwuZnIifQ.wuHxfZVWcJL-KvrKqo6UiUpYs_0CCCuAwHmvuk62BVsl8bEdjwIHFvPdSOh7UYJNUyBnGbWVBC7bSrVp6_C6amjscAA68tZFqtN4I2wbV2XdrblmVBiNq2gU_8WfhlSSt7rocKn3ePWrXhZyUwDTWkKpEMg6w9fTxhktiXkQZpAveUUwtSp7ROpKUFCEpAogFnDPXTDHw52KOgLlyvyxsGhhY-hkNJSHN8CKExR3l5RYMdUc9lcM1DyWvDXJLU5RYDsM6zk6ZFu8h5O_rjqq5mV4OCkNUQQe7dZnD5LpJvJqC0jB5L8uYrOuKY0msLEeSX7Bii1-5vR9NbISWc0zTg' \
  -H 'Content-Type: application/x-www-form-urlencoded' \

Nous avons bien un code HTTP 200 :

Conclusion

Nous voila maintenant avec une API Node.js sécurisée à deux niveaux. l’application peut-être améliorée, par exemple :

  • Ajouter nodemon pour éviter de couper et redémarrer votre serveur à chaque fois qu’une modification sera effectuée.
  • Mieux découper votre projet (découper son fichier `index.j s` pour créer des dossiers et fichiers ( pour les routes, middleware, etc… ) pour plus de lisibilité.
  •  Mettre en place une base de donnée (par exemple MongoDB avec Mongoose).

Il y a une infinité d’améliorations mais grace à ce poste vous devriez avoir les bases pour commencer un projet d’api sécurisée avec Node.js et Keycloak.

Source du projet : https://github.com/ineat/node-keycloak-tutorials

Série d’articles Keycloak