Een JavaScript-app migreren van ADAL.js naar MSAL.js

Microsoft Authentication Library for JavaScript (MSAL.js, ook wel bekend als msal-browser) 2.x is de verificatiebibliotheek die we aanbevelen met JavaScript-toepassingen op het Microsoft Identity Platform. In dit artikel worden de wijzigingen beschreven die u moet aanbrengen om een app te migreren die gebruikmaakt van de ADAL.js voor het gebruik van MSAL.js 2.x

Notitie

We raden MSAL.js 2.x ten zeerste aan via MSAL.js 1.x. De stroom voor het verlenen van verificatiecodes is veiliger en stelt toepassingen met één pagina in staat om een goede gebruikerservaring te behouden, ondanks de privacymaatregelen die browsers zoals Safari hebben geïmplementeerd om cookies van derden te blokkeren, onder andere voordelen.

Vereisten

  • U moet het / type antwoord-URL voor platform instellen op de app-registratieportal (als u andere platforms hebt toegevoegd in uw app-registratie, zoals web, moet u ervoor zorgen dat de omleidings-URI's niet overlappen. Zie: Beperkingen voor omleidings-URI's)
  • U moet polyfills opgeven voor ES6-functies waarvoor MSAL.js afhankelijk is (bijvoorbeeld beloften) om uw apps uit te voeren op Internet Explorer
  • Migreer uw Microsoft Entra-apps naar het v2-eindpunt als u dat nog niet hebt gedaan

MSAL installeren en importeren

Er zijn twee manieren om de MSAL.js 2.x-bibliotheek te installeren:

Via npm:

npm install @azure/msal-browser

Importeer deze, afhankelijk van uw modulesysteem, zoals hieronder wordt weergegeven:

import * as msal from "@azure/msal-browser"; // ESM

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

Via CDN:

Laad het script in de koptekstsectie van uw HTML-document:

<!DOCTYPE html>
<html>
  <head>
    <script type="text/javascript" src="https://alcdn.msauth.net/browser/2.14.2/js/msal-browser.min.js"></script>
  </head>
</html>

Zie voor alternatieve CDN-koppelingen en aanbevolen procedures bij het gebruik van CDN: CDN-gebruik

MSAL initialiseren

In ADAL.js instantieert u de klasse AuthenticationContext, waarmee vervolgens de methoden worden weergegeven die u kunt gebruiken om verificatie te bereiken (loginacquireTokenPopupenzovoort). Dit object fungeert als de weergave van de verbinding van uw toepassing met de autorisatieserver of id-provider. Bij het initialiseren is de enige verplichte parameter de clientId:

window.config = {
  clientId: "YOUR_CLIENT_ID"
};

var authContext = new AuthenticationContext(config);

In MSAL.js instantieert u in plaats daarvan de klasse PublicClientApplication . Net als ADAL.js verwacht de constructor een configuratieobject dat de clientId parameter minimaal bevat. Zie voor meer informatie: MSAL.js initialiseren

const msalConfig = {
  auth: {
      clientId: 'YOUR_CLIENT_ID'
  }
};

const msalInstance = new msal.PublicClientApplication(msalConfig);

In zowel ADAL.js als MSAL.js wordt de instantie-URI standaard https://login.microsoftonline.com/common ingesteld als u deze niet opgeeft.

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.js aanmelding wilt beperken tot een Microsoft Entra-account (hetzelfde gedrag als bij ADAL.js), gebruikt https://login.microsoftonline.com/organizations u in plaats daarvan.

MSAL configureren

Sommige configuratieopties in ADAL.js die worden gebruikt bij het initialiseren van AuthenticationContext, worden afgeschaft in MSAL.js, terwijl sommige nieuwe opties worden geïntroduceerd. Bekijk de volledige lijst met beschikbare opties. Belangrijk is dat veel van deze opties, met uitzondering clientIdvan , kunnen worden overschreven tijdens het verkrijgen van tokens, zodat u ze per aanvraag kunt instellen. U kunt bijvoorbeeld een andere instantie-URI of omleidings-URI gebruiken dan de URI die u tijdens de initialisatie hebt ingesteld bij het verkrijgen van tokens.

Daarnaast hoeft u niet langer de aanmeldingservaring op te geven (dat wil gezegd, of u nu pop-upvensters gebruikt of de pagina omleidt) via de configuratieopties. In plaats daarvan MSAL.js maakt en loginRedirect methoden beschikbaar loginPopup via het PublicClientApplication exemplaar.

Logboekregistratie inschakelen

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

window.config = {
  clientId: "YOUR_CLIENT_ID"
};

var authContext = new AuthenticationContext(config);

var Logging = {
  level: 3,
  log: function (message) {
      console.log(message);
  },
  piiLoggingEnabled: false
};


authContext.log(Logging)

In MSAL.js maakt logboekregistratie deel uit van de configuratieopties en wordt deze gemaakt tijdens de initialisatie van PublicClientApplication:

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 msalInstance = new msal.PublicClientApplication(msalConfig);

Overschakelen naar MSAL-API

Sommige van de openbare methoden in ADAL.js hebben equivalenten in MSAL.js:

ADAL MSAL Opmerkingen
acquireToken acquireTokenSilent De naam is gewijzigd en verwacht nu een accountobject
acquireTokenPopup acquireTokenPopup Asynchroon en retourneert nu een belofte
acquireTokenRedirect acquireTokenRedirect Asynchroon en retourneert nu een belofte
handleWindowCallback handleRedirectPromise Nodig als u omleidingservaring gebruikt
getCachedUser getAllAccounts De naam is gewijzigd en retourneert nu een matrix met accounts.

Andere zijn afgeschaft, terwijl MSAL.js nieuwe methoden biedt:

ADAL MSAL Opmerkingen
login N.v.t. Afgeschaft. Gebruik loginPopup of loginRedirect
logOut N.v.t. Afgeschaft. Gebruik logoutPopup of logoutRedirect
N.v.t. loginPopup
N.v.t. loginRedirect
N.v.t. logoutPopup
N.v.t. logoutRedirect
N.v.t. getAccountByHomeId Accounts filteren op thuis-id (oid + tenant-id)
N.v.t. getAccountLocalId Accounts filteren op lokale id (handig voor ADFS)
N.v.t. getAccountUsername Accounts filteren op gebruikersnaam (indien aanwezig)

Aangezien MSAL.js wordt geïmplementeerd in TypeScript in tegenstelling tot ADAL.js, worden verschillende typen en interfaces weergegeven die u in uw projecten kunt gebruiken. Zie de MSAL.js API-verwijzing voor meer informatie.

Bereiken gebruiken in plaats van bronnen

Een belangrijk verschil tussen de Azure Active Directory v1.0-eindpunten versus 2.0-eindpunten gaat over hoe de resources worden geopend. Wanneer u ADAL.js gebruikt met het v1.0-eindpunt , moet u eerst een machtiging registreren voor de portal voor app-registratie en vervolgens een toegangstoken aanvragen voor een resource (zoals Microsoft Graph), zoals hieronder wordt weergegeven:

authContext.acquireTokenRedirect("https://graph.microsoft.com", function (error, token) {
  // do something with the access token
});

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

msalInstance.acquireTokenRedirect({
  scopes: ["https://graph.microsoft.com/User.Read"]
});

Een voordeel van het bereikgerichte model is de mogelijkheid om dynamische bereiken te gebruiken. Wanneer u toepassingen bouwt met behulp van het v1.0-eindpunt, moet u de volledige set machtigingen (ook wel statische bereiken genoemd) registreren die de toepassing 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 ADAL.js worden callbacks gebruikt voor elke bewerking nadat de verificatie is geslaagd en een antwoord wordt verkregen:

authContext.acquireTokenPopup(resource, extraQueryParameter, claims, function (error, token) {
  // do something with the access token
});

In MSAL.js worden in plaats daarvan beloften gebruikt:

msalInstance.acquireTokenPopup({
      scopes: ["User.Read"] // shorthand for https://graph.microsoft.com/User.Read
  }).then((response) => {
      // do something with the auth response
  }).catch((error) => {
      // handle errors
  });

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

const getAccessToken = async() => {
  try {
      const authResponse = await msalInstance.acquireTokenPopup({
          scopes: ["User.Read"]
      });
  } catch (error) {
      // handle errors
  }
}

Tokens in cache opslaan en ophalen

Net als ADAL.js slaat MSAL.js tokens en andere verificatieartefacten in browseropslag op met behulp van de Web Storage-API. U wordt aangeraden om de optie (zie: configuratie) te gebruiken sessionStorage omdat het veiliger is om tokens op te slaan die zijn verkregen door uw gebruikers, maar localStorage u Single sign-on over tabbladen en gebruikerssessies.

Belangrijk is dat u niet rechtstreeks toegang tot de cache krijgt. In plaats daarvan moet u een geschikte MSAL.js-API gebruiken voor het ophalen van verificatieartefacten, zoals toegangstokens of gebruikersaccounts.

Tokens vernieuwen met vernieuwingstokens

ADAL.js maakt gebruik van de impliciete OAuth 2.0-stroom, die om veiligheidsredenen geen vernieuwingstokens retourneert (vernieuwingstokens hebben langere levensduur dan toegangstokens en zijn daarom gevaarlijker in handen van kwaadwillende actoren). Daarom voert ADAL.js tokenvernieuwing uit met behulp van een verborgen IFrame, zodat de gebruiker niet herhaaldelijk wordt gevraagd om te verifiëren.

Met de verificatiecodestroom met PKCE-ondersteuning krijgen apps die MSAL.js 2.x gebruiken vernieuwingstokens samen met id- en toegangstokens, die kunnen worden gebruikt om ze te vernieuwen. Het gebruik van vernieuwingstokens wordt weggeabstraheerd en de ontwikkelaars moeten geen logica om ze heen bouwen. In plaats daarvan beheert MSAL het vernieuwen van tokens met behulp van vernieuwingstokens zelf. De vorige tokencache met ADAL.js is niet overdraagbaar naar MSAL.js, omdat het tokencacheschema is gewijzigd en niet compatibel is met het schema dat wordt gebruikt in ADAL.js.

Fouten en uitzonderingen verwerken

Wanneer u MSAL.js gebruikt, is het meest voorkomende type fout dat u kunt tegenkomen de interaction_in_progress fout. Deze fout treedt op wanneer een interactieve API (loginPopup, loginRedirect, acquireTokenPopup, ) acquireTokenRedirectwordt aangeroepen terwijl er nog een interactieve API wordt uitgevoerd. De login* api's en acquireToken* API's zijn asynchroon , dus u moet ervoor zorgen dat de resulterende beloften zijn opgelost voordat u een andere aanroept.

Een andere veelvoorkomende fout is interaction_required. Deze fout wordt vaak opgelost door een interactieve tokenovernameprompt te starten. De web-API die u probeert te openen, heeft bijvoorbeeld mogelijk een beleid voor voorwaardelijke toegang, waardoor de gebruiker meervoudige verificatie (MFA) moet uitvoeren. In dat geval wordt de fout verwerkt interaction_required door de gebruiker te activeren acquireTokenPopup of acquireTokenRedirect te vragen om MFA, zodat ze deze volledig kunnen infiltreren.

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, de oplossing voor consent_required fout is vaak het initiëren van een interactieve token overnameprompt, met behulp van of acquireTokenPopupacquireTokenRedirect.

Zie voor meer informatie: Veelvoorkomende MSAL.js-fouten en hoe u deze kunt afhandelen

De gebeurtenissen-API gebruiken

MSAL.js (>=v2.4) introduceert een gebeurtenissen-API die u in uw apps kunt gebruiken. Deze gebeurtenissen zijn gerelateerd aan het verificatieproces en wat MSAL op elk moment doet, en kunnen worden gebruikt om de gebruikersinterface bij te werken, foutberichten weer te geven, te controleren of er interactie wordt uitgevoerd, enzovoort. Hieronder ziet u bijvoorbeeld een gebeurtenis-callback die wordt aangeroepen wanneer het aanmeldingsproces om welke reden dan ook mislukt:

const callbackId = msalInstance.addEventCallback((message) => {
  // Update UI or interact with EventMessage here
  if (message.eventType === EventType.LOGIN_FAILURE) {
      if (message.error instanceof AuthError) {
          // Do something with the error
      }
    }
});

Voor prestaties is het belangrijk om de registratie van callbacks voor gebeurtenissen ongedaan te maken wanneer ze niet meer nodig zijn. Zie voor meer informatie: MSAL.js Events API

Meerdere accounts verwerken

ADAL.js heeft het concept van een gebruiker die de momenteel geverifieerde entiteit vertegenwoordigt. MSAL.js vervangt gebruikers door accounts, gezien het feit dat een gebruiker meer dan één account aan hen kan koppelen. Dit betekent ook dat u nu voor meerdere accounts moet beheren en de juiste moet kiezen om mee te werken. Het onderstaande fragment illustreert dit proces:

let homeAccountId = null; // Initialize global accountId (can also be localAccountId or username) used for account lookup later, ideally stored in app state

// This callback is passed into `acquireTokenPopup` and `acquireTokenRedirect` to handle the interactive auth response
function handleResponse(resp) {
  if (resp !== null) {
      homeAccountId = resp.account.homeAccountId; // alternatively: resp.account.homeAccountId or resp.account.username
  } else {
      const currentAccounts = myMSALObj.getAllAccounts();
      if (currentAccounts.length < 1) { // No cached accounts
          return;
      } else if (currentAccounts.length > 1) { // Multiple account scenario
          // Add account selection logic here
      } else if (currentAccounts.length === 1) {
          homeAccountId = currentAccounts[0].homeAccountId; // Single account scenario
      }
  }
}

Zie voor meer informatie: Accounts in MSAL.js

De wrappers-bibliotheken gebruiken

Als u ontwikkelt voor Angular- en React-frameworks, kunt u respectievelijk MSAL Angular v2 en MSAL React gebruiken. Deze wrappers maken dezelfde openbare API beschikbaar als MSAL.js en bieden frameworkspecifieke methoden en onderdelen die de verificatie- en tokenverwervingsprocessen kunnen stroomlijnen.

De app uitvoeren

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

npm start

Voorbeeld: Een BEVEILIGD-WACHTWOORDVERIFICATIE beveiligen met ADAL.js versus MSAL.js

In de onderstaande fragmenten ziet u de minimale code die is vereist voor een toepassing met één pagina die gebruikers authenticeert met het Microsoft Identity Platform en een toegangstoken voor Microsoft Graph krijgt met behulp van eerste ADAL.js en vervolgens MSAL.js:

ADAL.js gebruiken MSAL.js gebruiken

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script type="text/javascript" src="https://alcdn.msauth.net/lib/1.0.18/js/adal.min.js"></script>
</head>

<body>
    <div>
        <p id="welcomeMessage" style="visibility: hidden;"></p>
        <button id="loginButton">Login</button>
        <button id="logoutButton" style="visibility: hidden;">Logout</button>
        <button id="tokenButton" style="visibility: hidden;">Get Token</button>
    </div>
    <script>
        // DOM elements to work with
        var welcomeMessage = document.getElementById("welcomeMessage");
        var loginButton = document.getElementById("loginButton");
        var logoutButton = document.getElementById("logoutButton");
        var tokenButton = document.getElementById("tokenButton");

        // if user is logged in, update the UI
        function updateUI(user) {
            if (!user) {
                return;
            }

            welcomeMessage.innerHTML = 'Hello ' + user.profile.upn + '!';
            welcomeMessage.style.visibility = "visible";
            logoutButton.style.visibility = "visible";
            tokenButton.style.visibility = "visible";
            loginButton.style.visibility = "hidden";
        };

        // attach logger configuration to window
        window.Logging = {
            piiLoggingEnabled: false,
            level: 3,
            log: function (message) {
                console.log(message);
            }
        };

        // ADAL configuration
        var adalConfig = {
            instance: 'https://login.microsoftonline.com/',
            clientId: "ENTER_CLIENT_ID_HERE",
            tenant: "ENTER_TENANT_ID_HERE",
            redirectUri: "ENTER_REDIRECT_URI_HERE",
            cacheLocation: "sessionStorage",
            popUp: true,
            callback: function (errorDesc, token, error, tokenType) {
                if (error) {
                    console.log(error, errorDesc);
                } else {
                    updateUI(authContext.getCachedUser());
                }
            }
        };

        // instantiate ADAL client object
        var authContext = new AuthenticationContext(adalConfig);

        // handle redirect response or check for cached user
        if (authContext.isCallback(window.location.hash)) {
            authContext.handleWindowCallback();
        } else {
            updateUI(authContext.getCachedUser());
        }

        // attach event handlers to button clicks
        loginButton.addEventListener('click', function () {
            authContext.login();
        });

        logoutButton.addEventListener('click', function () {
            authContext.logOut();
        });

        tokenButton.addEventListener('click', () => {
            authContext.acquireToken(
                "https://graph.microsoft.com",
                function (errorDesc, token, error) {
                    if (error) {
                        console.log(error, errorDesc);

                        authContext.acquireTokenPopup(
                            "https://graph.microsoft.com",
                            null, // extraQueryParameters
                            null, // claims
                            function (errorDesc, token, error) {
                                if (error) {
                                    console.log(error, errorDesc);
                                } else {
                                    console.log(token);
                                }
                            }
                        );
                    } else {
                        console.log(token);
                    }
                }
            );
        });
    </script>
</body>

</html>


<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script type="text/javascript" src="https://alcdn.msauth.net/browser/2.34.0/js/msal-browser.min.js"></script>
</head>

<body>
    <div>
        <p id="welcomeMessage" style="visibility: hidden;"></p>
        <button id="loginButton">Login</button>
        <button id="logoutButton" style="visibility: hidden;">Logout</button>
        <button id="tokenButton" style="visibility: hidden;">Get Token</button>
    </div>
    <script>
        // DOM elements to work with
        const welcomeMessage = document.getElementById("welcomeMessage");
        const loginButton = document.getElementById("loginButton");
        const logoutButton = document.getElementById("logoutButton");
        const tokenButton = document.getElementById("tokenButton");

        // if user is logged in, update the UI
        const updateUI = (account) => {
            if (!account) {
                return;
            }

            welcomeMessage.innerHTML = `Hello ${account.username}!`;
            welcomeMessage.style.visibility = "visible";
            logoutButton.style.visibility = "visible";
            tokenButton.style.visibility = "visible";
            loginButton.style.visibility = "hidden";
        };

        // MSAL configuration
        const msalConfig = {
            auth: {
                clientId: "ENTER_CLIENT_ID_HERE",
                authority: "https://login.microsoftonline.com/ENTER_TENANT_ID_HERE",
                redirectUri: "ENTER_REDIRECT_URI_HERE",
            },
            cache: {
                cacheLocation: "sessionStorage"
            },
            system: {
                loggerOptions: {
                    loggerCallback(loglevel, message, containsPii) {
                        console.log(message);
                    },
                    piiLoggingEnabled: false,
                    logLevel: msal.LogLevel.Verbose,
                }
            }
        };

        // instantiate MSAL client object
        const pca = new msal.PublicClientApplication(msalConfig);

        // handle redirect response or check for cached user
        pca.handleRedirectPromise().then((response) => {
            if (response) {
                pca.setActiveAccount(response.account);
                updateUI(response.account);
            } else {
                const account = pca.getAllAccounts()[0];
                updateUI(account);
            }
        }).catch((error) => {
            console.log(error);
        });

        // attach event handlers to button clicks
        loginButton.addEventListener('click', () => {
            pca.loginPopup().then((response) => {
                pca.setActiveAccount(response.account);
                updateUI(response.account);
            })
        });

        logoutButton.addEventListener('click', () => {
            pca.logoutPopup().then((response) => {
                window.location.reload();
            });
        });

        tokenButton.addEventListener('click', () => {
            const account = pca.getActiveAccount();

            pca.acquireTokenSilent({
                account: account,
                scopes: ["User.Read"]
            }).then((response) => {
                console.log(response);
            }).catch((error) => {
                if (error instanceof msal.InteractionRequiredAuthError) {
                    pca.acquireTokenPopup({
                        scopes: ["User.Read"]
                    }).then((response) => {
                        console.log(response);
                    });
                }

                console.log(error);
            });
        });
    </script>
</body>

</html>

Volgende stappen