Migrera en Node.js-app från ADAL till MSAL

Microsoft Authentication Library for Node (MSAL Node) är nu den rekommenderade SDK:t för aktivering av autentisering och auktorisering för dina program som registrerats på Microsofts identitetsplattform. Den här artikeln beskriver de viktiga steg som du behöver gå igenom för att migrera dina appar från Active Directory Authentication Library for Node (ADAL Node) till MSAL Node.

Förutsättningar

  • Nodversion 10, 12, 14, 16 eller 18. Se anteckningen om versionsstöd

Uppdatera inställningar för appregistrering

När du arbetade med ADAL Node använde du förmodligen Azure AD v1.0-slutpunkten. Appar som migrerar från ADAL till MSAL bör växla till Azure AD v2.0-slutpunkten.

Installera och importera MSAL

  1. installera MSAL Node-paketet via npm:
  npm install @azure/msal-node
  1. Därefter importerar du MSAL Node i koden:
  const msal = require('@azure/msal-node');
  1. Avinstallera slutligen ADAL Node-paketet och ta bort eventuella referenser i koden:
  npm uninstall adal-node

Initiera MSAL

I ADAL Node initierar du ett AuthenticationContext objekt som sedan exponerar de metoder som du kan använda i olika autentiseringsflöden (till exempel acquireTokenWithAuthorizationCode för webbappar). När du initierar är den enda obligatoriska parametern utfärdarens URI:

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

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

I MSAL Node har du två alternativ i stället: Om du skapar en mobilapp eller en skrivbordsapp instansierar du ett PublicClientApplication objekt. Konstruktorn förväntar sig minst ett konfigurationsobjekt som innehåller parametern clientId . MSAL använder utfärdar-URI:n som https://login.microsoftonline.com/common standard om du inte anger den.

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

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

Kommentar

Om du använder utfärdaren https://login.microsoftonline.com/common i v2.0 tillåter du användare att logga in med någon Microsoft Entra-organisation eller ett personligt Microsoft-konto (MSA). Om du vill begränsa inloggningen till ett Microsoft Entra-konto i MSAL Node (samma beteende som med ADAL Node) använder https://login.microsoftonline.com/organizations du i stället.

Om du å andra sidan skapar en webbapp eller en daemonapp instansierar du ett ConfidentialClientApplication objekt. Med sådana appar måste du också ange en klientautentiseringsuppgift, till exempel en klienthemlighet eller ett certifikat:

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

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

Både PublicClientApplication och ConfidentialClientApplication, till skillnad från ADAL: s AuthenticationContext, är bundna till ett klient-ID. Det innebär att om du har olika klient-ID:n som du vill använda i ditt program måste du instansiera en ny MSAL-instans för var och en. Mer information finns i: Initiering av MSAL-nod

Konfigurera MSAL

När du skapar appar på Microsofts identitetsplattform innehåller appen många parametrar som är relaterade till autentisering. I ADAL Node AuthenticationContext har objektet ett begränsat antal konfigurationsparametrar som du kan instansiera det med, medan de återstående parametrarna hänger fritt i koden (till exempel 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 som identifierar en tokenutfärdare
  • validateAuthority: en funktion som hindrar din kod från att begära token från en potentiellt skadlig utfärdare
  • cache: anger den tokencache som används av den här AuthenticationContext-instansen. Om den här parametern inte har angetts används ett standardvärde i minnescachen

MSAL Node använder å andra sidan ett konfigurationsobjekt av typen Konfiguration. Det innehåller följande egenskaper: .

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);

Som en märkbar skillnad har MSAL ingen flagga för att inaktivera verifiering av utfärdare och myndigheterna verifieras alltid som standard. MSAL jämför din begärda utfärdare med en lista över myndigheter som är kända för Microsoft eller en lista över myndigheter som du har angett i konfigurationen. Mer information finns i Konfigurationsalternativ

Växla till MSAL API

De flesta offentliga metoder i ADAL Node har motsvarigheter i MSAL Node:

ADAL MSAL Kommentar
acquireToken acquireTokenSilent Har bytt namn och förväntar sig nu ett kontoobjekt
acquireTokenWithAuthorizationCode acquireTokenByCode
acquireTokenWithClientCredentials acquireTokenByClientCredential
acquireTokenWithRefreshToken acquireTokenByRefreshToken Användbart för migrering av giltiga uppdateringstoken
acquireTokenWithDeviceCode acquireTokenByDeviceCode Abstraherar nu anskaffning av användarkod (se nedan)
acquireTokenWithUsernamePassword acquireTokenByUsernamePassword

Vissa metoder i ADAL Node är dock inaktuella, medan MSAL Node erbjuder nya metoder:

ADAL MSAL Kommentar
acquireUserCode Ej tillämpligt Sammanfogad med acquireTokeByDeviceCode (se ovan)
Ej tillämpligt acquireTokenOnBehalfOf En ny metod som abstraherar OBO-flöde
acquireTokenWithClientCertificate Ej tillämpligt Behövs inte längre eftersom certifikat tilldelas under initieringen nu (se konfigurationsalternativ)
Ej tillämpligt getAuthCodeUrl En ny metod som abstraherar auktorisering av slutpunkts-URL-konstruktion

Använda omfång i stället för resurser

En viktig skillnad mellan v1.0 och v2.0-slutpunkter handlar om hur resurserna används. I ADAL Node registrerar du först en behörighet på appregistreringsportalen och begär sedan en åtkomsttoken för en resurs (till exempel Microsoft Graph) enligt nedan:

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 stöder endast v2.0-slutpunkten . V2.0-slutpunkten använder en omfångscentrerad modell för att få åtkomst till resurser. När du begär en åtkomsttoken för en resurs måste du därför också ange omfånget för resursen:

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);
});

En fördel med den omfångscentrerade modellen är möjligheten att använda dynamiska omfång. När du skapade program med v1.0 behövde du registrera den fullständiga uppsättning behörigheter (kallas statiska omfång) som krävs av programmet för att användaren ska kunna samtycka till vid tidpunkten för inloggningen. I v2.0 kan du använda omfångsparametern för att begära behörigheterna när du vill ha dem (därav dynamiska omfång). På så sätt kan användaren ge inkrementellt medgivande till omfång. Så om du i början bara vill att användaren ska logga in på ditt program och du inte behöver någon typ av åtkomst, kan du göra det. Om du senare behöver möjlighet att läsa användarens kalender kan du sedan begära kalenderomfånget i metoderna acquireToken och få användarens medgivande. Mer information finns i: Resurser och omfång

Använd löften i stället för återanrop

I ADAL Node används återanrop för alla åtgärder när autentiseringen har slutförts och ett svar erhålls:

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
    }
});

I MSAL Node används löften i stället:

    const cca = new msal.ConfidentialClientApplication(msalConfig);

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

Du kan också använda syntaxen async/await som medföljer ES8:

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

Aktivera loggning

I ADAL Node konfigurerar du loggning separat på valfri plats i koden:

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.
});

I MSAL Node är loggning en del av konfigurationsalternativen och skapas med initieringen av MSAL Node-instansen:

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);

Aktivera cachelagring av token

I ADAL Node hade du möjlighet att importera en minnesintern tokencache. Tokencachen används som en parameter när ett AuthenticationContext objekt initieras:

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 använder en minnesintern tokencache som standard. Du behöver inte uttryckligen importera den. minnesintern tokencache exponeras som en del av klasserna ConfidentialClientApplication och PublicClientApplication .

const msalTokenCache = publicClientApplication.getTokenCache();

Viktigt är att din tidigare tokencache med ADAL Node inte kan överföras till MSAL Node, eftersom cachescheman är inkompatibla. Du kan dock använda giltiga uppdateringstoken som appen hämtade tidigare med ADAL Node i MSAL Node. Mer information finns i avsnittet om uppdateringstoken .

Du kan också skriva cacheminnet till disken genom att tillhandahålla ett eget cache-plugin-program. Cache-plugin-programmet måste implementera gränssnittet ICachePlugin. Precis som loggning är cachelagring en del av konfigurationsalternativen och skapas med initieringen av MSAL Node-instansen:

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);

Ett exempel på ett cache-plugin-program kan implementeras enligt nedan:

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
};

Om du utvecklar offentliga klientprogram som skrivbordsappar erbjuder Microsoft Authentication Extensions for Node säkra mekanismer för klientprogram för att utföra cache-serialisering och beständighet mellan plattformar. Plattformar som stöds är Windows, Mac och Linux.

Kommentar

Microsoft-autentiseringstillägg för Node rekommenderas inte för webbprogram eftersom det kan leda till skalnings- och prestandaproblem. I stället rekommenderas webbappar att spara cacheminnet i sessionen.

Ta bort logik kring uppdateringstoken

I ADAL Node exponerades uppdateringstoken (RT) så att du kan utveckla lösningar kring användningen av dessa token genom att cachelagra dem och använda acquireTokenWithRefreshToken metoden. Typiska scenarier där RT:er är särskilt relevanta:

  • Långvariga tjänster som utför åtgärder, inklusive uppdatering av instrumentpaneler för de användare där användarna inte längre är anslutna.
  • WebFarm-scenarier för att göra det möjligt för klienten att ta RT till webbtjänsten (cachelagring görs på klientsidan, krypterad cookie och inte på serversidan).

MSAL Node, tillsammans med andra MSALs, exponerar inte uppdateringstoken av säkerhetsskäl. I stället hanterar MSAL uppdateringstoken åt dig. Därför behöver du inte längre skapa logik för detta. Du kan dock använda dina tidigare förvärvade (och fortfarande giltiga) uppdateringstoken från ADAL Node cache för att hämta en ny uppsättning token med MSAL Node. För att göra detta erbjuder acquireTokenByRefreshTokenMSAL Node , vilket motsvarar ADAL Node-metoden acquireTokenWithRefreshToken :

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);
});

Mer information finns i migreringsexemplet ADAL-nod till MSAL Node.

Kommentar

Vi rekommenderar att du förstör den äldre ADAL Node-tokencachen när du använder de fortfarande giltiga uppdateringstoken för att hämta en ny uppsättning token med hjälp av MSAL Node-metoden acquireTokenByRefreshToken enligt ovan.

Hantera fel och undantag

När du använder MSAL Node är den vanligaste typen av fel som du kan stöta på interaction_required felet. Det här felet löses ofta genom att en interaktiv tokeninsamlingsprompt initieras. När du till exempel använder acquireTokenSilent, om det inte finns några cachelagrade uppdateringstoken, kommer MSAL Node inte att kunna hämta en åtkomsttoken tyst. På samma sätt kan webb-API:et som du försöker komma åt ha en princip för villkorsstyrd åtkomst på plats, vilket kräver att användaren utför multifaktorautentisering (MFA). I sådana fall uppmanar hanteringsfel interaction_required genom att acquireTokenByCode utlösa användaren för MFA, vilket gör att de kan fyll i det fullt ut.

Ett annat vanligt fel som du kan stöta på är consent_required, som inträffar när behörigheter som krävs för att hämta en åtkomsttoken för en skyddad resurs inte godkänns av användaren. Precis som i interaction_requiredinitierar lösningen för consent_required felet ofta en interaktiv tokeninsamlingsprompt med hjälp av acquireTokenByCode metoden .

Kör appen

När ändringarna är klara kör du appen och testar ditt autentiseringsscenario:

npm start

Exempel: Hämta token med ADAL-nod jämfört med MSAL-nod

Kodfragmentet nedan visar en konfidentiell klientwebbapp i Express.js-ramverket. Den utför en inloggning när en användare träffar autentiseringsvägen /auth, hämtar en åtkomsttoken för Microsoft Graph via /redirect vägen och visar sedan innehållet i denna token.

Använda ADAL-nod Använda MSAL-nod
// 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!`));

Nästa steg