Compartir a través de


Habilitación de la autenticación en su propia aplicación de página única mediante Azure AD B2C

Importante

A partir del 1 de mayo de 2025, Azure AD B2C ya no estará disponible para ser adquirido por nuevos clientes. Obtenga más información en nuestras preguntas más frecuentes.

En este artículo se muestra cómo agregar la autenticación de Azure Active Directory B2C (Azure AD B2C) a su propia aplicación de página única (SPA). Obtenga información sobre cómo crear una aplicación SPA mediante la biblioteca de autenticación de Microsoft para JavaScript (MSAL.js).

Use este artículo con Configuración de la autenticación en una aplicación SPA de ejemplo, sustituyendo la aplicación spa de ejemplo por su propia aplicación spa.

Información general

En este artículo se usa Node.js y Express para crear una aplicación web de Node.js básica. Express es un marco de aplicaciones web Node.js mínimo y flexible que proporciona un conjunto de funciones para aplicaciones web y móviles.

La biblioteca de autenticación MSAL.js es una biblioteca proporcionada por Microsoft que simplifica la adición de compatibilidad con la autenticación y la autorización a las aplicaciones SPA.

Sugerencia

Todo el código MSAL.js se ejecuta en el lado del cliente. Puede sustituir el código del lado del servidor Node.js y Express por otras soluciones, como los lenguajes de scripting .NET Core, Java y Hypertext Preprocessor (PHP).

Prerrequisitos

Para revisar los requisitos previos e instrucciones de integración, consulte Configuración de la autenticación en una aplicación SPA de ejemplo.

Paso 1: Crear un proyecto de aplicación SPA

Puede usar un proyecto de aplicación spa existente o crear uno nuevo. Para crear un proyecto, haga lo siguiente:

  1. Abra un shell de comandos y cree un directorio (por ejemplo, myApp). Este directorio contendrá el código de la aplicación, la interfaz de usuario y los archivos de configuración.

  2. Acceda al directorio que creó.

  3. Use el npm init comando para crear un package.json archivo para la aplicación. Este comando te solicita información sobre tu app (por ejemplo, el nombre y la versión de tu app, y el nombre del punto de entrada inicial, el archivo index.js ). Ejecute el comando siguiente y acepte los valores predeterminados:

npm init

Paso 2: Instalación de las dependencias.

Para instalar el paquete Express, en el shell de comandos, ejecute el siguiente comando:

npm install express

Para buscar los archivos estáticos de la aplicación, el código del lado servidor usa el paquete Ruta de acceso .

Para instalar el paquete Path, ejecute el siguiente comando en el shell de comandos:

npm install path

Paso 3: Configurar el servidor web

En la carpeta myApp , cree un archivo denominado index.js, que contiene el siguiente código:

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

Paso 4: Creación de la interfaz de usuario de SPA

Agregue el archivo index.html de la aplicación SPA. Este archivo implementa una interfaz de usuario que se construye con un framework de Bootstrap e importa archivos de script para configuración, autenticación y llamadas a la API web.

Los recursos a los que hace referencia el archivo index.html se detallan en la tabla siguiente:

Referencia Definición
MSAL.js Biblioteca Ruta de acceso CDN de la biblioteca de JavaScript de autenticación de MSAL.js.
Hoja de estilos de bootstrap Un marco de front-end gratuito para un desarrollo web más rápido y sencillo. El marco incluye plantillas de diseño basadas en HTML y css.

Para representar el archivo de índice SPA, en la carpeta myApp , cree un archivo denominado index.html, que contiene el siguiente fragmento de código 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>

Paso 5: Configurar la biblioteca de autenticación

Configure cómo se integra la biblioteca de MSAL.js con Azure AD B2C. La biblioteca MSAL.js usa un objeto de configuración común para conectarse a los puntos de conexión de autenticación del inquilino de Azure AD B2C.

Para configurar la biblioteca de autenticación, haga lo siguiente:

  1. En la carpeta myApp , cree una carpeta denominada App.

  2. Dentro de la carpeta Aplicación , cree un nuevo archivo denominado authConfig.js.

  3. Agregue el siguiente código JavaScript al archivo 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. Reemplace <Application-ID> por el id. de la aplicación de registro de la aplicación. Para obtener más información, consulte Configuración de la autenticación en una aplicación SPA de ejemplo.

Sugerencia

Para obtener más opciones de configuración de objetos MSAL, consulte el artículo Opciones de autenticación .

Paso 6: Especificar los flujos de usuario de Azure AD B2C

Cree el archivo policies.js , que proporciona información sobre el entorno de Azure AD B2C. La biblioteca MSAL.js usa esta información para crear solicitudes de autenticación a Azure AD B2C.

Para especificar los flujos de usuario de Azure AD B2C, haga lo siguiente:

  1. Dentro de la carpeta Aplicación , cree un nuevo archivo denominado policies.js.

  2. Agregue el siguiente código al archivo 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. Reemplace B2C_1_SUSI por el nombre de directiva de inicio de sesión de Azure AD B2C.

  4. Reemplace B2C_1_EditProfile por el nombre de directiva de edición de perfiles de Azure AD B2C.

  5. Reemplace todas las instancias de contoso por el nombre del inquilino de Azure AD B2C.

Paso 7: Uso de MSAL para iniciar sesión en el usuario

En este paso, implemente los métodos para inicializar el flujo de inicio de sesión, la adquisición de tokens de acceso de API y los métodos de cierre de sesión.

Para obtener más información, consulte el artículo Uso de la biblioteca de autenticación de Microsoft (MSAL) para iniciar sesión en el usuario .

Para iniciar sesión en el usuario, haga lo siguiente:

  1. Dentro de la carpeta Aplicación , cree un nuevo archivo denominado authRedirect.js.

  2. En su authRedirect.js, copie y pegue el siguiente código:

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

Paso 8: Configuración de la ubicación y el ámbito de la API web

Para permitir que la aplicación spa llame a una API web, proporcione la ubicación del punto de conexión de la API web y los ámbitos que se usarán para autorizar el acceso a la API web.

Para configurar la ubicación y los ámbitos de la API web, haga lo siguiente:

  1. Dentro de la carpeta Aplicación , cree un nuevo archivo denominado apiConfig.js.

  2. En su apiConfig.js, copie y pegue el siguiente código:

    // 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. Remplace contoso por el nombre del inquilino. El nombre de ámbito necesario se puede encontrar como se describe en el artículo Configuración de ámbitos .

  4. Reemplace el valor de webApi por su ubicación de punto de conexión de API web.

Paso 9: Llamada a la API web

Defina la solicitud HTTP al punto de conexión de API. La solicitud HTTP está configurada para pasar el token de acceso que se adquirió con MSAL.js en el encabezado HTTP de la solicitud Authorization.

El siguiente código define la solicitud HTTP GET para el punto de conexión de la API, pasando el token de acceso en el encabezado HTTP Authorization. La ubicación de la API está definida por la clave webApi .

Para llamar a la API web mediante el token que adquirió, haga lo siguiente:

  1. Dentro de la carpeta Aplicación , cree un nuevo archivo denominado api.js.

  2. Agregue el siguiente código al archivo 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);
        });
    }
    

Paso 10: Agregar la referencia de elementos de la interfaz de usuario

La aplicación SPA usa JavaScript para controlar los elementos de la interfaz de usuario. Por ejemplo, visualiza los botones de inicio de sesión y cierre de sesión y muestra las reclamaciones del token de identificación de los usuarios en la pantalla.

Para agregar la referencia de elementos de la interfaz de usuario, haga lo siguiente:

  1. Dentro de la carpeta App , cree un archivo llamado ui.js.

  2. Agregue el siguiente código al archivo 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'));
    }
    

Paso 11: Ejecución de la aplicación SPA

En el shell de comandos, ejecute los siguientes comandos:

npm install  
npm ./index.js
  1. Vaya a https://localhost:6420.
  2. Seleccione Iniciar sesión.
  3. Complete el proceso de inicio de sesión o registro.

Después de autenticarse correctamente, el token de identificador analizado se muestra en la pantalla. Seleccione Call API para llamar a su punto de conexión de API.

Pasos siguientes