Inicio rápido: registrar y configurar una aplicación SPA para Dataverse usando msal.js

En este tema se describe el proceso para registrar y configurar la Aplicación de una sola página (SPA) más sencilla para tener acceso a los datos en Microsoft Dataverse mediante msal.js y el uso compartido de recursos de origen cruzado(CORS). Más información: Usar OAuth con uso compartido de recursos entre orígenes para conectar una aplicación de una sola página a Dataverse.

Requisitos previos

Objetivo de este inicio rápido

Al completar este inicio rápido podrá ejecutar una aplicación SPA sencilla que proporcione la capacidad para que un usuario se autentique y recupere datos de Dataverse.

Cuando se depura la aplicación inicialmente solo habrá un botón Iniciar sesión.

  • Haga clic en Iniciar sesión y se abrirá una ventana emergente para ingresar sus credenciales.

  • Tras escribir sus credenciales encontrará que el botón Iniciar sesión está oculto y un botón Cerrar sesión y Obtener cuentas están visibles. También verá un saludo con información de su cuenta de usuario.

  • Haga clic en el botón Obtener cuentas para recuperar 10 registros de cuenta de su organización de Dataverse. El resultado se muestra en la captura de pantalla siguiente:

    La página SimpleSPA.

  • Por último, puede hacer clic en el botón Cerrar sesión para cerrar la sesión.

Nota

Esta aplicación SPA no está diseñada para representar un modelo para desarrollar aplicaciones SPA robustas. Está simplificada para centrarse en el proceso de registrar y configurar la aplicación.

Conseguir el punto de conexión de API web de Dataverse

Use las instrucciones en Ver recursos para desarrolladores para identificar un extremo de la API web para un entorno al que pueda acceder. Debería tener aspecto similar a éste: https://yourorg.api.crm.dynamics.com/api/data/v9.2

Registrar su aplicación

  1. Desde el centro de administración Power Platform en la navegación izquierda expanda Centros de administración y seleccione Microsoft Entra ID.

    Microsoft Entra ID desde el centro de administración de Power Platform

    Esto abre el Centro de administración de Microsoft Entra

  2. Expanda Aplicaciones y seleccione Registros de aplicaciones.

    Registros de aplicaciones de Azure desde el centro de administración de Microsoft Entra

  3. Haga clic en Nuevo registro. Esto abrirá el formulario Registrar una aplicación.

    Registrar un formulario de aplicación

  4. En el formulario Registrar una aplicación, escriba un Nombre. A los efectos de esta guía de inicio rápido, utilice el nombre SPA sencillo.

  5. Para Tipos de cuenta admitidos, la selección predeterminada debe ser:
    Solo cuentas de este directorio organizativo (solo de <nombre de inquilino>: inquilino único). No cambie esto.

  6. Para URI de redirección (opcional), utilice estas opciones:

    • Seleccione una plataforma: solicitud de una sola página (SPA)
    • e.g. https://example.com/auth: http://localhost:5500/index.html
  7. Haga clic en Registrar.

  8. En el área Visión general, copie los siguientes valores porque los necesitará en el paso final de Crear un proyecto de aplicación web.

    • Identificador de aplicación (cliente)
    • Id. de de directorio (inquilino)
  9. Seleccione Permisos de API.

  10. Haga clic en Agregar permisos.

  11. En el menú desplegable Solicitar permisos de API, seleccione Dynamics CRM.

    • Si no ve Dynamics CRM, busque Dataverse. O bien, seleccione la pestaña API que usa mi organización y busque Dataverse.
  12. Seleccione el permiso delegado person_impersonation.

  13. Haga clic en Agregar permisos.

Cuando finalice los permisos configurados deberían tener este aspecto:

Permisos configurados para la aplicación Simple SPA

Instalar la extensión Live Server de Visual Studio Code

Live Server es una extensión de Visual Studio Code que le permite lanzar fácilmente un servidor de desarrollo local para páginas web.

  1. Utilice estas instrucciones para buscar e instalar la extensión Live Server para VS Code en el mercado de VS Code:

  2. Una vez que haya instalado la extensión Live Server, realice estos cambios en la configuración predeterminada.

  3. Haga clic en el icono de engranaje en VS Code y seleccione Ajustes o utilice el atajo de teclado Ctrl+,.

  4. En la ventana de búsqueda, escriba liveServer.settings.host y cambie el valor predeterminado de 127.0.0.1 a localhost.

Crear un proyecto de aplicación web

  1. Cree una carpeta en su equipo. El nombre no es importante, pero a los efectos de estas instrucciones, asígnele el nombre simplespa.

  2. Abra VS Code y seleccione Archivo > Abrir carpeta en el menú. Seleccione la carpeta simplespa.

  3. Cree un nuevo archivo HTML en la carpeta llamada index.html. (No index.htm)

  4. Copie el contenido a continuación en el archivo index.html:

    <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>
          const baseUrl = "https://org.api.crm.dynamics.com";      //<= Change this
          const clientId = "11111111-1111-1111-1111-111111111111"; //<= Change this
          const tenantId = "22222222-2222-2222-2222-222222222222"; //<= Change this
          const redirectUrl = "http://localhost:5500/index.html";
          const webAPIEndpoint = baseUrl +"/api/data/v9.2";
    
    
          // Configuration object to be passed to MSAL instance on creation. 
    
          const msalConfig = {
             auth: {       
                clientId: clientId,
                // Full directory URL, in the form of https://login.microsoftonline.com/<tenant-id>
                authority: "https://login.microsoftonline.com/"+tenantId,       
                redirectUri: redirectUrl,
             },
             cache: {
                cacheLocation: "sessionStorage" // This configures where your cache will be stored
             },
             system: {   
                loggerOptions: {   
                   loggerCallback: (level, message, containsPii) => {   
                         if (containsPii) {      
                            return;      
                         }      
                         switch (level) {      
                            case msal.LogLevel.Error:      
                               console.error(message);      
                               return;      
                            case msal.LogLevel.Info:      
                               console.info(message);      
                               return;      
                            case msal.LogLevel.Verbose:      
                               console.debug(message);      
                               return;      
                            case msal.LogLevel.Warning:      
                               console.warn(message);      
                               return;      
                         }   
                   }   
                }   
             }
          };
    
       </script>
          <!-- Latest version of msal-browser.js from CDN as of 2022/09 -->
       <script
             type="text/javascript" 
             src="https://alcdn.msauth.net/browser/2.28.1/js/msal-browser.min.js">
       </script>
       <style>
          body {  
             font-family: 'Segoe UI';  
          }  
    
          table {  
             border-collapse: collapse;  
          }  
    
          td, th {  
             border: 1px solid black;  
          }
    
          #message {  
             color: green;  
          }
    </style>
    </head>
    <body>
    <div>
       <button id="loginButton" onclick="signIn()">Login</button>
       <button id="logoutButton" onclick="signOut()" style="display:none;">Logout</button>
       <button id="getAccountsButton" onclick="getAccounts(writeTable)" style="display:none;">Get Accounts</button>  
       <div id="message"></div>
       <table id="accountsTable" style="display:none;">  
        <thead><tr><th>Name</th><th>City</th></tr></thead>  
        <tbody id="accountsTableBody"></tbody>  
       </table>
    </div>
    <script>
       const loginButton = document.getElementById("loginButton");
       const logoutButton = document.getElementById("logoutButton");
       const getAccountsButton = document.getElementById("getAccountsButton");
       const accountsTable = document.getElementById("accountsTable");
       const accountsTableBody = document.getElementById("accountsTableBody");
       const message = document.getElementById("message");
       // Create the main myMSALObj instance
       const myMSALObj = new msal.PublicClientApplication(msalConfig);
    
       let username = "";
    
       // Sets the username. Called at the end of this script.
       function selectAccount() {
    
          const currentAccounts = myMSALObj.getAllAccounts();
          if (currentAccounts.length === 0) {
             return;
          } else if (currentAccounts.length > 1) {
             // Add choose account code here
             console.warn("Multiple accounts detected.");
          } else if (currentAccounts.length === 1) {
             username = currentAccounts[0].username;
             showWelcomeMessage(username);
          }
       }
    
       // Called by the loginButton
       function signIn() {
          myMSALObj.loginPopup({
             scopes: ["User.Read",baseUrl+"/user_impersonation"] //<= Includes Dataverse scope
             })
             .then(response =>{
                if (response !== null) {
                username = response.account.username;
                showWelcomeMessage(username);
                   } else {
                      selectAccount();
                   }
             })
             .catch(error => {
                   console.error(error);
             });
       }
    
       // Shows greeting and enables logoutButton and getAccountsButton
       // Called from signIn or selectAccount functions
       function showWelcomeMessage(username) {
        message.innerHTML = `Welcome ${username}`;
        loginButton.style.display = "none";
        logoutButton.style.display = "block";
        getAccountsButton.style.display = "block";
       }
    
       // Called by the logoutButton
       function signOut() {
    
          const logoutRequest = {
             account: myMSALObj.getAccountByUsername(username),
             postLogoutRedirectUri: msalConfig.auth.redirectUri,
             mainWindowRedirectUri: msalConfig.auth.redirectUri
          };
    
          myMSALObj.logoutPopup(logoutRequest);
       }
    
       // Provides the access token for a request, opening pop-up if necessary.
       // Used by GetAccounts function
       function getTokenPopup(request) {
    
          request.account = myMSALObj.getAccountByUsername(username);
    
          return myMSALObj.acquireTokenSilent(request)
             .catch(error => {
                   console.warn("Silent token acquisition fails. Acquiring token using popup");
                   if (error instanceof msal.InteractionRequiredAuthError) {
                      // fallback to interaction when silent call fails
                      return myMSALObj.acquireTokenPopup(request)
                         .then(tokenResponse => {
                               console.log(tokenResponse);
                               return tokenResponse;
                         }).catch(error => {
                               console.error(error);
                         });
                   } else {
                      console.warn(error);   
                   }
          });
       }
    
       // Retrieves top 10 account records from Dataverse
       function getAccounts(callback) {
          // Gets the access token
          getTokenPopup({
                scopes: [baseUrl+"/.default"]
             })
             .then(response => {
                getDataverse("accounts?$select=name,address1_city&$top=10", response.accessToken, callback);
             }).catch(error => {
                console.error(error);
             });
       }
    
       /** 
        * Helper function to get data from Dataverse
       * using the authorization bearer token scheme
       * callback is the writeTable function below
       */
       function getDataverse(url, token, callback) {
           const headers = new Headers();
           const bearer = `Bearer ${token}`;
           headers.append("Authorization", bearer);
           // Other Dataverse headers
           headers.append("Accept", "application/json"); 
           headers.append("OData-MaxVersion", "4.0");  
           headers.append("OData-Version", "4.0");  
    
           const options = {
              method: "GET",
              headers: headers
           };
    
         console.log('GET Request made to Dataverse at: ' + new Date().toString());
    
         fetch(webAPIEndpoint+"/"+url, options)
              .then(response => response.json())
              .then(response => callback(response))
              .catch(error => console.log(error));
        }
    
        // Renders the table with data from GetAccounts
        function writeTable(data) {
    
           data.value.forEach(function (account) {
    
               var name = account.name;
               var city = account.address1_city;
    
               var nameCell = document.createElement("td");
               nameCell.textContent = name;
    
               var cityCell = document.createElement("td");
               cityCell.textContent = city;
    
               var row = document.createElement("tr");
    
               row.appendChild(nameCell);
               row.appendChild(cityCell);
    
               accountsTableBody.appendChild(row); 
    
           });
    
           accountsTable.style.display = "block";
           getAccountsButton.style.display = "none";
        }
    
        selectAccount();
      </script>
     </body>
    </html>
    

    Nota

    El código JavaScript de la página HTML se adaptó del código de ejemplo publicado aquí: https://github.com/Azure-Samples/ms-identity-javascript-v2 que se conecta a Microsoft Graph.

    La diferencia clave son los ámbitos utilizados al obtener el token de acceso.

    Utilice estos ámbitos para el botón de inicio de sesión:

      // Called by the loginButton
      function signIn() {
         myMSALObj.loginPopup({
            scopes: ["User.Read",baseUrl+"/user_impersonation"]  //<= Includes Dataverse scope
            })
    

    Estos ámbitos incluyen el ámbito User.Read de Microsoft Graph y también el ámbito user_impersonation de Dataverse. Al incluir ambos ámbitos al iniciar sesión, el cuadro de diálogo de consentimiento inicial incluirá todos los ámbitos necesarios utilizados en la aplicación.

    Luego, al especificar el ámbito utilizado para la llamada a Dataverse puede usar /.default o /user_impersonation.

          // Retrieves top 10 account records from Dataverse
          function getAccounts(callback) {
             // Gets the access token
             getTokenPopup({
                   scopes: [baseUrl+"/.default"]
                })
    

    El ámbito /user_impersonation solo funciona para permisos delegados, que es el caso aquí, por lo que podría usarse. /.default funciona tanto para permisos delegados como de aplicación.

    Si no incluye el ámbito baseUrl+"/user_impersonation" al iniciar sesión, el usuario tendrá que dar su consentimiento por segunda vez cuando haga clic en el botón Obtener cuentas por primera vez.

    Puede encontrar otros ejemplos y tutoriales de SPA aquí: Documentación de solicitud de una sola página (SPA).

  5. Dentro de la página index.html, localice las siguientes variables de configuración y configúrelas utilizando la información que recopiló en los pasos anteriores: Conseguir su punto de conexión de API web de Dataverse y Registrar su aplicación.

    const baseUrl = "https://org.api.crm.dynamics.com";      //<= Change this
    const clientId = "11111111-1111-1111-1111-111111111111"; //<= Change this
    const tenantId = "22222222-2222-2222-2222-222222222222"; //<= Change this
    

Depurar la aplicación

Debido a que instaló la extensión Live Server en Instalar la extensión Live Server de Visual Studio Code, en la barra de herramientas de VS Code debería encontrar este botón: .

  1. Haga clic en el botón Poner en marcha y se abrirá una nueva ventana del navegador para http://localhost:5500/index.html que representa la página index.html.

    La primera vez que ejecuta la aplicación y hace clic en el botón Acceso, obtendrá un cuadro de diálogo de consentimiento como este:

    Cuadro de diálogo Permisos solicitados

    Si es administrador, puede seleccionar la casilla Consentimiento en nombre de su organización que permitirá que otros también ejecuten la aplicación sin tener que usar el diálogo Permisos solicitados.

  2. Haga clic en Aceptar para continuar probando para verificar que la aplicación funciona como se describe en Objetivo de este inicio rápido.

Solución de problemas

La experiencia en este inicio rápido depende de que la configuración del puerto de Live Server sea el valor predeterminado:5500. Si ya tiene Live Server instalado y ha modificado la configuración del puerto, deberá cambiar la configuración predeterminada o la URL establecida en el registro de la aplicación.

Tenga en cuenta que liveServer.settings.port también se puede establecer para el espacio de trabajo y anulará el ajuste Usuario.

Si abre varias instancias de Live Server, la configuración del puerto puede aumentar a 5501 o superior. Esto romperá la devolución de llamada utilizada para la autenticación porque el puerto está 'codificado' en el registro de la aplicación como http://localhost:5500/index.html.

Consulte también

Documentación de aplicación de una sola página (SPA)
Usar OAuth con uso compartido de recursos entre orígenes para conectar una aplicación de una sola página a Dataverse
Crear aplicaciones cliente

Nota

¿Puede indicarnos sus preferencias de idioma de documentación? Realice una breve encuesta. (tenga en cuenta que esta encuesta está en inglés)

La encuesta durará unos siete minutos. No se recopilan datos personales (declaración de privacidad).