FIDO2
Introduction
Dans ce guide, vous apprendrez comment un login FIDO basé sur un navigateur utilisant WebAuthn peut être exécuté de manière programmatique via des API REST en utilisant IBM Security Verify. FIDO prend en charge l'authentification de l'utilisateur au premier facteur à l'aide d'un authentificateur FIDO2-based enregistré sur le même site. Cet authentificateur peut être intégré à l'appareil de l'utilisateur (par exemple, la barre tactile du Mac) ou être une clé matérielle portable connectée via USB, Bluetooth ou NFC.
Pour en savoir plus, consultez les Concepts FIDO2.
Si vous souhaitez passer directement à la mise en place d'une application de démonstration fonctionnelle, consultez ce blog.
Intégration plus simple avec la fédération
Au lieu de supporter FIDO2 nativement dans votre application (comme décrit ici), une alternative est d'intégrer IBM Security Verify en utilisant OpenID Connect (ou SAML) et de le laisser s'occuper de l'authentification en dehors de votre application. Vous bénéficiez ainsi de la prise en charge de toutes nos fonctionnalités d'authentification (y compris FIDO2.
Prérequis
L'API WebAuthn qui permet la prise en charge de FIDO2 dans les navigateurs n'est accessible qu'à partir d'une page chargée avec HTTPS. Cela signifie que votre serveur d'application doit prendre en charge les connexions HTTPS.
Les navigateurs doivent se connecter à votre serveur d'application en utilisant un nom d'hôte entièrement qualifié qui est enregistré comme origine dans la définition de la partie se fiant à FIDO2. localhost ne fonctionnera pas. Cela signifie que votre serveur d'application doit être dans le DNS ou, pour les tests, vous pouvez utiliser une entrée dans un fichier d'hôtes local.
Vous devez disposer d'une définition de partie utilisatrice FIDO2 dans votre locataire IBM Security Verify, avec un identifiant de partie utilisatrice et des origines définies pour votre application Web. Voir Créer une partie utilisatrice FIDO pour plus de détails.
Le client API utilisé par l'application doit disposer des autorisations suivantes dans IBM Security Verify:
- Gérer les enregistrements d'authentificateurs pour tous les utilisateurs (pour l'enregistrement)
- Authentifier n'importe quel utilisateur (pour exécuter le flux de connexion)
- Lire les utilisateurs et les groupes (pour consulter les données des utilisateurs)
Pour plus d'informations sur la création d'un client API, voir Créer un client API.
L'application doit avoir acquis un jeton d'accès. Si l'application est un client privilégié, elle peut utiliser le flux "Client Credentials ". Sinon, il doit utiliser le jeton d'accès obtenu via l'OIDC ou un flux d'utilisateurs OAuth (par exemple le flux Authorization Code ou PolicyAuth ).
L'enregistrement doit également être mis en œuvre dans l'application
L'authentification FIDO2 n'autorise que les authentificateurs enregistrés pour une partie utilisatrice spécifique à être utilisés pour l'authentification auprès de cette partie utilisatrice. Cette règle est appliquée par le domaine DNS. Par conséquent, vous ne pouvez pas utiliser un authentificateur enregistré à l'aide du launchpad de l'utilisateur final pour l'authentification dans une application personnalisée.
Ce guide traite également de l'enregistrement.
Variables
Les variables suivantes sont nécessaires pour exécuter les flux décrits dans ce guide :
Variables | Exemple de valeur | Description |
---|---|---|
uRL du locataire | tenant.verify.ibm.com | URL de votre locataire IBM Security Verify. |
jeton d'accès | eWn4Z5xChc3q9B9dqbGgFlsHDh7uhAOnmNeKW5Ez | Le jeton d'accès obtenu à partir du point de terminaison du jeton. |
origine | https://www.example.com | L' URL base utilisée pour accéder à votre application web. Il doit s'agir d'une origine dans la définition de votre Relying Party. |
Recherche de l'identifiant de la partie utilisatrice
Un locataire IBM Security Verify peut prendre en charge plusieurs parties utilisatrices FIDO2. Lors de l'appel des points d'extrémité du serveur FIDO2, l'identifiant de la partie utilisatrice doit être inclus. Vous allez maintenant rechercher l'identifiant de la partie utilisatrice en utilisant l'origine comme clé.
curl -X POST "https://${tenant_url}/v2.0/factors/fido2/relyingparties" -H "Content-Type: application/json" -H "Authorization: Bearer ${access_token}" --data "{\"origin\": \"${origin}\"}"
//Pre-requisites
//var axios = require('axios');
//var tenant_url = "Tenant URL";
//var access_token = "Access Token";
//var origin = "Web App Origin";
async function getRpId(token, origin) {
var request = {
method: 'post',
url: 'https://' + tenant_url + '/v2.0/factors/fido2/relyingparties',
headers: {
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json'
},
data: {
'origin': origin
}
}
var response = await axios(request);
var rpId = response.data.fido2[0].id;
return (rpId);
}
var fido2rp_id = getRpId(access_token, origin);
La réponse JSON à cet appel a le format suivant :
{
"origin": "https://www.example.com",
"fido2": [{
"id": "56753c0b-6c6b-4b4b-8885-dedaec7754a8",
"rpId": "example.com",
"name": "Example RP",
"attestationOptionsPath": "/v2.0/factors/fido2/relyingparties/56753c0b-6c6b-4b4b-8885-dedaec7754a8/attestation/options",
"assertionOptionsPath": "/v2.0/factors/fido2/relyingparties/56753c0b-6c6b-4b4b-8885-dedaec7754a8/assertion/options",
"attestationResultPath": "/v2.0/factors/fido2/relyingparties/56753c0b-6c6b-4b4b-8885-dedaec7754a8/attestation/result",
"assertionResultPath": "/v2.0/factors/fido2/relyingparties/56753c0b-6c6b-4b4b-8885-dedaec7754a8/assertion/result"
}]
}
Vous avez besoin de l'élément fido2[0].id
de cette réponse et de le stocker en tant que fido2rp_id.
Flux d'inscription
Variables utilisateur requises
Outre les variables énumérées ci-dessus, le flux d'enregistrement requiert également les informations suivantes concernant l'utilisateur :
Variables | Exemple de valeur | Description |
---|---|---|
user_uuid | NjQyMDAwRVBPVQ | L'identifiant unique interne de l'utilisateur pour lequel l'enregistrement est effectué. |
nom d'utilisateur | Utilisateur test | Nom d'affichage de l'utilisateur pour lequel l'enregistrement est effectué. S'il n'est pas fourni, le nom d'utilisateur sera utilisé. |
Si l'utilisateur s'est déjà connecté, il est probable que ces deux valeurs soient disponibles dans l'objet JSON de l'utilisateur acquis soit auprès de l'OIDC, soit auprès du point d'accès SCIM.
Navigateur : Déclencher l'enregistrement
Pour une expérience utilisateur transparente, les flux FIDO2 sont généralement exécutés au sein d'une seule page web à l'aide d'appels REST en arrière-plan lancés dans le JavaScript côté client. Ces appels sont traités par le serveur d'application de la partie utilisatrice, ce qui évite les problèmes CORS et la nécessité d'avoir les identifiants de l'API dans le navigateur.
Voici le code JavaScript côté client pour lancer le flux. L'appel à fidoRegister( ) est déclenché par un bouton ou une action de l'utilisateur sur la page. Le code appelle simplement une URL déclenchement dans le serveur d'application de la partie utilisatrice (dans ce cas, /fido/register ).
var locationHostPort = location.hostname
+ (location.port ? ':' + location.port : '');
var baseURL = location.protocol + '//' + locationHostPort;
function fidoRegister() {
var options = {
method: 'GET',
headers: {
'Accept': 'application/json'
}
}
fetch(baseURL + '/fido/register', options).then(response => {
var status = response.status;
response.json().then(data => {
processAttestationOptionsResponse(status, data);
});
});
}
La réponse à cet appel sera un message de réponse d'options qui peut déclencher l'API WebAuthn dans le navigateur. La fonction processAttestationOptionsResponse est définie plus loin dans ce guide.
App Server : Initier l'enregistrement FIDO2 à IBM Security Verify
Lorsque l'App Server reçoit la demande de déclenchement du navigateur, il doit lancer le processus d'enregistrement FIDO2 auprès d' IBM Security Verify. Les options pour l'enregistrement sont transmises dans cet appel :
userId non requis en cas d'utilisation d'un jeton d'accès délégué
Si le jeton d'accès utilisé pour cet appel d'initiation est associé à un utilisateur final, il n'est pas nécessaire de spécifier l'attribut userId. L'enregistrement sera effectué pour l'utilisateur associé au jeton d'accès.
//Pre-requisites
//var axios = require('axios');
//var tenant_url = "Tenant URL";
//var access_token = "Access Token";
//var fidorp_id = "Relying Party ID";
//var user_uuid = "User Unique ID";
//var user_displayname = "User Display Name";
app.get('/fido/register', async (req, res) =>{
// prepare registration options
var options = {
userId: user_uuid,
displayName: user_displayname,
authenticatorSelection: {
requireResidentKey: true, //false to support 2FA-only authenticators
userVerification: "preferred"
},
attestation: "none"
};
var request = {
url: 'https://' + tenant_url
+ "/v2.0/factors/fido2/relyingparties/" + await fidorp_id
+ "/attestation/options",
method: "POST",
headers: {
"Content-type": "application/json",
"Authorization": "Bearer " + access_token
},
data: options
};
try {
var response = await axios(request);
console.log(JSON.stringify(response.data));
res.json(response.data);
} catch (e) {
console.log(e);
res.json({error: "Initiation Failed"});
}
});
La réponse du point d'accès IBM Security Verify FIDO2 a le format suivant :
{
"rp": {
"id": "example.com",
"name": "Example RP"
},
"user": {
"id": "NjQyMDAwRVBPVQ",
"name": "testuser",
"displayName": "Test User"
},
"timeout": 240000,
"challenge": "NphwnbdeNU-8sQMQog8RtUMH4qd3zEzHR_UtPKyTTfs",
"extensions": {
"credentialProtectionPolicy": "userVerificationOptional",
"credProtect": 2
},
"authenticatorSelection": {
"requireResidentKey": true,
"userVerification": "preferred"
},
"attestation": "none",
"pubKeyCredParams": [{
"alg": -7,
"type": "public-key"
}, {
"alg": -257,
"type": "public-key"
}]
}
Cette réponse n'est pas traitée par l'App Server - elle est renvoyée telle quelle au navigateur web.
Navigateur : Convertir base64url en ArrayBuffer et appeler l'API WebAuthn
Le message envoyé par le point d'accès IBM Security Verify FIDO2 comprend un certain nombre de chaînes Base64url-encoded. Ils sont encodés de cette manière pour permettre le transport en JSON, mais ils doivent être des objets ArrayBuffer lorsqu'ils sont transmis à l'API WebAuthn.
Voici une fonction utilitaire simple qui permet d'effectuer la conversion dans le navigateur :
function b64urlToArrayBuf(base64url) {
var binaryString = window.atob(base64url
.replace(/-/g, '+')
.replace(/_/g, '/'));
var byteArray = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; i++) {
byteArray[i] = binaryString.charCodeAt(i);
}
return byteArray.buffer;
}
Voici le code qui effectuera les conversions nécessaires et appellera ensuite la fonction navigator.credentials.create ():
function processAttestationOptionsResponse(rspStatus, serverOptions) {
if (rspStatus == 200) {
// Convert b64url fields into the ArrayBuffer type
//required by WebAuthn API
serverOptions.user.id = b64urlToArrayBuf(serverOptions.user.id);
serverOptions.challenge = b64urlToArrayBuf(serverOptions.challenge);
if (serverOptions["excludeCredentials"] != null
&& serverOptions["excludeCredentials"].length > 0) {
for (var i = 0; i < serverOptions["excludeCredentials"].length; i++) {
var b64uCID = serverOptions.excludeCredentials[i].id;
serverOptions.excludeCredentials[i].id = b64urlToArrayBuf(b64uCID);
}
}
var credCreateOptions = { "publicKey": serverOptions };
// call the webauthn API
navigator.credentials.create(credCreateOptions).then(handleCreateResult);
} else {
console.log("Unable to obtain attestation options. rspStatus: "
+ rspStatus + " response: " + serverOptions);
}
}
Lorsque navigator.credentials.create () est appelé, le navigateur prend en charge le traitement et gère le processus d'enregistrement auprès de l'authentificateur. Il peut demander à l'utilisateur d'interagir avec l'authentificateur ou de saisir un code PIN.
Une fois cette opération terminée, la fonction handleCreateResult est appelée. Il doit traiter le résultat de l'appel API et le renvoyer au serveur FIDO2 (via le serveur d'application de la partie utilisatrice).
Navigateur : Convertir ArrayBuffer en Base64url et envoyer le résultat
Le résultat renvoyé par l'API WebAuthn comprend un certain nombre d'objets ArrayBuffer qui doivent être Base64url-encoded avant d'être renvoyés au serveur FIDO2.
Voici une fonction utilitaire simple qui permet d'effectuer la conversion dans le navigateur :
function arrayBufToB64url(byteArrayBuffer) {
var binaryString = '';
var byteArray = new Uint8Array(byteArrayBuffer);
for (var i = 0; i < byteArray.byteLength; i++) {
binaryString += String.fromCharCode(byteArray[i]);
}
return window.btoa(binaryString)
.replace(/\+/g, '-')
.replace(/\//g, '_');
}
Voici le code qui effectuera les conversions nécessaires et enverra ensuite le résultat de WebAuthn au serveur d'application de la partie utilisatrice :
function handleCreateResult(result) {
// success
var createResponse = result;
console.log("Received response from authenticator.");
// marshall the important parts of the response into an object which
// we'll later send to the server for validation.
// convert buffer arrays into base64url for sending in JSON.
attestationResponseObject = {
"id": createResponse.id,
"rawId": createResponse.id,
"nickname": "My FIDO Authenticator",
"type": "public-key",
"response": {
"clientDataJSON": arrayBufToB64url(createResponse.response.clientDataJSON),
"attestationObject": arrayBufToB64url(createResponse.response.attestationObject)
}
};
// if there are extensions results, include those
var clientExtensionResults = createResponse.getClientExtensionResults();
if (clientExtensionResults != null) {
attestationResponseObject["getClientExtensionResults"] = clientExtensionResults;
}
var options = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(attestationResponseObject)
}
fetch(baseURL + '/fido/attestation/result', options).then(response => {
var status = response.status;
response.json().then(data => {
if (status == 200) {
// Done - redirect to received URL
window.location.href = data.url;
} else {
console.log("Unexpected HTTP response");
}
});
});
}
Après la conversion, le résultat WebAuthn envoyé au serveur d'application de la partie utilisatrice a le format suivant :
{
"id": "4Aa44jAC3EKueXBfkBkHS4h0HQU0mSe7gFrFGI4JW2U",
"rawId": "4Aa44jAC3EKueXBfkBkHS4h0HQU0mSe7gFrFGI4JW2U",
"nickname": "My FIDO Authenticator",
"type": "public-key",
"response": {
"clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG...IjpmYWxzZX0=",
"attestationObject": "o2NmbXRkbm9uZWdh...xOXT2pwACnM-I="
},
"getClientExtensionResults": {}
}
App Server : Transférer le résultat WebAuthn
Le serveur d'application de la partie utilisatrice ne modifie que légèrement le résultat de WebAuthn. L'attribut enabled est ajouté. Cela indique à IBM Security Verify d'activer cet authentificateur dans le cadre de l'enregistrement. Le résultat est ensuite transmis à IBM Security Verify:
//Pre-requisites
//var axios = require('axios');
//var tenant_url = "Tenant URL";
//var access_token = "Access Token";
//var fidorp_id = "Relying Party ID";
app.post('/fido/attestation/result', async (req, res) =>{
var body = req.body;
body.enabled = true;
var request = {
url: 'https://' + tenant_url
+ "/v2.0/factors/fido2/relyingparties/" + await fidorp_id
+ "/attestation/result",
method: "POST",
headers: {
"Content-type": "application/json",
"Authorization": "Bearer " + access_token
},
data: body
};
try {
response = await axios(request);
console.log(JSON.stringify(response.data));
if (response.status == 200) {
req.session.registerData = response.data
res.json({url:'/fido/registerdone'});
} else {
res.json({url:'/fido/error'});
}
} catch (e) {
console.log(e);
res.json({url:'/fido/error'});
}
});
La réponse du serveur FIDO2 a le format suivant :
{
"id": "78c686ee-9c09-4c46-b3e1-f401e5ff4526",
"userId": "642000EPOU",
"type": "fido2",
"created": "2021-01-26T19:52:30.730Z",
"updated": "2021-01-26T19:52:30.730Z",
"enabled": true,
"validated": false,
"attributes": {
"attestationType": "None",
"attestationFormat": "none",
"nickname": "My FIDO Authenticator",
"userVerified": true,
"userPresent": true,
"credentialId": "4Aa44jAC3EKueXBfkBkHS4h0HQU0mSe7gFrFGI4JW2U",
"credentialPublicKey": "v2EzYi03YTMmYTECYi0xAWItMlgglH...qcAApzPi/w==",
"rpId": "example.com",
"counter": 1170
},
"references": {
"rpUuid": "56753c0b-6c6b-4b4b-8885-dedaec7754a8"
}
}
La manière dont le serveur d'application et le code côté client gèrent les enregistrements réussis (ou échoués) est laissée à l'appréciation de l'application. Dans le code ci-dessus, le serveur d'application renvoie une URL vers laquelle le code côté client redirige. Il s'agit soit d'une page de succès, soit d'une page d'erreur. En cas de succès, la réponse du serveur FIDO2 est enregistrée dans la session de l'application afin que les informations qu'elle contient soient disponibles lors du rendu de la page de succès.
Flux d'authentification
Le processus d'authentification FIDO2 est assez similaire au processus d'enregistrement.
Navigateur : Déclencher l'enregistrement
Pour une expérience utilisateur transparente, les flux FIDO2 sont généralement exécutés au sein d'une seule page web à l'aide d'appels REST en arrière-plan lancés dans le JavaScript côté client. Ces appels sont traités par le serveur d'application de la partie utilisatrice, ce qui évite les problèmes CORS et la nécessité d'avoir les identifiants de l'API dans le navigateur.
Voici le code JavaScript côté client pour lancer le flux. L'appel à fidoAuthenticate( ) est déclenché par un bouton ou une action de l'utilisateur sur la page. Le code appelle simplement une URL déclenchement dans le serveur d'application de la partie utilisatrice (dans ce cas, /fido/authenticate ).
var locationHostPort = location.hostname
+ (location.port ? ':' + location.port : '');
var baseURL = location.protocol + '//' + locationHostPort;
function fidoAuthenticate() {
var options = {
method: 'GET',
headers: {
'Accept': 'application/json'
}
}
fetch(baseURL + '/fido/authenticate', options).then(response => {
var status = response.status;
response.json().then(data => {
processAssertionOptionsResponse(status, data);
});
});
}
La réponse à cet appel sera un message de réponse d'options qui peut déclencher l'API WebAuthn dans le navigateur. La fonction processAssertionOptionsResponse est définie plus loin dans ce guide.
App Server : Initier l'authentification FIDO2 à IBM Security Verify
Lorsque l'App Server reçoit la demande de déclenchement du navigateur, il doit lancer le flux d'authentification FIDO2 au niveau d' IBM Security Verify. Les options d'authentification sont transmises dans cet appel :
//Pre-requisites
//var axios = require('axios');
//var tenant_url = "Tenant URL";
//var access_token = "Access Token";
//var fidorp_id = "Relying Party ID";
app.get('/fido/authenticate', async (req, res) =>{
// prepare authentication options
var options = {
//userId = user_uuid, if supporting 2FA-only authenticators
userVerification: "preferred"
};
var request = {
url: 'https://' + tenant_url
+ "/v2.0/factors/fido2/relyingparties/" + await fidorp_id
+ "/assertion/options",
method: "POST",
headers: {
"Content-type": "application/json",
"Authorization": "Bearer " + access_token
},
data: options
};
try {
var response = await axios(request);
console.log(JSON.stringify(response.data));
res.json(response.data);
} catch (e) {
console.log(e);
res.json({error: "Initiation Failed"});
}
});
La réponse du point d'accès IBM Security Verify FIDO2 a le format suivant :
{
"rpId": "example.com",
"timeout": 240000,
"challenge": "k0ytEzA0SJfBBg4g2-ZvtfwqMK9mrdbRf5SyFlH4vaA"
}
Cette réponse n'est pas traitée par l'App Server - elle est renvoyée telle quelle au navigateur web.
Navigateur : Convertir base64url en ArrayBuffer et appeler l'API WebAuthn
Le message envoyé par le point d'accès IBM Security Verify FIDO2 comprend un certain nombre de chaînes Base64url-encoded. Ils sont encodés de cette manière pour permettre le transport en JSON, mais ils doivent être des objets ArrayBuffer lorsqu'ils sont transmis à l'API WebAuthn.
Voici une fonction utilitaire simple qui permet d'effectuer la conversion dans le navigateur :
function b64urlToArrayBuf(base64url) {
var binaryString = window.atob(base64url
.replace(/-/g, '+')
.replace(/_/g, '/'));
var byteArray = new Uint8Array(binaryString.length);
for (var i = 0; i < binaryString.length; i++) {
byteArray[i] = binaryString.charCodeAt(i);
}
return byteArray.buffer;
}
Voici le code qui effectuera les conversions nécessaires et appellera ensuite la fonction navigator.credentials.get ():
function processAssertionOptionsResponse(rspStatus, serverOptions) {
if (rspStatus == 200) {
// Convert Base64url fields to ArrayBuffers required by WebAuthn API
serverOptions.challenge = b64urlToArrayBuf(serverOptions.challenge);
if (serverOptions["allowCredentials"] != null
&& serverOptions["allowCredentials"].length > 0) {
for (var i = 0; i < serverOptions["allowCredentials"].length; i++) {
var b64uCID = serverOptions.allowCredentials[i].id;
serverOptions.allowCredentials[i].id = b64urlToArrayBuf(b64uCID);
}
}
var credRequestOptions = {
"publicKey": serverOptions
};
// call the webauthn API
navigator.credentials.get(credRequestOptions).then(handleGetResult);
} else {
console.log("Unable to obtain assertion options. Response: "
+ serverOptions);
}
}
Lorsque navigator.credentials.get () est appelé, le navigateur prend en charge le traitement et gère le processus d'authentification avec l'authentificateur. Il peut demander à l'utilisateur d'interagir avec l'authentificateur ou de saisir un code PIN.
Une fois l'opération terminée, la fonction handleGetResult est appelée. Il doit traiter le résultat de l'appel API et le renvoyer au serveur FIDO2 (via le serveur d'application de la partie utilisatrice).
Navigateur : Convertir ArrayBuffer en Base64url et envoyer le résultat
Le résultat renvoyé par l'API WebAuthn comprend un certain nombre d'objets ArrayBuffer qui doivent être Base64url-encoded avant d'être renvoyés au serveur FIDO2.
Voici une fonction utilitaire simple qui permet d'effectuer la conversion dans le navigateur :
function arrayBufToB64url(byteArrayBuffer) {
var binaryString = '';
var byteArray = new Uint8Array(byteArrayBuffer);
for (var i = 0; i < byteArray.byteLength; i++) {
binaryString += String.fromCharCode(byteArray[i]);
}
return window.btoa(binaryString)
.replace(/\+/g, '-')
.replace(/\//g, '_');
}
Voici le code qui effectuera les conversions nécessaires et enverra ensuite le résultat de WebAuthn au serveur d'application de la partie utilisatrice :
function handleGetResult(getResponse) {
// marshall the important parts of the response into an object
// which we send to the server for validation.
// ArrayBuffers are converted to Base64url for transmission as JSON.
assertionResponseObject = {
"id": getResponse.id,
"rawId": getResponse.id,
"type": "public-key",
"response": {
"clientDataJSON": arrayBufToB64url(getResponse.response.clientDataJSON),
"authenticatorData": arrayBufToB64url(getResponse.response.authenticatorData),
"signature": arrayBufToB64url(getResponse.response.signature),
"userHandle": arrayBufToB64url(getResponse.response.userHandle)
}
};
// if there are extensions results, include those
var clientExtensionResults = getResponse.getClientExtensionResults();
if (clientExtensionResults != null) {
assertionResponseObject["getClientExtensionResults"] = clientExtensionResults;
}
// send to server for result processing
var options = {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(assertionResponseObject)
}
fetch(baseURL + '/fido/assertion/result', options).then(response => {
var status = response.status;
response.json().then(data => {
if (status == 200) {
// Done - redirect to received URL
window.location.href = data.url;
} else {
console.log("Unexpected HTTP response code: " + status);
}
});
});
}
Après la conversion, le résultat WebAuthn envoyé au serveur d'application de la partie utilisatrice a le format suivant :
{
"id": "4Aa44jAC3EKueXBfkBkHS4h0HQU0mSe7gFrFGI4JW2U",
"rawId": "4Aa44jAC3EKueXBfkBkHS4h0HQU0mSe7gFrFGI4JW2U",
"type": "public-key",
"response": {
"clientDataJSON": "eyJ0eXBlIjoid2ViY...WxzZX0=",
"authenticatorData": "XOHtoHS8ddDaN...lUr0WKLS71KUFAAAFzw==",
"signature": "MEQCIEANaAq57CsfBwue...Ljl4zVxVTQ==",
"userHandle": "NjQyMDAwRVBPVQ=="
},
"getClientExtensionResults": {}
}
App Server : Transférer le résultat WebAuthn
Le serveur d'application de la partie utilisatrice ne modifie pas le résultat de WebAuthn. Il est simplement transmis à IBM Security Verify:
//Pre-requisites
//var axios = require('axios');
//var tenant_url = "Tenant URL";
//var access_token = "Access Token";
//var fidorp_id = "Relying Party ID";
app.post('/fido/assertion/result', async (req, res) =>{
// Add ?returnJwt=true to URL to get back a JWT
var request = {
url: 'https://' + tenant_url
+ "/v2.0/factors/fido2/relyingparties/" + await fidorp_id
+ "/assertion/result",
method: "POST",
headers: {
"Content-type": "application/json",
"Authorization": "Bearer " + access_token
},
data: req.body
};
try {
var response = await axios(request);
req.session.user = await getUserSCIMById(response.data.userId);
res.json({'url': '/authenticatedone'});
} catch (e) {
console.log(e);
res.json({'url': '/fido/error'});
}
});
La réponse du serveur FIDO2 a le format suivant :
{
"id": "78c686ee-9c09-4c46-b3e1-f401e5ff4526",
"userId": "642000EPOU",
"type": "fido2",
"created": "2021-01-26T19:52:30.730Z",
"updated": "2021-01-26T19:52:30.730Z",
"attempted": "2021-01-27T10:16:56.374Z",
"enabled": true,
"validated": true,
"attributes": {
"attestationType": "None",
"attestationFormat": "none",
"nickname": "My FIDO Authenticator",
"userVerified": true,
"userPresent": true,
"credentialId": "4Aa44jAC3EKueXBfkBkHS4h0HQU0mSe7gFrFGI4JW2U",
"credentialPublicKey": "v2EzYi03YTMmYTECYi0xAWItMlggl...IsTl09qcAApzPi/w==",
"rpId": "www.example.com",
"counter": 1487
},
"references": {
"rpUuid": "56753c0b-6c6b-4b4b-8885-dedaec7754a8"
}
}
Dans cette réponse, l'attribut userId contient l'identifiant unique IBM Security Verify de l'utilisateur authentifié. Elle peut être utilisée pour obtenir des informations sur l'utilisateur en appelant l'interface IBM Security Verify M :
//Pre-requisites
//var axios = require('axios');
//var tenant_url = "Tenant URL";
//var access_token = "Access Token";
async function getUserSCIMById(userid) {
var request = {
method: 'get',
url: 'https://' + tenant_url + '/v2.0/Users/' + userid,
headers: {'Authorization': 'Bearer ' + access_token}
};
try {
var response = await axios(request);
return response.data;
} catch (error) {
console.log(error);
};
}
La manière dont le serveur d'application et le code côté client gèrent l'authentification réussie (ou échouée) est laissée à l'appréciation de l'application. Dans le code ci-dessus, le serveur d'application renvoie une URL vers laquelle le code côté client redirige. Il s'agit soit d'une page de succès, soit d'une page d'erreur. En cas de succès, l'objet SCIM de l'utilisateur authentifié est sauvegardé dans la session d'application afin que les informations qu'il contient soient disponibles lors du traitement ultérieur de l'application.
FIDO2 dans le cadre du flux policyauth
Si vous utilisez l'authentification FIDO2 dans un flux d' authentification de politique, vous devez ajouter ?returnJwt=true à l'appel au point de terminaison /assertion/result. IBM Security Verify renvoie alors un JWT dans un attribut d' assertion qui peut être utilisé pour appeler le point de terminaison du jeton.
Jon Harry, IBM Security
Updated about 1 month ago