Share via


Abilitare l'autenticazione nell'applicazione a pagina singola usando Azure AD B2C

Questo articolo illustra come aggiungere l'autenticazione di Azure Active Directory B2C (Azure AD B2C) alla propria applicazione a pagina singola. Informazioni su come creare un'applicazione a pagina singola usando Microsoft Authentication Library per JavaScript (MSAL.js).Learn how create a SPA application by using the Microsoft Authentication Library for JavaScript (MSAL.js).

Usare questo articolo con Configurare l'autenticazione in un'applicazione SPA di esempio, sostituendo l'app SPA di esempio con la propria app SPA.

Panoramica

Questo articolo usa Node.js ed Express per creare un'app Web Node.js di base. Express è un framework di app Web minimo e flessibile Node.js che offre un set di funzionalità per le applicazioni Web e per dispositivi mobili.

La libreria di autenticazioneMSAL.js è una libreria fornita da Microsoft che semplifica l'aggiunta del supporto per l'autenticazione e l'autorizzazione alle app SPA.

Suggerimento

L'intero codice MSAL.js viene eseguito sul lato client. È possibile sostituire il codice lato server Node.js ed Express con altre soluzioni, ad esempio i linguaggi di scripting .NET Core, Java e Hypertext Preprocessor (PHP).

Prerequisiti

Per esaminare i prerequisiti e le istruzioni di integrazione, vedere Configurare l'autenticazione in un'applicazione a pagina singola di esempio.

Passaggio 1: Creare un progetto di app SPA

È possibile usare un progetto di app SPA esistente o crearne uno nuovo. Per creare un nuovo progetto, eseguire le operazioni seguenti:

  1. Aprire una shell dei comandi e creare una nuova directory, ad esempio myApp. Questa directory conterrà il codice dell'app, l'interfaccia utente e i file di configurazione.

  2. Immettere la directory creata.

  3. Usare il npm init comando per creare un package.json file per l'app. Questo comando richiede informazioni sull'app, ad esempio il nome e la versione dell'app e il nome del punto di ingresso iniziale, il file diindex.js . Eseguire il comando seguente e accettare i valori predefiniti:

npm init

Passaggio 2: Installare le dipendenze

Per installare il pacchetto Express, nella shell dei comandi eseguire il comando seguente:

npm install express

Per individuare i file statici dell'app, il codice lato server usa il pacchetto Path .

Per installare il pacchetto Path, nella shell dei comandi eseguire il comando seguente:

npm install path

Passaggio 3: Configurare il server Web

Nella cartella myApp creare un file denominato index.js, che contiene il codice seguente:

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

Passaggio 4: Creare l'interfaccia utente spa

Aggiungere il file dell'app SPA index.html . Questo file implementa un'interfaccia utente compilata con un framework Bootstrap e importa i file di script per le chiamate di configurazione, autenticazione e API Web.

Le risorse a cui fa riferimento il file index.html sono descritte in dettaglio nella tabella seguente:

Riferimento Definizione
libreria MSAL.js MSAL.js percorso della rete CDN della libreria JavaScript di autenticazione.
Foglio di stile Bootstrap Framework front-end gratuito per lo sviluppo Web più veloce e semplice. Il framework include modelli di progettazione basati su HTML e CSS.
policies.js Contiene i criteri personalizzati e i flussi utente di Azure AD B2C.
authConfig.js Contiene i parametri di configurazione dell'autenticazione.
authRedirect.js Contiene la logica di autenticazione.
apiConfig.js Contiene gli ambiti dell'API Web e il percorso dell'endpoint API.
api.js Definisce il metodo da usare per chiamare l'API e gestire la risposta.
ui.js Controlla gli elementi dell'interfaccia utente.

Per eseguire il rendering del file di indice SPA, nella cartella myApp creare un file denominato index.html, che contiene il frammento HTML seguente:

<!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>

Passaggio 5: Configurare la libreria di autenticazione

Configurare la modalità di integrazione della libreria MSAL.js con Azure AD B2C. La libreria MSAL.js usa un oggetto di configurazione comune per connettersi agli endpoint di autenticazione del tenant di Azure AD B2C.

Per configurare la libreria di autenticazione, eseguire le operazioni seguenti:

  1. Nella cartella myApp creare una nuova cartella denominata App.

  2. All'interno della cartella App creare un nuovo file denominato authConfig.js.

  3. Aggiungere il codice JavaScript seguente al file authConfig.js :

    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. Sostituire <Application-ID> con l'ID applicazione di registrazione dell'app. Per altre informazioni, vedere Configurare l'autenticazione in un'applicazione a pagina singola di esempio.

Suggerimento

Per altre opzioni di configurazione degli oggetti MSAL, vedere l'articolo Opzioni di autenticazione .

Passaggio 6: Specificare i flussi utente di Azure AD B2C

Creare il file policies.js , che fornisce informazioni sull'ambiente Azure AD B2C. La libreria MSAL.js usa queste informazioni per creare richieste di autenticazione ad Azure AD B2C.

Per specificare i flussi utente di Azure AD B2C, seguire questa procedura:

  1. All'interno della cartella App creare un nuovo file denominato policies.js.

  2. Aggiungere il codice seguente al file policies.js :

    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. Sostituire B2C_1_SUSI con il nome dei criteri di Azure AD B2C di accesso.

  4. Sostituire B2C_1_EditProfile con il nome dei criteri di Azure AD B2C del profilo di modifica.

  5. Sostituire tutte le istanze di con il nome del contosotenant di Azure AD B2C.

Passaggio 7: Usare MSAL per accedere all'utente

In questo passaggio implementare i metodi per inizializzare il flusso di accesso, l'acquisizione del token di accesso api e i metodi di disconnessione.

Per altre informazioni, vedere l'articolo Usare Microsoft Authentication Library (MSAL) per accedere all'utente .

Per accedere all'utente, eseguire le operazioni seguenti:

  1. All'interno della cartella App creare un nuovo file denominato authRedirect.js.

  2. Nel authRedirect.jscopiare e incollare il codice seguente:

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

Passaggio 8: Configurare il percorso e l'ambito dell'API Web

Per consentire all'app a pagina singola di chiamare un'API Web, specificare il percorso dell'endpoint dell'API Web e gli ambiti da usare per autorizzare l'accesso all'API Web.

Per configurare il percorso e gli ambiti dell'API Web, eseguire le operazioni seguenti:

  1. All'interno della cartella App creare un nuovo file denominato apiConfig.js.

  2. Nel apiConfig.jscopiare e incollare il codice seguente:

    // 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. Sostituire contoso con il nome del tenant. Il nome dell'ambito richiesto è disponibile come descritto nell'articolo Configurare gli ambiti .

  4. Sostituire il valore per webApi con il percorso dell'endpoint dell'API Web.

Passaggio 9: Chiamare l'API Web

Definire la richiesta HTTP all'endpoint API. La richiesta HTTP è configurata per passare il token di accesso acquisito con MSAL.js nell'intestazione Authorization HTTP nella richiesta.

Il codice seguente definisce la richiesta HTTP GET all'endpoint API, passando il token di accesso all'interno dell'intestazione Authorization HTTP. Il percorso DELL'API è definito dalla webApi chiave in apiConfig.js.

Per chiamare l'API Web usando il token acquisito, eseguire le operazioni seguenti:

  1. All'interno della cartella App creare un nuovo file denominato api.js.

  2. Aggiungere il codice seguente al file api.js :

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

Passaggio 10: Aggiungere il riferimento agli elementi dell'interfaccia utente

L'app SPA usa JavaScript per controllare gli elementi dell'interfaccia utente. Ad esempio, visualizza i pulsanti di accesso e disconnessione ed esegue il rendering delle attestazioni del token ID degli utenti nella schermata.

Per aggiungere il riferimento agli elementi dell'interfaccia utente, seguire questa procedura:

  1. All'interno della cartella App creare un file denominato ui.js.

  2. Aggiungere il codice seguente al file ui.js :

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

Passaggio 11: Eseguire l'applicazione SPA

Nella shell dei comandi eseguire i comandi seguenti:

npm install  
npm ./index.js
  1. Passare a https://localhost:6420.
  2. Selezionare Accedi.
  3. Completare il processo di iscrizione o accesso.

Dopo aver eseguito l'autenticazione, il token ID analizzato viene visualizzato sullo schermo. Selezionare questa opzione Call API per chiamare l'endpoint API.

Passaggi successivi