Configurer l’authentification renforcée pour les API

Avec l’authentification renforcée, les applications qui permettent d’accéder à différents types de ressources peuvent exiger des utilisateurs qu’ils s’authentifient à l’aide d’un mécanisme plus puissant pour accéder à des informations sensibles ou effectuer certaines transactions.

Par exemple, l’utilisateur d’une application bancaire peut être autorisé à transférer de l’argent entre ses comptes uniquement après avoir confirmé son identité à l’aide de l’authentification multifacteur (MFA).

Lorsque votre audience est une API, vous pouvez mettre en œuvre une authentification renforcée avec Auth0 en utilisant des permissions, des jetons d’accès et des Actions. Lorsqu’une application souhaite accéder aux ressources protégées d’une API, elle doit fournir un jeton d’accès. Les ressources auxquelles elle aura accès dépendent des autorisations incluses dans le jeton d’accès. Ces autorisations sont exprimées sous la forme de permissions.

Valider les jetons d’accès pour la MFA

Outre la vérification de la permission, l’API doit valider le jeton d’accès pour :

  • Vérifier la signature du jeton, utilisée pour vérifier que l’expéditeur du jeton est bien celui qu’il prétend être et pour s’assurer que le message n’a pas été modifié en cours de route.

  • Valider les demandes standard :

    Demande Description
    exp Expiration du jeton
    iss Émetteur du jeton
    aud Destinataire du jeton

Scénario : Transactions bancaires avec notifications poussées

Dans le scénario suivant, une application authentifie un utilisateur à l’aide de son nom d’utilisateur et de son mot de passe, puis demande le solde de son compte. Avant de récupérer les informations sur le solde du compte, l’utilisateur doit s’authentifier avec le facteur Guardian poussé.

L’API bancaire peut accepter deux niveaux d’autorisation différents : consulter le solde du compte (permission view:balance) ou transférer des fonds (permission transfer:funds). Lorsque l’application demande à l’API de récupérer le solde de l’utilisateur, le jeton d’accès doit contenir la permission view:balance. Pour transférer de l’argent vers un autre compte, le jeton d’accès doit contenir la permission transfer:funds.

Processus

  1. L’utilisateur se connecte à l’application en utilisant l’authentification par nom d’utilisateur et mot de passe. La connexion standard permet à l’utilisateur d’interagir avec l’API et de récupérer son solde. Cela signifie que le jeton d’accès que l’application reçoit après l’authentification de l’utilisateur contient la permission view:balance.

  2. L’application envoie une demande à l’API pour récupérer le solde, en utilisant le jeton d’accès identifiants.

  3. L’API valide le jeton et envoie les informations relatives au solde à l’application, afin que l’utilisateur puisse les consulter.

  4. L’utilisateur souhaite transférer des fonds d’un compte à un autre, ce qui est considéré comme une transaction sensible nécessitant la permission transfer:funds. L’application envoie une demande à l’API en utilisant le même jeton d’accès.

  5. L’API valide le jeton et refuse l’accès parce qu’il manque au jeton la permission transfer:funds.

  6. L’application redirige vers Auth0, où une action est utilisée pour demander à l’utilisateur de s’authentifier avec la MFA puisqu’un champ d’application de grande valeur a été demandé. Une fois que l’utilisateur s’est authentifié avec succès avec la MFA, un nouveau jeton d’accès comprenant la permission appropriée est généré et envoyé à l’application dans le cadre de la réponse.

  7. L’application envoie une autre demande de transfert de fonds à l’aide du nouveau jeton d’accès, qui inclut cette fois la permission transfer:funds.

  8. L’API valide le jeton, l’ignore et procède à l’opération.

Prérequis

Pour ce scénario, vous devez configurer les éléments suivants dans Dashboard :

Créer une action

Créez une action qui demande à l’utilisateur de s’authentifier avec la MFA lorsque la permission transfer:funds est demandée. Allez à Dashboard > Actions > Flux et créez une action qui contient ce qui suit :

exports.onExecutePostLogin = async (event, api) => {
  const CLIENTS_WITH_MFA = ['REPLACE_WITH_YOUR_CLIENT_ID'];
  // run only for the specified clients
  if (CLIENTS_WITH_MFA.includes(event.client.client_id)) {
    // ask for MFA only if scope transfer:funds was requested
    if (event.transaction.requested_scopes.indexOf('transfer:funds') > -1)
      api.multifactor.enable('any', { allowRememberBrowser: false });
    }
  }
}

Was this helpful?

/

  • La variable CLIENTS_WITH_MFA qui contient les identifiants clients des applications à laquelle vous voulez appliquer cette action. Vous pouvez la supprimer (et la condition if qui suit) si vous n’en avez pas besoin.

  • La propriété event.transaction.requested_scopes contient toutes les permissions pour lesquelles la demande d’authentification a été effectuée. Si elle inclut la valeur transfer:funds, nous demandons alors la MFA en définissant la propriété context.multifactor sur la valeur appropriée. Dans ce cas, nous demandons la MFA en utilisant une notification poussée push.

Configurer l’application

Configurez l’application pour qu’elle envoie la demande d’authentification appropriée à l’API, selon que l’utilisateur tente ou non d’effectuer la transaction sensible qu’est le transfert de fonds. Notez que la seule différence entre les deux demandes d’authentification (avec ou sans MFA) est la permission.

  • Avec MFA :

    https://{yourDomain}/authorize?
    audience=https://my-banking-api&
    scope=openid%20view:balance%20transfer:funds&
    response_type=id_token%20token&
    client_id={yourClientId}&
    redirect_uri={https://yourApp/callback}&
    nonce=NONCE&
    state=OPAQUE_VALUE

    Was this helpful?

    /

  • Sans MFA :

    https://{yourDomain}/authorize?
    audience=https://my-banking-api&
    scope=openid%20view:balance&
    response_type=id_token%20token&
    client_id={yourClientId}&
    redirect_uri={https://yourApp/callback}&
    nonce=NONCE&
    state=OPAQUE_VALUE

    Was this helpful?

    /

Paramètre Réglage
audience Définissez l’identifiant de votre API (trouvez-le dans API Settings (Paramètres API)). Nous avons réglé le nôtre sur https://my-banking-api.
response_type Défini sur id_token token afin que nous obtenions à la fois un jeton d’ID et un jeton d’accès dans la réponse.
client_ID Définissez l’ID client de votre application (trouvez-le dans Application Settings (Paramètres de l’application)).
redirect_URI Définissez sur une URL dans votre application vers laquelle Auth0 devrait rediriger l’utilisateur après l’authentification (trouvez-le dans Application Settings (Paramètres de l’application)).
nonce Défini sur une valeur de chaîne sécurisée qui sera incluse dans la réponse de Auth0. Ceci est utilisé pour empêcher les attaques par réinsertion de jeton et est requis pour le jeton response_type=id_token token.
state Défini sur une valeur opaque que Auth0 inclut lors de la redirection vers l’application. Cette valeur doit être utilisée par l’application pour empêcher les attaques CSRF.

Configurer l’API

Configurez l’API pour valider le jeton entrant et vérifier les autorisations accordées.

  1. Configurez deux points de terminaison pour notre API : GET /balance : pour récupérer le solde actuel POST /transfer : pour transférer des fonds

  2. Utilisez Node.js et plusieurs modules :

    1. express : ajoute le cadre d’applications Web Express.

    2. jwks-rsa : récupère les clés de connexion RSA à partir d’un point de terminaison JWKS (JSON Web Key Set). À l’aide de expressJwtSecret nous pouvons générer un fournisseur de secrets qui fournit la bonne clé de connexion à express-jwt en fonction du kid dans l’en-tête JWT.

    3. express-jwt : vous permet d’authentifier les requêtes HTTP à l’aide de jetons JWT dans vos applications Node.js. Il fournit plusieurs fonctions qui facilitent le travail avec les JWT.

    4. express-jwt-authz : vérifie si le jeton d’accès contient une permission précise.

  3. Installez les dépendances : npm install express express-jwt jwks-rsa express-jwt-authz --save

  4. Définissez les points de terminaison de l’API, créez une fonction de médiation pour valider le jeton d’accès et sécurisez les points de terminaison à l’aide de ce logiciel médiateur. Le code de votre fichier server.js ressemble à l’exemple de script suivant :

    // set dependencies
        const express = require('express');
        const app = express();
        const jwt = require('express-jwt');
        const jwksRsa = require('jwks-rsa');
        const jwtAuthz = require('express-jwt-authz');
    
        // Create middleware for checking the JWT
        const checkJwt = jwt({
          // Dynamically provide a signing key based on the kid in the header and the signing keys provided by the JWKS endpoint
          secret: jwksRsa.expressJwtSecret({
            cache: true,
            rateLimit: true,
            jwksRequestsPerMinute: 5,
            jwksUri: `https://{yourDomain}/.well-known/jwks.json`
          }),
    
          // Validate the audience and the issuer
          audience: 'https://my-banking-api', // replace with your API's audience, available at Dashboard > APIs
          issuer: 'https://{yourDomain}/',
          algorithms: [ 'RS256' ] // we are using RS256 to sign our tokens
        });
    
        // create retrieve balance endpoint
        app.get('/balance', checkJwt, jwtAuthz(['view:balance']), function (req, res) {
          // code that retrieves the user's balance and sends it back to the calling app
          res.status(201).send({message: "This is the GET /balance endpoint"});
        });
    
    
        // create transfer funds endpoint
        app.post('/transfer', checkJwt, jwtAuthz(['transfer:funds']), function (req, res) {
          // code that transfers funds from one account to another
          res.status(201).send({message: "This is the POST /transfer endpoint"});
        });
    
        // launch the API Server at localhost:8080
        app.listen(8080);
        console.log('Listening on http://localhost:8080');

    Was this helpful?

    /
    Chaque fois que l’API reçoit une demande, il se produit ce qui suit :

    1. Le point de terminaison appelle le logiciel médiateur checkJwt.

    2. express-jwt décode le jeton et achemine la demande, l’en-tête et les données utiles vers jwksRsa.expressJwtSecret.

    3. jwks-rsa télécharge toutes les clés de connexion à partir du point de terminaison JWKS et vérifie si l’une des clés de connexion correspond à la valeur kid dans l’en-tête du jeton d’accès. Si aucune des clés de connexion ne correspond à la valeur kid reçue, une erreur sera générée. S’il y a une correspondance, transmettez la bonne clé de connexion à express-jwt.

    4. express-jwt poursuit sa propre logique pour valider la signature du jeton, l’expiration, l’audience et l’émetteur.

    5. jwtAuthz vérifie si la permission requise par le point de terminaison fait partie du jeton d’accès. Si les permissions spécifiées sont absents du jeton d’accès, la demande est rejetée avec un message d’erreur 403.

En savoir plus