Povolení ověřování ve vlastní jednostránkové aplikaci pomocí Azure AD B2C

V tomto článku se dozvíte, jak přidat ověřování Azure Active Directory B2C (Azure AD B2C) do vlastní jednostránkové aplikace (SPA). Zjistěte, jak vytvořit aplikaci SPA pomocí knihovny Microsoft Authentication Library pro JavaScript (MSAL.js).

Tento článek použijte k tématu Konfigurace ověřování v ukázkové aplikaci SPA a nahrazení ukázkové aplikace SPA vlastní aplikací SPA.

Přehled

Tento článek používá Node.js a Express k vytvoření základní webové aplikace Node.js. Express je minimální a flexibilní architektura Node.js webových aplikací, která poskytuje sadu funkcí pro webové a mobilní aplikace.

Knihovna ověřováníMSAL.js je knihovna od Microsoftu, která zjednodušuje přidávání podpory ověřování a autorizace do aplikací SPA.

Tip

Celý kód MSAL.js běží na straně klienta. Kód na straně serveru Node.js a Express můžete nahradit jinými řešeními, jako jsou skriptovací jazyky .NET Core, Java a HYPERTEXT Preprocessor (PHP).

Požadavky

Pokud si chcete projít požadavky a pokyny k integraci, přečtěte si téma Konfigurace ověřování v ukázkové aplikaci SPA.

Krok 1: Vytvoření projektu aplikace SPA

Můžete použít existující projekt aplikace SPA nebo vytvořit nový. Pokud chcete vytvořit nový projekt, postupujte takto:

  1. Otevřete příkazové prostředí a vytvořte nový adresář (například myApp). Tento adresář bude obsahovat kód aplikace, uživatelské rozhraní a konfigurační soubory.

  2. Zadejte adresář, který jste vytvořili.

  3. Pomocí příkazu npm init vytvořte package.json soubor pro vaši aplikaci. Tento příkaz vás vyzve k zadání informací o vaší aplikaci (například název a verze aplikace a název počátečního vstupního bodu ,index.js souboru). Spusťte následující příkaz a přijměte výchozí hodnoty:

npm init

Krok 2: Instalace závislostí

Pokud chcete nainstalovat balíček Express, spusťte v příkazovém prostředí následující příkaz:

npm install express

K vyhledání statických souborů aplikace používá kód na straně serveru balíček Path .

Pokud chcete nainstalovat balíček Path, spusťte v příkazovém prostředí následující příkaz:

npm install path

Krok 3: Konfigurace webového serveru

Ve složce myApp vytvořte soubor s názvemindex.js, který obsahuje následující kód:

// Initialize express
const express = require('express');
const app = express();

// The port to listen to incoming HTTP requests
const port = 6420;

// Initialize path
const path = require('path');

// Set the front-end folder to serve public assets.
app.use(express.static('App'));

// Set up a route for the index.html
app.get('*', (req, res) => {
    res.sendFile(path.join(__dirname + '/index.html'));
});

// Start the server, and listen for HTTP requests
app.listen(port, () => {
  console.log(`Listening on http://localhost:${port}`);
});

Krok 4: Vytvoření uživatelského rozhraní SPA

Přidejte soubor aplikace index.html SPA. Tento soubor implementuje uživatelské rozhraní vytvořené pomocí architektury Bootstrap a importuje soubory skriptů pro konfiguraci, ověřování a volání webového rozhraní API.

Prostředky, na které odkazuje souborindex.html , jsou podrobně popsané v následující tabulce:

Reference Definice
MSAL.js knihovna MSAL.js ověřování javascriptové knihovny cesta CDN.
Šablona stylů Bootstrap Bezplatná front-endová architektura pro rychlejší a jednodušší vývoj pro web. Architektura obsahuje šablony návrhu založené na HTML a CSS.
policies.js Obsahuje vlastní zásady Azure AD B2C a toky uživatelů.
authConfig.js Obsahuje parametry konfigurace ověřování.
authRedirect.js Obsahuje logiku ověřování.
apiConfig.js Obsahuje obory webového rozhraní API a umístění koncového bodu rozhraní API.
api.js Definuje metodu, která se má použít k volání rozhraní API a zpracování jeho odpovědi.
ui.js Řídí prvky uživatelského rozhraní.

Pokud chcete vykreslit indexový soubor SPA, vytvořte ve složce myApp soubor s názvemindex.html, který obsahuje následující fragment kódu HTML:

<!DOCTYPE html>
<html>
    <head>
        <title>My Azure AD B2C test app</title>
    </head>
    <body>
        <h2>My Azure AD B2C test app</h2>
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous" />
        <button type="button" id="signIn" class="btn btn-secondary" onclick="signIn()">Sign-in</button>
        <button type="button" id="signOut" class="btn btn-success d-none" onclick="signOut()">Sign-out</button>
        <h5 id="welcome-div" class="card-header text-center d-none"></h5>
        <br />
        <!-- Content -->
        <div class="card">
            <div class="card-body text-center">
                <pre id="response" class="card-text"></pre>
                <button type="button" id="callApiButton" class="btn btn-primary d-none" onclick="passTokenToApi()">Call API</button>
            </div>
        </div>
        <script src="https://alcdn.msauth.net/browser/2.14.2/js/msal-browser.min.js" integrity="sha384-ggh+EF1aSqm+Y4yvv2n17KpurNcZTeYtUZUvhPziElsstmIEubyEB6AIVpKLuZgr" crossorigin="anonymous"></script>

        <!-- Importing app scripts (load order is important) -->
        <script type="text/javascript" src="./apiConfig.js"></script>
        <script type="text/javascript" src="./policies.js"></script>
        <script type="text/javascript" src="./authConfig.js"></script>
        <script type="text/javascript" src="./ui.js"></script>

        <!-- <script type="text/javascript" src="./authRedirect.js"></script>   -->
        <!-- uncomment the above line and comment the line below if you would like to use the redirect flow -->
        <script type="text/javascript" src="./authRedirect.js"></script>
        <script type="text/javascript" src="./api.js"></script>
    </body>
</html>

Krok 5: Konfigurace knihovny ověřování

Nakonfigurujte způsob integrace knihovny MSAL.js s Azure AD B2C. Knihovna MSAL.js používá pro připojení ke koncovým bodům ověřování tenanta Azure AD B2C společný konfigurační objekt.

Knihovnu ověřování nakonfigurujete takto:

  1. Ve složce myApp vytvořte novou složku s názvem Aplikace.

  2. Ve složce Aplikace vytvořte nový soubor s názvemauthConfig.js.

  3. Do souboruauthConfig.js přidejte následující kód JavaScriptu:

    const msalConfig = {
        auth: {
        clientId: "<Application-ID>", 
        authority: b2cPolicies.authorities.signUpSignIn.authority, 
        knownAuthorities: [b2cPolicies.authorityDomain], 
        redirectUri: "http://localhost:6420",
        },
        cache: {
        cacheLocation: "localStorage", .
        storeAuthStateInCookie: false, 
        }
    };
    
    const loginRequest = {
    scopes: ["openid", ...apiConfig.b2cScopes],
    };
    
    const tokenRequest = {
    scopes: [...apiConfig.b2cScopes],
    forceRefresh: false
    };
    
  4. Nahraďte <Application-ID> ID aplikace registrace vaší aplikace. Další informace najdete v tématu Konfigurace ověřování v ukázkové aplikaci SPA.

Tip

Další možnosti konfigurace objektů MSAL najdete v článku Možnosti ověřování .

Krok 6: Určení toků uživatelů Azure AD B2C

Vytvořte souborpolicies.js, který poskytuje informace o prostředí Azure AD B2C. Knihovna MSAL.js používá tyto informace k vytváření žádostí o ověření pro Azure AD B2C.

Pokud chcete určit toky uživatelů Azure AD B2C, postupujte takto:

  1. Ve složce Aplikace vytvořte nový soubor s názvempolicies.js.

  2. Do souborupolicies.js přidejte následující kód:

    const b2cPolicies = {
        names: {
            signUpSignIn: "B2C_1_SUSI",
            editProfile: "B2C_1_EditProfile"
        },
        authorities: {
            signUpSignIn: {
                authority: "https://contoso.b2clogin.com/contoso.onmicrosoft.com/Your-B2C-SignInOrSignUp-Policy-Id",
            },
            editProfile: {
                authority: "https://contoso.b2clogin.com/contoso.onmicrosoft.com/Your-B2C-EditProfile-Policy-Id"
            }
        },
        authorityDomain: "contoso.b2clogin.com"
    }
    
  3. Nahraďte B2C_1_SUSI názvem zásady přihlášení Azure AD B2C.

  4. Nahraďte B2C_1_EditProfile názvem zásad B2C Azure AD profilu pro úpravy.

  5. Nahraďte všechny instance názvem vašeho tenanta contosoAzure AD B2C.

Krok 7: Přihlášení uživatele pomocí knihovny MSAL

V tomto kroku implementujete metody pro inicializaci toku přihlášení, získání přístupového tokenu rozhraní API a metody odhlášení.

Další informace najdete v článku Přihlášení uživatele pomocí knihovny Microsoft Authentication Library (MSAL).

Pokud chcete uživatele přihlásit, postupujte takto:

  1. Ve složce Aplikace vytvořte nový soubor s názvemauthRedirect.js.

  2. Do authRedirect.jszkopírujte a vložte následující kód:

    // Create the main myMSALObj instance
    // configuration parameters are located at authConfig.js
    const myMSALObj = new msal.PublicClientApplication(msalConfig);
    
    let accountId = "";
    let idTokenObject = "";
    let accessToken = null;
    
    myMSALObj.handleRedirectPromise()
        .then(response => {
            if (response) {
                /**
                 * For the purpose of setting an active account for UI update, we want to consider only the auth response resulting
                 * from SUSI flow. "tfp" claim in the id token tells us the policy (NOTE: legacy policies may use "acr" instead of "tfp").
                 * To learn more about B2C tokens, visit https://learn.microsoft.com/azure/active-directory-b2c/tokens-overview
                 */
                if (response.idTokenClaims['tfp'].toUpperCase() === b2cPolicies.names.signUpSignIn.toUpperCase()) {
                    handleResponse(response);
                }
            }
        })
        .catch(error => {
            console.log(error);
        });
    
    
    function setAccount(account) {
        accountId = account.homeAccountId;
        idTokenObject = account.idTokenClaims;
        myClaims= JSON.stringify(idTokenObject);
        welcomeUser(myClaims);
    }
    
    function selectAccount() {
    
        /**
         * See here for more information on account retrieval: 
         * https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/docs/Accounts.md
         */
    
        const currentAccounts = myMSALObj.getAllAccounts();
    
        if (currentAccounts.length < 1) {
            return;
        } else if (currentAccounts.length > 1) {
    
            /**
             * Due to the way MSAL caches account objects, the auth response from initiating a user-flow
             * is cached as a new account, which results in more than one account in the cache. Here we make
             * sure we are selecting the account with homeAccountId that contains the sign-up/sign-in user-flow, 
             * as this is the default flow the user initially signed-in with.
             */
            const accounts = currentAccounts.filter(account =>
                account.homeAccountId.toUpperCase().includes(b2cPolicies.names.signUpSignIn.toUpperCase())
                &&
                account.idTokenClaims.iss.toUpperCase().includes(b2cPolicies.authorityDomain.toUpperCase())
                &&
                account.idTokenClaims.aud === msalConfig.auth.clientId 
                );
    
            if (accounts.length > 1) {
                // localAccountId identifies the entity for which the token asserts information.
                if (accounts.every(account => account.localAccountId === accounts[0].localAccountId)) {
                    // All accounts belong to the same user
                    setAccount(accounts[0]);
                } else {
                    // Multiple users detected. Logout all to be safe.
                    signOut();
                };
            } else if (accounts.length === 1) {
                setAccount(accounts[0]);
            }
    
        } else if (currentAccounts.length === 1) {
            setAccount(currentAccounts[0]);
        }
    }
    
    // in case of page refresh
    selectAccount();
    
    async function handleResponse(response) {
    
        if (response !== null) {
            setAccount(response.account);
        } else {
            selectAccount();
        }
    }
    
    function signIn() {
        myMSALObj.loginRedirect(loginRequest);
    }
    
    function signOut() {
        const logoutRequest = {
            postLogoutRedirectUri: msalConfig.auth.redirectUri,
        };
    
        myMSALObj.logoutRedirect(logoutRequest);
    }
    
    function getTokenRedirect(request) {
        request.account = myMSALObj.getAccountByHomeId(accountId); 
    
        return myMSALObj.acquireTokenSilent(request)
            .then((response) => {
                // In case the response from B2C server has an empty accessToken field
                // throw an error to initiate token acquisition
                if (!response.accessToken || response.accessToken === "") {
                    throw new msal.InteractionRequiredAuthError;
                } else {
                    console.log("access_token acquired at: " + new Date().toString());
                    accessToken = response.accessToken;
                    passTokenToApi();
                }
            }).catch(error => {
                console.log("Silent token acquisition fails. Acquiring token using popup. \n", error);
                if (error instanceof msal.InteractionRequiredAuthError) {
                    // fallback to interaction when silent call fails
                    return myMSALObj.acquireTokenRedirect(request);
                } else {
                    console.log(error);   
                }
        });
    }
    
    // Acquires and access token and then passes it to the API call
    function passTokenToApi() {
        if (!accessToken) {
            getTokenRedirect(tokenRequest);
        } else {
            try {
                callApi(apiConfig.webApi, accessToken);
            } catch(error) {
                console.log(error); 
            }
        }
    }
    
    function editProfile() {
    
    
        const editProfileRequest = b2cPolicies.authorities.editProfile;
        editProfileRequest.loginHint = myMSALObj.getAccountByHomeId(accountId).username;
    
        myMSALObj.loginRedirect(editProfileRequest);
    }
    

Krok 8: Konfigurace umístění a rozsahu webového rozhraní API

Pokud chcete aplikaci SPA povolit volání webového rozhraní API, zadejte umístění koncového bodu webového rozhraní API a obory , které se mají použít k autorizaci přístupu k webovému rozhraní API.

Umístění a rozsahy webového rozhraní API nakonfigurujete takto:

  1. Ve složce Aplikace vytvořte nový soubor s názvemapiConfig.js.

  2. Do apiConfig.jszkopírujte a vložte následující kód:

    // The current application coordinates were pre-registered in a B2C tenant.
    const apiConfig = {
        b2cScopes: ["https://contoso.onmicrosoft.com/tasks/tasks.read"],
        webApi: "https://mydomain.azurewebsites.net/tasks"
    };
    
  3. Nahraďte contoso názvem vašeho tenanta. Požadovaný název oboru najdete podle popisu v článku Konfigurace oborů .

  4. Nahraďte hodnotu webApi umístěním koncového bodu webového rozhraní API.

Krok 9: Volání webového rozhraní API

Definujte požadavek HTTP na koncový bod rozhraní API. Požadavek HTTP je nakonfigurovaný tak, aby předával přístupový token získaný pomocíMSAL.js do hlavičky Authorization HTTP v požadavku.

Následující kód definuje požadavek HTTP GET na koncový bod rozhraní API a předá přístupový token v hlavičce Authorization HTTP. Umístění rozhraní API je definováno webApi klíčem v apiConfig.js.

Pokud chcete volat webové rozhraní API pomocí tokenu, který jste získali, postupujte takto:

  1. Ve složce Aplikace vytvořte nový soubor s názvemapi.js.

  2. Do souboruapi.js přidejte následující kód:

    function callApi(endpoint, token) {
    
        const headers = new Headers();
        const bearer = `Bearer ${token}`;
    
        headers.append("Authorization", bearer);
    
        const options = {
            method: "GET",
            headers: headers
        };
    
        logMessage('Calling web API...');
    
        fetch(endpoint, options)
        .then(response => response.json())
        .then(response => {
    
            if (response) {
            logMessage('Web API responded: ' + response.name);
            }
    
            return response;
        }).catch(error => {
            console.error(error);
        });
    }
    

Krok 10: Přidání odkazu na prvky uživatelského rozhraní

Aplikace SPA používá k řízení prvků uživatelského rozhraní JavaScript. Například zobrazí tlačítka pro přihlášení a odhlášení a vykreslí na obrazovce deklarace identity tokenu ID uživatele.

Pokud chcete přidat odkaz na prvky uživatelského rozhraní, postupujte takto:

  1. Ve složce Aplikace vytvořte soubor s názvemui.js.

  2. Do souboruui.js přidejte následující kód:

    // Select DOM elements to work with
    const signInButton = document.getElementById('signIn');
    const signOutButton = document.getElementById('signOut')
    const titleDiv = document.getElementById('title-div');
    const welcomeDiv = document.getElementById('welcome-div');
    const tableDiv = document.getElementById('table-div');
    const tableBody = document.getElementById('table-body-div');
    const editProfileButton = document.getElementById('editProfileButton');
    const callApiButton = document.getElementById('callApiButton');
    const response = document.getElementById("response");
    const label = document.getElementById('label');
    
    function welcomeUser(claims) {
        welcomeDiv.innerHTML = `Token claims: </br></br> ${claims}!`
    
        signInButton.classList.add('d-none');
        signOutButton.classList.remove('d-none');
        welcomeDiv.classList.remove('d-none');
        callApiButton.classList.remove('d-none');
    }
    
    function logMessage(s) {
        response.appendChild(document.createTextNode('\n' + s + '\n'));
    }
    

Krok 11: Spuštění aplikace SPA

V příkazovém prostředí spusťte následující příkazy:

npm install  
npm ./index.js
  1. Přejděte na https://localhost:6420..
  2. Vyberte Přihlásit se.
  3. Dokončete proces registrace nebo přihlášení.

Po úspěšném ověření se na obrazovce zobrazí analyzovaný token ID. Vyberte Call API , pokud chcete volat koncový bod rozhraní API.

Další kroky