Mise en place d'un exemple d'application
Getting Started with Adaptive Access for Native Applications
Introduction
Ce guide explique comment utiliser Adaptive Access dans une application native NodeJS à l’aide des packages Adaptive Browser SDK et Adaptive Proxy SDK.
- Adaptive Browser SDK : diffuse le JavaScript côté client chargé de la collecte des données navigateur.
- Adaptive Proxy SDK : fournit les classes qui gèrent les transactions d’authentification basées sur des politiques, y compris l’évaluation du risque.
Pour en savoir plus sur les entités impliquées, voir Adaptive SDK.
Prérequis
- Avoir créé une application personnalisée et l’avoir intégrée à Adaptive Sign-On
(voir On-boarding de l’application). - NodeJS installé sur la machine de développement. Téléchargez-le ici : https://nodejs.org/en/.
Mise en place de l’environnement
1. Créer un projet Node
npm init -y
Un fichier package.json
est créé pour gérer les dépendances.
2. Installer les dépendances
npm install @ibm-verify/adaptive-browser @ibm-verify/adaptive-proxy \
express body-parser express-session
3. Créer index.js
index.js
touch index.js
Configuration du serveur Express
Éditez index.js
:
const express = require('express');
const app = express();
// Middleware JSON
app.use(express.json());
// Sessions
const session = require('express-session');
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true
}));
// Exposer le Browser SDK à /static/adaptive-v1.js
app.use(
'/static/adaptive-v1.js',
express.static(__dirname + '/node_modules/@ibm-verify/adaptive-browser/dist/adaptive-v1.min.js')
);
Ajout d’une page de connexion
1. Créer login.html
login.html
touch login.html
2. Intégrer le snippet web
Copiez le snippet fourni lors de l’on-boarding dans la balise <head>
; il chargera le Browser SDK.
Exemple de squelette :
<html>
<head>
<!-- Collez ici votre snippet -->
</head>
<body>
<h1>Login</h1>
<form name="login" action="/login" method="POST">
<label for="username">Username:</label><br>
<input type="text" id="username" name="username"><br>
<label for="password">Password:</label><br>
<input type="password" id="password" name="password"><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
Chargez le Browser SDK uniquement sur les pages login et recollection.
S’assurer que la collecte est terminée
Ajoutez le script suivant dans <head>
après le snippet :
<script>
window.addEventListener('load', () => {
const form = document.forms.login;
form.addEventListener('submit', (e) => {
e.preventDefault(); // Empêche l’envoi immédiat
getSessionId().then((sid) => { // fournie par le Browser SDK
const hidden = document.createElement('input');
hidden.type = 'hidden';
hidden.name = 'sessionId';
hidden.value = sid;
form.appendChild(hidden);
form.submit(); // Soumission finale
});
});
});
</script>
Servir la page de connexion
Complétez index.js
:
// Ressources statiques
app.get('/login', (_, res) => res.sendFile(__dirname + '/login.html'));
// Page d’accueil : affiche les tokens si authentifié
app.get('/', (req, res) => {
if (!req.session.token) return res.redirect('/login');
res.send(req.session.token);
});
// Lancer le serveur
app.listen(3000, () => console.log('Listening on port 3000'));
Domaine autorisé
Accédez au serveur via un domaine listé dans Allowed domains lors de l’on-boarding (sinon la collecte échouera). En développement, ajoutez une entrée
127.0.0.1 www.<ALLOWED.DOMAIN>
dans/etc/hosts
.
Ressource statique requise
Exposez un GIF 1×1 px à /icons/blank.gif
:
app.use(
'/icons/blank.gif',
express.static(__dirname + '/node_modules/@ibm-verify/adaptive-browser/blank.gif')
);
Initialiser le Proxy SDK
Ajoutez à index.js
(remplacez par vos valeurs) :
const Adaptive = require('@ibm-verify/adaptive-proxy');
const adaptive = new Adaptive({
tenantUrl: 'https://<tenant>.verify.ibm.com',
clientId: '<client_id>',
clientSecret:'<client_secret>'
});
Sécurisez
clientSecret
en production (vault/env var).
Authentifier un utilisateur
Ajoutez à index.js
avant app.listen
:
const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));
let identitySourceId; // mis en cache
app.post('/login', async (req, res) => {
const { username, password, sessionId } = req.body;
const context = {
sessionId,
userAgent: req.headers['user-agent'],
ipAddress: req.ip
};
try {
// 1. Pré-évaluation ( politique + facteurs autorisés )
const assess = await adaptive.assessPolicy(context);
if (assess.status === 'deny')
return res.status(403).send({ error: 'Denied' });
// 2. Recherche de la source (une seule fois)
if (!identitySourceId) {
const sources = await adaptive.lookupIdentitySources(
context, assess.transactionId, 'Cloud Directory'
);
identitySourceId = sources[0].id;
}
// 3. Vérifier le mot de passe
const result = await adaptive.evaluatePassword(
context, assess.transactionId, identitySourceId, username, password
);
handleEvaluateResult(req, res, context, result);
} catch (err) {
console.error(err);
const msg = err.response?.data?.messageDescription || err.message;
res.status(403).send({ error: msg });
}
});
// Gestion centralisée des réponses
function handleEvaluateResult(req, res, context, result) {
if (result.status === 'deny')
return res.status(403).send({ error: 'Denied' });
if (result.status === 'allow') {
req.session.token = result.token;
return res.redirect('/');
}
if (result.status === 'requires') {
const fact = result.enrolledFactors.find(f => f.type === 'emailotp');
if (!fact)
return res.status(500).send({ error: 'Email OTP not available' });
req.session.transactionId = result.transactionId;
req.session.currentFactor = 'emailotp';
adaptive.generateEmailOTP(context, result.transactionId, fact.id)
.then(() => res.sendFile(__dirname + '/otp.html'))
.catch(e => res.status(500).send({ error: e.message }));
}
}
Page OTP (otp.html
)
otp.html
)<html>
<body>
<h1>Submit OTP</h1>
<form action="/otp" method="POST">
<label for="otp">OTP :</label><br>
<input type="text" id="otp" name="otp"><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
Ajoutez la route :
app.post('/otp', async (req, res) => {
const otp = req.body.otp;
const { sessionId, transactionId } = req.session;
const context = {
sessionId,
userAgent: req.headers['user-agent'],
ipAddress: req.ip
};
try {
const result = await adaptive.evaluateEmailOTP(context, transactionId, otp);
if (result.status === 'deny')
return res.status(403).send({ error: 'Denied' });
if (result.status === 'allow') {
req.session.token = result.token;
return res.redirect('/');
}
} catch (e) {
const msg = e.response?.data?.messageDescription || e.message;
res.status(403).send({ error: msg });
}
});
Lancer et tester
node index.js
- Allez sur
http://www.<ALLOWED.DOMAIN>:3000/
- Page de login → saisissez username/password
- Recevez et saisissez l’OTP e-mail
- Vous êtes redirigé sur
/
: les jetons s’affichent.
Vous avez configuré avec succès Adaptive Access pour une application native ! 🎉
Updated 16 days ago