Hoe kan ik een Node.js-app migreren van ADAL naar MSAL?

Microsoft Authentication Library for Node (MSAL Node) is nu de aanbevolen SDK voor het inschakelen van verificatie en autorisatie voor uw toepassingen die zijn geregistreerd op het Microsoft Identity-platform. In dit artikel worden de belangrijke stappen beschreven die u moet doorlopen om uw apps te migreren van Active Directory Authentication Library for Node (ADAL Node) naar MSAL Node.

Vereisten

Instellingen voor app-registratie bijwerken

Wanneer u met ADAL Node werkt, hebt u waarschijnlijk het Azure AD v1.0-eindpunt gebruikt. Apps die van ADAL naar MSAL migreren, moeten overschakelen naar het Azure AD v2.0-eindpunt.

MSAL installeren en importeren

  1. installeer het MSAL Node-pakket via npm:
  npm install @azure/msal-node
  1. Daarna importeert u MSAL Node in uw code:
  const msal = require('@azure/msal-node');
  1. Verwijder tenslotte het ADAL Node-pakket en verwijder eventuele verwijzingen in uw code:
  npm uninstall adal-node

MSAL initialiseren

In ADAL Node initialiseert u een AuthenticationContext object, waarmee vervolgens de methoden worden weergegeven die u kunt gebruiken in verschillende verificatiestromen (bijvoorbeeld acquireTokenWithAuthorizationCode voor web-apps). Bij het initialiseren is de enige verplichte parameter de instantie-URI:

var adal = require('adal-node');

var authorityURI = "https://login.microsoftonline.com/common";
var authenticationContex = new adal.AuthenticationContext(authorityURI);

In MSAL Node hebt u in plaats daarvan twee alternatieven: Als u een mobiele app of een desktop-app bouwt, maakt u een exemplaar van een PublicClientApplication object. De constructor verwacht een configuratieobject dat clientId op zijn minst de parameter bevat. MSAL stelt de instantie-URI https://login.microsoftonline.com/common standaard in als u deze niet opgeeft.

const msal = require('@azure/msal-node');

const pca = new msal.PublicClientApplication({
        auth: {
            clientId: "YOUR_CLIENT_ID"
        }
    });

Notitie

Als u de https://login.microsoftonline.com/common instantie in v2.0 gebruikt, kunnen gebruikers zich aanmelden met een Microsoft Entra-organisatie of een persoonlijk Microsoft-account (MSA). Als u in MSAL Node aanmelding wilt beperken tot een Microsoft Entra-account (hetzelfde gedrag als bij ADAL Node), gebruikt https://login.microsoftonline.com/organizations u in plaats daarvan.

Als u daarentegen een web-app of een daemon-app bouwt, maakt u een instantie van een ConfidentialClientApplication object. Met dergelijke apps moet u ook een clientreferentie opgeven, zoals een clientgeheim of een certificaat:

const msal = require('@azure/msal-node');

const cca = new msal.ConfidentialClientApplication({
        auth: {
            clientId: "YOUR_CLIENT_ID",
            clientSecret: "YOUR_CLIENT_SECRET"
        }
    });

PublicClientApplication en ConfidentialClientApplication zijn, in tegenstelling tot ADAL's AuthenticationContext, gebonden aan een client-id. Dit betekent dat als u verschillende client-id's hebt die u in uw toepassing wilt gebruiken, u een nieuw MSAL-exemplaar voor elk exemplaar moet instantiëren. Zie voor meer informatie: Initialisatie van MSAL-knooppunt

MSAL configureren

Wanneer u apps bouwt op het Microsoft Identity-platform, bevat uw app veel parameters met betrekking tot verificatie. In het ADAL-knooppunt heeft het AuthenticationContext object een beperkt aantal configuratieparameters waarmee u het kunt instantiëren, terwijl de resterende parameters vrij in uw code hangen (bijvoorbeeld clientSecret):

var adal = require('adal-node');

var authority = "https://login.microsoftonline.com/YOUR_TENANT_ID"
var validateAuthority = true,
var cache = null;

var authenticationContext = new adal.AuthenticationContext(authority, validateAuthority, cache);
  • authority: URL die een token-instantie identificeert
  • validateAuthority: een functie die voorkomt dat uw code tokens aanvraagt bij een mogelijk schadelijke instantie
  • cache: stelt de tokencache in die wordt gebruikt door dit AuthenticationContext-exemplaar. Als deze parameter niet is ingesteld, wordt in de geheugencache een standaardwaarde gebruikt

MSAL Node gebruikt daarentegen een configuratieobject van het type Configuratie. Deze bevat de volgende eigenschappen:

const msal = require('@azure/msal-node');

const msalConfig = {
    auth: {
        clientId: "YOUR_CLIENT_ID",
        authority: "https://login.microsoftonline.com/YOUR_TENANT_ID",
        clientSecret: "YOUR_CLIENT_SECRET",
        knownAuthorities: [],
    },
    cache: {
        // your implementation of caching
    },
    system: {
        loggerOptions: { /** logging related options */ }
    }
}


const cca = new msal.ConfidentialClientApplication(msalConfig);

Als opmerkelijk verschil heeft MSAL geen vlag om autorisatievalidatie uit te schakelen en worden instanties altijd standaard gevalideerd. MSAL vergelijkt uw aangevraagde instantie met een lijst met autoriteiten die bekend zijn bij Microsoft of een lijst met autoriteiten die u in uw configuratie hebt opgegeven. Raadpleeg voor meer informatie Configuratieopties

Overschakelen naar MSAL-API

De meeste openbare methoden in ADAL-knooppunten hebben equivalenten in MSAL Node:

ADAL MSAL Opmerkingen
acquireToken acquireTokenSilent De naam is gewijzigd en verwacht nu een accountobject
acquireTokenWithAuthorizationCode acquireTokenByCode
acquireTokenWithClientCredentials acquireTokenByClientCredential
acquireTokenWithRefreshToken acquireTokenByRefreshToken Handig voor het migreren van geldige vernieuwingstokens
acquireTokenWithDeviceCode acquireTokenByDeviceCode Abstraheert nu het ophalen van gebruikerscode (zie hieronder)
acquireTokenWithUsernamePassword acquireTokenByUsernamePassword

Sommige methoden in ADAL-knooppunt worden echter afgeschaft, terwijl MSAL Node nieuwe methoden biedt:

ADAL MSAL Opmerkingen
acquireUserCode N.v.t. Samengevoegd met acquireTokeByDeviceCode (zie hierboven)
N.v.t. acquireTokenOnBehalfOf Een nieuwe methode waarmee de OBO-stroom wordt geabstraheerd
acquireTokenWithClientCertificate N.v.t. Niet meer nodig omdat certificaten nu worden toegewezen tijdens de initialisatie (zie configuratieopties)
N.v.t. getAuthCodeUrl Een nieuwe methode waarmee de URL-constructie van het van eindpunt voor autorisatie wordt geabstraheerd

Bereiken gebruiken in plaats van bronnen

Een belangrijk verschil tussen v1.0 versus v2.0-eindpunten is de wijze waarop de bronnen worden geopend. In ADAL Node registreert u eerst een machtiging voor de app-registratieportal en vraagt u vervolgens een toegangstoken aan voor een bron (zoals Microsoft Graph), zoals hieronder wordt weergegeven:

authenticationContext.acquireTokenWithAuthorizationCode(
    req.query.code,
    redirectUri,
    resource, // e.g. 'https://graph.microsoft.com'
    clientId,
    clientSecret,
    function (err, response) {
        // do something with the authentication response
    }
);

MSAL Node ondersteunt alleen het v2.0-eindpunt . Het v2.0-eindpunt maakt gebruik van een bereikgericht model voor toegang tot bronnen. Wanneer u dus een toegangstoken voor een bron aanvraagt, moet u ook het bereik voor die bron opgeven:

const tokenRequest = {
    code: req.query.code,
    scopes: ["https://graph.microsoft.com/User.Read"],
    redirectUri: REDIRECT_URI,
};

pca.acquireTokenByCode(tokenRequest).then((response) => {
    // do something with the authentication response
}).catch((error) => {
    console.log(error);
});

Een voordeel van het bereikgerichte model is de mogelijkheid om dynamische bereiken te gebruiken. Bij het bouwen van toepassingen met v1.0 moest u de volledige set machtigingen ( ook wel statische bereiken genoemd) registreren die door de toepassing zijn vereist voor de gebruiker om toestemming te geven op het moment van aanmelding. In v2.0 kunt u de bereikparameter gebruiken om de machtigingen aan te vragen op het moment dat u ze wilt (dus dynamische bereiken). Hierdoor kan de gebruiker incrementele toestemming verstrekken voor bereiken. Dus als u aan het begin wilt dat de gebruiker zich aanmeldt bij uw toepassing en u geen toegang nodig hebt, kunt u dit doen. Als u later de agenda van de gebruiker moet kunnen lezen, kunt u vervolgens het agendabereik aanvragen in de methoden acquireToken en toestemming van de gebruiker krijgen. Zie voor meer informatie Bronnen en bereiken

Beloftes gebruiken in plaats van opnieuw aanroepen

In het ADAL-knooppunt wordt opnieuw aanroepen gebruikt voor elke bewerking nadat de verificatie is geslaagd en wordt een antwoord verkregen:

var context = new AuthenticationContext(authorityUrl, validateAuthority);

context.acquireTokenWithClientCredentials(resource, clientId, clientSecret, function(err, response) {
    if (err) {
        console.log(err);
    } else {
        // do something with the authentication response
    }
});

In MSAL Node worden in plaats daarvan beloftes gebruikt:

    const cca = new msal.ConfidentialClientApplication(msalConfig);

    cca.acquireTokenByClientCredential(tokenRequest).then((response) => {
        // do something with the authentication response
    }).catch((error) => {
        console.log(error);
    });

U kunt ook de syntaxis async/await gebruiken die bij ES8 wordt geleverd:

    try {
        const authResponse = await cca.acquireTokenByCode(tokenRequest);
    } catch (error) {
        console.log(error);
    }

Logboekregistratie inschakelen

In ADAL Node configureert u logboekregistratie afzonderlijk op elke plaats in uw code:

var adal = require('adal-node');

//PII or OII logging disabled. Default Logger does not capture any PII or OII.
adal.logging.setLoggingOptions({
  log: function (level, message, error) {
    console.log(message);

    if (error) {
        console.log(error);
    }
  },
  level: logging.LOGGING_LEVEL.VERBOSE, // provide the logging level
  loggingWithPII: false  // Determine if you want to log personal identification information. The default value is false.
});

In MSAL Node maakt logboekregistratie deel uit van de configuratie-opties en wordt deze gemaakt met de initialisatie van het MSAL Node-exemplaar:

const msal = require('@azure/msal-node');

const msalConfig = {
    auth: {
        // authentication related parameters
    },
    cache: {
        // cache related parameters
    },
    system: {
        loggerOptions: {
            loggerCallback(loglevel, message, containsPii) {
                console.log(message);
            },
            piiLoggingEnabled: false,
            logLevel: msal.LogLevel.Verbose,
        }
    }
}

const cca = new msal.ConfidentialClientApplication(msalConfig);

Tokencaching inschakelen

In ADAL Node hebt u de mogelijkheid gehad om een tokencache in het geheugen te importeren. De tokencache wordt gebruikt als parameter bij het initialiseren van een AuthenticationContext object:

var MemoryCache = require('adal-node/lib/memory-cache');

var cache = new MemoryCache();
var authorityURI = "https://login.microsoftonline.com/common";

var context = new AuthenticationContext(authorityURI, true, cache);

MSAL Node maakt standaard gebruik van een tokencache in het geheugen. U hoeft deze niet expliciet te importeren; in-memory tokencache wordt weergegeven als onderdeel van de ConfidentialClientApplication en PublicClientApplication klassen.

const msalTokenCache = publicClientApplication.getTokenCache();

Belangrijk is dat uw vorige tokencache met ADAL Node niet overdraagbaar is naar MSAL Node, omdat cacheschema's niet compatibel zijn. U kunt echter de geldige vernieuwingstokens gebruiken die uw app eerder hebt verkregen met ADAL Node in MSAL Node. Zie de sectie over vernieuwingstokens voor meer informatie.

U kunt uw cache ook naar de schijf schrijven door uw eigen invoegtoepassing voor cache op te geven. De cache-invoegtoepassing moet de interface ICachePluginimplementeren. Net als bij logboekregistratie maakt in de cache opslaan deel uit van de configuratieopties en wordt deze gemaakt met de initialisatie van het MSAL Node-exemplaar:

const msal = require('@azure/msal-node');

const msalConfig = {
    auth: {
        // authentication related parameters
    },
    cache: {
        cachePlugin // your implementation of cache plugin
    },
    system: {
        // logging related options
    }
}

const msalInstance = new ConfidentialClientApplication(msalConfig);

Een voorbeeld van een invoegtoepassing voor cache kan worden geïmplementeerd zoals hieronder:

const fs = require('fs');

// Call back APIs which automatically write and read into a .json file - example implementation
const beforeCacheAccess = async (cacheContext) => {
    cacheContext.tokenCache.deserialize(await fs.readFile(cachePath, "utf-8"));
};

const afterCacheAccess = async (cacheContext) => {
    if(cacheContext.cacheHasChanged) {
        await fs.writeFile(cachePath, cacheContext.tokenCache.serialize());
    }
};

// Cache Plugin
const cachePlugin = {
    beforeCacheAccess,
    afterCacheAccess
};

Als u openbare clienttoepassingen ontwikkelt, zoals desktop-apps, biedt de Microsoft Authentication Extensions for Node veilige mechanismen voor clienttoepassingen voor het uitvoeren van serialisatie en persistentie van tokens op meerdere platforms. Ondersteunde platforms zijn Windows, Mac en Linux.

Notitie

Microsoft Authentication Extensions for Node wordt niet aanbevolen voor webtoepassingen, omdat dit kan leiden tot problemen met schalen en prestaties. In plaats daarvan worden web-apps aanbevolen om de cache in sessie te behouden.

Logica verwijderen rond vernieuwingstokens

In ADAL Node werden de vernieuwingstokens (RT) weergegeven, zodat u oplossingen kunt ontwikkelen rond het gebruik van deze tokens door ze in de cache op te slaan en de acquireTokenWithRefreshToken methode te gebruiken. Typische scenario's waarbij RT's met name relevant zijn:

  • Langdurige diensten die acties uitvoeren, waaronder het vernieuwen van dashboards namens de gebruikers waarop de gebruikers niet meer zijn verbonden.
  • WebFarm-scenario's voor het inschakelen van de client om de RT naar de webservice te brengen (in de cache opslaan wordt uitgevoerd bij de client, versleutelde cookie en niet bij de server).

MSAL Node, samen met andere MSALs, biedt geen vernieuwingstokens om veiligheidsredenen. In plaats daarvan verwerkt MSAL het vernieuwen van tokens voor u. Daarom hoeft u hiervoor geen logica meer te bouwen. U kunt echter gebruikmaken van uw eerder verkregen (en nog steeds geldige) vernieuwingstokens uit de cache van het ADAL-knooppunt om een nieuwe set tokens met MSAL Node op te halen. Om dit te doen, biedt MSAL Node acquireTokenByRefreshToken, die gelijk is aan de methode van acquireTokenWithRefreshToken ADAL Node:

var msal = require('@azure/msal-node');

const config = {
    auth: {
        clientId: "ENTER_CLIENT_ID",
        authority: "https://login.microsoftonline.com/ENTER_TENANT_ID",
        clientSecret: "ENTER_CLIENT_SECRET"
    }
};

const cca = new msal.ConfidentialClientApplication(config);

const refreshTokenRequest = {
    refreshToken: "", // your previous refresh token here
    scopes: ["https://graph.microsoft.com/.default"],
    forceCache: true,
};

cca.acquireTokenByRefreshToken(refreshTokenRequest).then((response) => {
    console.log(response);
}).catch((error) => {
    console.log(error);
});

Raadpleeg het ADAL-knooppunt naar het MSAL Node-migratievoorbeeld voor meer informatie.

Notitie

We raden u aan de oudere ADAL Node-tokencache te vernietigen zodra u de nog geldige vernieuwingstokens gebruikt om een nieuwe set tokens op te halen met de methode van acquireTokenByRefreshToken MSAL Node, zoals hierboven wordt weergegeven.

Fouten en uitzonderingen verwerken

Wanneer u MSAL Node gebruikt, is het meest voorkomende type fout dat u kunt tegenkomen de interaction_required fout. Deze fout wordt vaak opgelost door een interactieve tokenovernameprompt te starten. Als er bijvoorbeeld acquireTokenSilentgeen vernieuwingstokens in de cache zijn, kan MSAL Node geen toegangstoken op de achtergrond verkrijgen. Op dezelfde manier heeft de web-API die u probeert te openen mogelijk een beleid voor voorwaardelijke toegang, waardoor de gebruiker meervoudige verificatie (MFA) moet uitvoeren. In dergelijke gevallen interaction_required wordt de gebruiker door het activeren van acquireTokenByCode een fout gevraagd om MFA, zodat deze volledig kan worden gefiltreerd.

Een andere veelvoorkomende fout die u kunt tegenkomen, is consent_required, wat optreedt wanneer machtigingen die vereist zijn voor het verkrijgen van een toegangstoken voor een beveiligde resource niet worden toegestaan door de gebruiker. Zoals in interaction_required, is de oplossing voor consent_required fout vaak het initiëren van een interactieve prompt voor het verkrijgen van tokens met de acquireTokenByCode methode.

De app uitvoeren

Zodra uw wijzigingen zijn voltooid, voert u de app uit en test u uw verificatiescenario:

npm start

Voorbeeld: tokens verkrijgen met ADAL-knooppunt versus MSAL-knooppunt

In het onderstaande fragment ziet u een vertrouwelijke clientweb-app in het Express.js framework. Er wordt een aanmelding uitgevoerd wanneer een gebruiker de verificatieroute /authbereikt, een toegangstoken voor Microsoft Graph verkrijgt via de /redirect route en vervolgens de inhoud van het genoemde token weergeeft.

ADAL-knooppunt gebruiken MSAL-knooppunt gebruiken
// Import dependencies
var express = require('express');
var crypto = require('crypto');
var adal = require('adal-node');

// Authentication parameters
var clientId = 'Enter_the_Application_Id_Here';
var clientSecret = 'Enter_the_Client_Secret_Here';
var tenant = 'Enter_the_Tenant_Info_Here';
var authorityUrl = 'https://login.microsoftonline.com/' + tenant;
var redirectUri = 'http://localhost:3000/redirect';
var resource = 'https://graph.microsoft.com';

// Configure logging
adal.Logging.setLoggingOptions({
    log: function (level, message, error) {
        console.log(message);
    },
    level: adal.Logging.LOGGING_LEVEL.VERBOSE,
    loggingWithPII: false
});

// Auth code request URL template
var templateAuthzUrl = 'https://login.microsoftonline.com/'
    + tenant + '/oauth2/authorize?response_type=code&client_id='
    + clientId + '&redirect_uri=' + redirectUri
    + '&state=<state>&resource=' + resource;

// Initialize express
var app = express();

// State variable persists throughout the app lifetime
app.locals.state = "";

app.get('/auth', function(req, res) {

    // Create a random string to use against XSRF
    crypto.randomBytes(48, function(ex, buf) {
        app.locals.state = buf.toString('base64')
            .replace(/\//g, '_')
            .replace(/\+/g, '-');

        // Construct auth code request URL
        var authorizationUrl = templateAuthzUrl
            .replace('<state>', app.locals.state);

        res.redirect(authorizationUrl);
    });
});

app.get('/redirect', function(req, res) {
    // Compare state parameter against XSRF
    if (app.locals.state !== req.query.state) {
        res.send('error: state does not match');
    }

    // Initialize an AuthenticationContext object
    var authenticationContext =
        new adal.AuthenticationContext(authorityUrl);

    // Exchange auth code for tokens
    authenticationContext.acquireTokenWithAuthorizationCode(
        req.query.code,
        redirectUri,
        resource,
        clientId,
        clientSecret,
        function(err, response) {
            res.send(response);
        }
    );
});

app.listen(3000, function() {
    console.log(`listening on port 3000!`);
});
// Import dependencies
const express = require("express");
const msal = require('@azure/msal-node');

// Authentication parameters
const config = {
    auth: {
        clientId: "Enter_the_Application_Id_Here",
        authority: "https://login.microsoftonline.com/Enter_the_Tenant_Info_Here",
        clientSecret: "Enter_the_Client_Secret_Here"
    },
    system: {
        loggerOptions: {
            loggerCallback(loglevel, message, containsPii) {
                console.log(message);
            },
            piiLoggingEnabled: false,
            logLevel: msal.LogLevel.Verbose,
        }
    }
};

const REDIRECT_URI = "http://localhost:3000/redirect";

// Initialize MSAL Node object using authentication parameters
const cca = new msal.ConfidentialClientApplication(config);

// Initialize express
const app = express();

app.get('/auth', (req, res) => {

    // Construct a request object for auth code
    const authCodeUrlParameters = {
        scopes: ["user.read"],
        redirectUri: REDIRECT_URI,
    };

    // Request auth code, then redirect
    cca.getAuthCodeUrl(authCodeUrlParameters)
        .then((response) => {
            res.redirect(response);
        }).catch((error) => res.send(error));
});

app.get('/redirect', (req, res) => {

    // Use the auth code in redirect request to construct
    // a token request object
    const tokenRequest = {
        code: req.query.code,
        scopes: ["user.read"],
        redirectUri: REDIRECT_URI,
    };

    // Exchange the auth code for tokens
    cca.acquireTokenByCode(tokenRequest)
        .then((response) => {
            res.send(response);
        }).catch((error) => res.status(500).send(error));
});

app.listen(3000, () =>
    console.log(`listening on port 3000!`));

Volgende stappen