Jak migrovat aplikaci Node.js z ADAL na MSAL

Microsoft Authentication Library for Node (MSAL Node) je teď doporučená sada SDK pro povolení ověřování a autorizace pro vaše aplikace zaregistrované na Microsoft identity platform. Tento článek popisuje důležité kroky, které je potřeba provést, abyste mohli migrovat aplikace z knihovny Active Directory Authentication Library for Node (ADAL Node) do uzlu MSAL.

Požadavky

Aktualizace nastavení registrace aplikace

Při práci s uzlem ADAL jste pravděpodobně používali koncový bod Azure AD verze 1.0. Aplikace migrující z knihovny ADAL na MSAL by se měly přepnout na koncový bod Azure AD verze 2.0.

Instalace a import MSAL

  1. nainstalujte balíček uzlu MSAL přes npm:
  npm install @azure/msal-node
  1. Potom do kódu naimportujte uzel MSAL:
  const msal = require('@azure/msal-node');
  1. Nakonec odinstalujte balíček uzlu ADAL a odeberte všechny odkazy v kódu:
  npm uninstall adal-node

Inicializace MSAL

V uzlu ADAL inicializujete AuthenticationContext objekt, který pak zpřístupní metody, acquireTokenWithAuthorizationCode které můžete použít v různých tocích ověřování (například pro webové aplikace). Při inicializaci je jediným povinným parametrem identifikátor URI autority:

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

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

V uzlu MSAL máte místo toho dvě alternativy: Pokud vytváříte mobilní aplikaci nebo desktopovou aplikaci, vytvoříte instanci objektu PublicClientApplication . Konstruktor očekává objekt konfigurace , který obsahuje clientId parametr přinejmenším. MSAL nastaví identifikátor URI autority ve výchozím nastavení na https://login.microsoftonline.com/common hodnotu , pokud ho nezadáte.

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

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

Poznámka

Pokud používáte autoritu https://login.microsoftonline.com/common ve verzi 2.0, umožníte uživatelům přihlásit se pomocí libovolné Microsoft Entra organizace nebo osobního účtu Microsoft (MSA). Pokud chcete v uzlu MSAL omezit přihlášení k libovolnému účtu Microsoft Entra (stejné chování jako u uzlu ADAL), použijte https://login.microsoftonline.com/organizations místo toho.

Na druhou stranu, pokud vytváříte webovou aplikaci nebo aplikaci démona, vytvoříte instanci objektu ConfidentialClientApplication . S těmito aplikacemi musíte také zadat přihlašovací údaje klienta, například tajný klíč klienta nebo certifikát:

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

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

ConfidentialClientApplicationA PublicClientApplication na rozdíl od knihovny ADAL AuthenticationContextjsou vázány na ID klienta. To znamená, že pokud máte různá ID klientů, která chcete ve své aplikaci používat, musíte vytvořit instanci nové instance MSAL pro každou z nich. Další informace najdete v tématu Inicializace uzlu MSAL.

Konfigurace MSAL

Při vytváření aplikací na Microsoft identity platform bude vaše aplikace obsahovat mnoho parametrů souvisejících s ověřováním. V uzlu ADAL má objekt omezený počet konfiguračních parametrů, AuthenticationContext se kterými ho můžete vytvořit, zatímco zbývající parametry volně visí ve vašem kódu (například 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: Adresa URL, která identifikuje autoritu tokenu
  • validateAuthority: Funkce, která brání kódu v vyžádání tokenů od potenciálně škodlivé autority
  • cache: Nastaví mezipaměť tokenů používanou touto instancí AuthenticationContext. Pokud tento parametr není nastavený, použije se výchozí hodnota v mezipaměti paměti.

Uzel MSAL na druhé straně používá objekt konfigurace typu Configuration. Obsahuje následující vlastnosti:

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

Mezi rozdíly patří to, že MSAL nemá příznak pro zakázání ověření autority a autority se ve výchozím nastavení vždy ověřují. MSAL porovnává požadovanou autoritu se seznamem autorit známých Microsoftu nebo se seznamem autorit, které jste zadali v konfiguraci. Další informace najdete v tématu Možnosti konfigurace.

Přepnutí na rozhraní MSAL API

Většina veřejných metod v uzlu ADAL má ekvivalenty v uzlu MSAL:

ADAL MSAL Poznámky
acquireToken acquireTokenSilent Přejmenováno a nyní očekává objekt účtu
acquireTokenWithAuthorizationCode acquireTokenByCode
acquireTokenWithClientCredentials acquireTokenByClientCredential
acquireTokenWithRefreshToken acquireTokenByRefreshToken Užitečné pro migraci platných obnovovacích tokenů
acquireTokenWithDeviceCode acquireTokenByDeviceCode Nyní abstrahuje získávání uživatelského kódu (viz níže).
acquireTokenWithUsernamePassword acquireTokenByUsernamePassword

Některé metody v uzlu ADAL jsou však zastaralé, zatímco uzel MSAL nabízí nové metody:

ADAL MSAL Poznámky
acquireUserCode Sloučeno s acquireTokeByDeviceCode (viz výše)
acquireTokenOnBehalfOf Nová metoda, která abstrahuje tok OBO
acquireTokenWithClientCertificate Už není potřeba, protože certifikáty se teď přiřazují během inicializace (viz možnosti konfigurace).
getAuthCodeUrl Nová metoda, která abstrahuje autorizaci vytváření adres URL koncového bodu

Použití oborů místo prostředků

Důležitým rozdílem mezi koncovými body verze 1.0 a v2.0 je způsob přístupu k prostředkům. V uzlu ADAL byste nejprve zaregistrovali oprávnění na portálu pro registraci aplikací a pak byste požádali o přístupový token pro prostředek (například Microsoft Graph), jak je znázorněno níže:

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

Uzel MSAL podporuje pouze koncový bod v2.0 . Koncový bod verze 2.0 používá pro přístup k prostředkům model zaměřený na obor . Proto když požadujete přístupový token pro prostředek, musíte také zadat obor pro tento prostředek:

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

Jednou z výhod modelu zaměřeného na obor je schopnost používat dynamické obory. Při vytváření aplikací pomocí verze 1.0 jste museli zaregistrovat úplnou sadu oprávnění (označovaných jako statické obory), které aplikace vyžaduje, aby s nimi uživatel souhlasil při přihlášení. Ve verzi 2.0 můžete pomocí parametru scope požádat o oprávnění v okamžiku, kdy je chcete (tedy dynamické obory). To uživateli umožňuje poskytnout přírůstkový souhlas s obory. Pokud tedy na začátku chcete, aby se uživatel přihlásil k vaší aplikaci, a vy nepotřebujete žádný druh přístupu, můžete to udělat. Pokud později potřebujete možnost číst kalendář uživatele, můžete požádat o rozsah kalendáře v metodách acquireToken a získat souhlas uživatele. Další informace najdete v tématu Prostředky a obory.

Použití příslibů místo zpětných volání

V uzlu ADAL se po úspěšném ověření a získání odpovědi používají zpětná volání pro všechny operace:

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

V uzlu MSAL se místo toho používají přísliby:

    const cca = new msal.ConfidentialClientApplication(msalConfig);

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

Můžete také použít syntaxi async/await , která je součástí ES8:

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

Povolit protokolování

V uzlu ADAL nakonfigurujete protokolování samostatně na libovolném místě v kódu:

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

V uzlu MSAL je protokolování součástí možností konfigurace a vytváří se s inicializací instance uzlu MSAL:

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

Povolení ukládání tokenů do mezipaměti

V uzlu ADAL jste měli možnost importovat mezipaměť tokenů v paměti. Mezipaměť tokenů se používá jako parametr při inicializaci objektu AuthenticationContext :

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

Uzel MSAL ve výchozím nastavení používá mezipaměť tokenů v paměti. Nemusíte ho explicitně importovat. Mezipaměť tokenů v paměti je zpřístupněna ConfidentialClientApplication jako součást tříd a PublicClientApplication .

const msalTokenCache = publicClientApplication.getTokenCache();

Důležité je, že předchozí mezipaměť tokenů s uzlem ADAL nebude přenosná do uzlu MSAL, protože schémata mezipaměti nejsou kompatibilní. Můžete ale použít platné obnovovací tokeny, které vaše aplikace získala dříve s uzlem ADAL v uzlu MSAL. Další informace najdete v části věnované obnovovacím tokenům .

Mezipaměť můžete také zapsat na disk poskytnutím vlastního modulu plug-in mezipaměti. Modul plug-in mezipaměti musí implementovat rozhraní ICachePlugin. Podobně jako protokolování je ukládání do mezipaměti součástí možností konfigurace a vytváří se s inicializací instance uzlu MSAL:

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

Příklad modulu plug-in mezipaměti můžete implementovat následujícím způsobem:

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

Pokud vyvíjíte veřejné klientské aplikace , jako jsou desktopové aplikace, nabízí rozšíření Microsoft Authentication Extensions for Node zabezpečené mechanismy pro klientské aplikace pro provádění serializace a trvalosti mezipaměti tokenů napříč platformami. Podporované platformy jsou Windows, Mac a Linux.

Poznámka

Rozšíření Microsoft Authentication Extensions for Node se pro webové aplikace nedoporučují , protože mohou vést k problémům se škálováním a výkonem. Místo toho se doporučuje, aby webové aplikace uchovávaly mezipaměť v relaci.

Odebrání logiky kolem obnovovacích tokenů

V uzlu ADAL byly zpřístupněny obnovovací tokeny (RT), které vám umožnily vyvíjet řešení týkající se použití těchto tokenů tím, že je uložíte do mezipaměti a použijete metodu acquireTokenWithRefreshToken . Typické scénáře, ve kterých jsou zvlášť důležité RT:

  • Dlouhotrvající služby, které provádějí akce, včetně aktualizace řídicích panelů jménem uživatelů, kteří už nejsou připojení.
  • Scénáře WebFarm umožňující klientovi přenést rt do webové služby (ukládání do mezipaměti se provádí na straně klienta, šifrovaný soubor cookie, nikoli na straně serveru).

Uzel MSAL spolu s dalšími seznamy MSAL z bezpečnostních důvodů nezpřístupňuje obnovovací tokeny. Místo toho služba MSAL zpracovává aktualizace tokenů za vás. Proto už nemusíte vytvářet logiku. Můžete ale využít dříve získané (a stále platné) obnovovací tokeny z mezipaměti uzlu ADAL k získání nové sady tokenů s uzlem MSAL. K tomu uzel MSAL nabízí acquireTokenByRefreshToken, což je ekvivalent metody uzlu acquireTokenWithRefreshToken ADAL:

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

Další informace najdete v ukázce migrace uzlu ADAL do uzlu MSAL.

Poznámka

Doporučujeme zničit starší mezipaměť tokenů uzlu ADAL, jakmile použijete stále platné obnovovací tokeny k získání nové sady tokenů pomocí metody uzlu acquireTokenByRefreshToken MSAL, jak je znázorněno výše.

Zpracování chyb a výjimek

Při použití uzlu MSAL je nejběžnějším typem chyby, se kterou se můžete setkat, interaction_required chyba. Tato chyba se často řeší spuštěním interaktivní výzvy k získání tokenu. Pokud například používáte , acquireTokenSilentpokud nejsou k dispozici žádné obnovovací tokeny uložené v mezipaměti, nebude uzel MSAL moct získat přístupový token bezobslužně. Podobně webové rozhraní API, ke kterým se pokoušíte získat přístup, může mít zásady podmíněného přístupu , které vyžadují, aby uživatel provedl vícefaktorové ověřování (MFA). V takových případech zpracování interaction_required chyby aktivací acquireTokenByCode vyzve uživatele k vícefaktorovém ověřování a umožní mu ho úplné zadání.

Další běžnou chybou, se kterou se můžete setkat, je consent_required, ke které dochází, když uživatel neodsoudí souhlas oprávnění potřebných k získání přístupového tokenu pro chráněný prostředek. Stejně jako v interaction_requirednástroji je řešením consent_required chyby často spuštění interaktivní výzvy k získání tokenu acquireTokenByCode pomocí metody .

Spuštění aplikace

Po dokončení změn spusťte aplikaci a otestujte scénář ověřování:

npm start

Příklad: Získávání tokenů pomocí uzlu ADAL vs. uzlu MSAL

Následující fragment kódu ukazuje důvěrnou klientskou webovou aplikaci v architektuře Express.js. Provede přihlášení, když uživatel narazí na trasu /authověřování , získá přístupový token pro Microsoft Graph prostřednictvím /redirect trasy a pak zobrazí obsah uvedeného tokenu.

Použití uzlu ADAL Použití uzlu MSAL
// 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!`));

Další kroky