Adición de un botón de inicio de sesión de autenticación de Microsoft a una aplicación de página única

Agregue la autenticación de Microsoft en este tutorial de TypeScript para proporcionar un botón de inicio y cierre de sesión. Desarrolle la aplicación con el SDK del lado cliente de Azure, @azure/msal-browser, para proporcionar funcionalidad de autenticación.

Arquitectura y funcionalidad de la aplicación

La SPA creada en este tutorial es una aplicación de React (create-react-app) con las siguientes tareas:

  • Inicio de sesión con un inicio de sesión compatible con Microsoft, como Office 365 u Outlook.com
  • Cierre de sesión de la aplicación

Para proporcionar una aplicación de página única rápida y sencilla, el ejemplo usa create-react-app con TypeScript. Este marco de front-end proporciona varios accesos directos en el desarrollo de cliente típico con los SDK de Azure:

  • Agrupación, necesaria para los SDK de Azure utilizados en una aplicación cliente
  • Variables de entorno en el archivo .env

1. Configuración del entorno de desarrollo

Compruebe que el software siguiente está instalado en el equipo local.

2. Tomar nota del valor de la variable de entorno

Prepare un lugar para copiar el valor del id. de cliente del registro de la aplicación, por ejemplo, en un archivo de texto. Obtendrá el id. de cliente en el paso 5 de la siguiente sección. El valor se utilizará como una variable de entorno para la aplicación web.

3. Creación del registro de aplicación para la autenticación

  1. Inicie sesión en Azure Portal y vaya a la opción Registros de aplicaciones del directorio predeterminado.

  2. Seleccione + Nuevo registro.

  3. Escriba los datos de registro de la aplicación según la tabla siguiente:

    Campo Valor Descripción
    Nombre Simple Auth Tutorial Este es el nombre de la aplicación que el usuario verá en el formulario de permisos cuando inicie sesión en la aplicación.
    Tipos de cuenta admitidos Cuentas en cualquier directorio organizativo (cualquier directorio de Azure AD: multiinquilino) y cuentas Microsoft personales Esto incluye la mayoría de los tipos de cuenta.
    Tipo de identificador URI de redirección Aplicación de una sola página (SPA)
    Valor del identificador URI de redirección http://localhost:3000 Dirección URL a la que se va a volver después de que la autenticación se realice correctamente o produzca un error.

    Nuevo registro de aplicación de Azure.

  4. Seleccione Registrar. Espere a que se complete el proceso de registro de la aplicación.

  5. Copie el identificador de la aplicación (cliente) de la sección Información general del registro de la aplicación. Agregará este valor a la variable de entorno para la aplicación cliente más adelante.

4. Creación de una aplicación de página única de React para TypeScript

  1. En un shell de Bash, cree una aplicación create-react-app con TypeScript como lenguaje:

    npx create-react-app tutorial-demo-login-button --template typescript
    
  2. Cambie al nuevo directorio e instale el paquete de autenticación @azure/msal-browser:

    cd tutorial-demo-login-button && npm install @azure/msal-browser
    
  3. Cree un archivo .env en el nivel raíz y agregue la línea siguiente:

    REACT_APP_AZURE_ACTIVE_DIRECTORY_APP_CLIENT_ID=
    

    El archivo .env se lee como parte del marco create-react-app. En este archivo puede almacenar el identificador de cliente para el desarrollo local.

  4. Copie el identificador de la aplicación (cliente) en el valor.

5. Adición de los botones de inicio y cierre de sesión

  1. Para los archivos específicos de Azure, cree una subcarpeta llamada azure dentro de la carpeta ./src.

  2. Cree un nuevo archivo para la configuración de la autenticación en la carpeta azure llamado azure-authentication-config.ts y copie en él el código siguiente:

    import { Configuration, LogLevel } from "@azure/msal-browser";
    
    const AzureActiveDirectoryAppClientId: any =
      process.env.REACT_APP_AZURE_ACTIVE_DIRECTORY_APP_CLIENT_ID;
    
    export const MSAL_CONFIG: Configuration = {
      auth: {
        clientId: AzureActiveDirectoryAppClientId,
      },
      cache: {
        cacheLocation: "sessionStorage",
        storeAuthStateInCookie: false,
      },
      system: {
        loggerOptions: {
          loggerCallback: (level, message, containsPii) => {
            if (containsPii) {
              return;
            }
            switch (level) {
              case LogLevel.Error:
                console.error(message);
                return;
              case LogLevel.Info:
                console.info(message);
                return;
              case LogLevel.Verbose:
                console.debug(message);
                return;
              case LogLevel.Warning:
                console.warn(message);
                return;
            }
          },
        },
      },
    };
    

    Este archivo lee el identificador de la aplicación desde el archivo .env, establece la sesión como almacenamiento del explorador en lugar de cookies y proporciona un registro que se considera información personal.

  3. Cree un nuevo archivo para el middleware de autenticación de Azure en la carpeta azure llamado azure-authentication-context.ts y copie en él el código siguiente:

    import {
      PublicClientApplication,
      AuthenticationResult,
      AccountInfo,
      EndSessionRequest,
      RedirectRequest,
      PopupRequest,
    } from "@azure/msal-browser";
    
    import { MSAL_CONFIG } from "./azure-authentication-config";
    
    export class AzureAuthenticationContext {
      private myMSALObj: PublicClientApplication = new PublicClientApplication(
        MSAL_CONFIG
      );
      private account?: AccountInfo;
      private loginRedirectRequest?: RedirectRequest;
      private loginRequest?: PopupRequest;
    
      public isAuthenticationConfigured = false;
    
      constructor() {
        // @ts-ignore
        this.account = null;
        this.setRequestObjects();
        if (MSAL_CONFIG?.auth?.clientId) {
          this.isAuthenticationConfigured = true;
        }
      }
    
      private setRequestObjects(): void {
        this.loginRequest = {
          scopes: [],
          prompt: "select_account",
        };
    
        this.loginRedirectRequest = {
          ...this.loginRequest,
          redirectStartPage: window.location.href,
        };
      }
    
      login(signInType: string, setUser: any): void {
        if (signInType === "loginPopup") {
          this.myMSALObj
            .loginPopup(this.loginRequest)
            .then((resp: AuthenticationResult) => {
              this.handleResponse(resp, setUser);
            })
            .catch((err) => {
              console.error(err);
            });
        } else if (signInType === "loginRedirect") {
          this.myMSALObj.loginRedirect(this.loginRedirectRequest);
        }
      }
    
      logout(account: AccountInfo): void {
        const logOutRequest: EndSessionRequest = {
          account,
        };
    
        this.myMSALObj.logout(logOutRequest);
      }
      handleResponse(response: AuthenticationResult, incomingFunction: any) {
        if(response !==null && response.account !==null) {
          this.account = response.account;
        } else {
          this.account = this.getAccount();
        }
    
        if (this.account) {
          incomingFunction(this.account);
        }
      }
      private getAccount(): AccountInfo | undefined {
        console.log(`loadAuthModule`);
        const currentAccounts = this.myMSALObj.getAllAccounts();
        if (currentAccounts === null) {
          // @ts-ignore
          console.log("No accounts detected");
          return undefined;
        }
    
        if (currentAccounts.length > 1) {
          // TBD: Add choose account code here
          // @ts-ignore
          console.log(
            "Multiple accounts detected, need to add choose account code."
          );
          return currentAccounts[0];
        } else if (currentAccounts.length === 1) {
          return currentAccounts[0];
        }
      }
    }
    
    export default AzureAuthenticationContext;
    

    Este archivo proporciona la autenticación en Azure para un usuario con las funciones login y logout.

  4. Cree un nuevo archivo para el archivo del componente del botón de la interfaz de usuario en la carpeta azure llamado azure-authentication-component.tsx y copie en él el código siguiente:

    import React, { useState } from "react";
    import AzureAuthenticationContext from "./azure-authentication-context";
    import { AccountInfo } from "@azure/msal-browser";
    
    const ua = window.navigator.userAgent;
    const msie = ua.indexOf("MSIE ");
    const msie11 = ua.indexOf("Trident/");
    const isIE = msie > 0 || msie11 > 0;
    
    // Log In, Log Out button
    const AzureAuthenticationButton = ({ onAuthenticated }: any): JSX.Element => {
      // Azure client context
      const authenticationModule: AzureAuthenticationContext = new AzureAuthenticationContext();
    
      const [authenticated, setAuthenticated] = useState<Boolean>(false);
      const [user, setUser] = useState<AccountInfo>();
    
      const logIn = (method: string): any => {
        const typeName = "loginPopup";
        const logInType = isIE ? "loginRedirect" : typeName;
    
        // Azure Login
        authenticationModule.login(logInType, returnedAccountInfo);
      };
      const logOut = (): any => {
        if (user) {
          onAuthenticated(undefined);
          // Azure Logout
          authenticationModule.logout(user);
        }
      };
    
      const returnedAccountInfo = (user: AccountInfo) => {
        // set state
        setAuthenticated(user?.name ? true : false);
        onAuthenticated(user);
        setUser(user);
      };
    
      const showLogInButton = (): any => {
        return (
          <button id="authenticationButton" onClick={() => logIn("loginPopup")}>
            Log in
          </button>
        );
      };
    
      const showLogOutButton = (): any => {
        return (
          <div id="authenticationButtonDiv">
            <div id="authentication">
              <button id="authenticationButton" onClick={() => logOut()}>
                Log out
              </button>
            </div>
          </div>
        );
      };
    
      const showButton = (): any => {
        return authenticated ? showLogOutButton() : showLogInButton();
      };
    
      return (
        <div id="authentication">
          {authenticationModule.isAuthenticationConfigured ? (
            showButton()
          ) : (
            <div>Authentication Client ID is not configured.</div>
          )}
        </div>
      );
    };
    
    export default AzureAuthenticationButton;
    

    Este componente del botón inicia la sesión de un usuario y devuelve la cuenta de usuario al componente autor de la llamada o primario.

    El texto y la funcionalidad del botón se alternan en función de si el usuario tiene sesión iniciada actualmente, lo que se captura con la función onAuthenticated como una propiedad que se pasa al componente.

    Cuando un usuario inicia sesión, el botón llama al método authenticationModule.login de la biblioteca de autenticación de Azure con returnedAccountInfo como la función de devolución de llamada. A continuación, la cuenta de usuario devuelta se vuelve a pasar al componente primario con la función onAuthenticated.

  5. Abra el archivo ./src/App.tsx y reemplace todo el código por el código siguiente para incorporar el componente del botón de inicio de sesión y cierre de sesión:

    import React, { useState } from "react";
    import AzureAuthenticationButton from "./azure/azure-authentication-component";
    import { AccountInfo } from "@azure/msal-browser";
    
    function App() {
      // current authenticated user
      const [currentUser, setCurrentUser] = useState<AccountInfo>();
    
      // authentication callback
      const onAuthenticated = async (userAccountInfo: AccountInfo) => {
        setCurrentUser(userAccountInfo);
      };
    
      // Render JSON data in readable format
      const PrettyPrintJson = ({ data }: any) => {
        return (
          <div>
            <pre>{JSON.stringify(data, null, 2)}</pre>
          </div>
        );
      };
    
      // Quick link - user revokes app's permission
      const ShowPermissionRevokeLinks = () => {
        return (
          <div>
            <div><a href="https://myapps.microsoft.com" target="_blank" rel="noopener">Revoke AAD permission</a></div>
            <div><a href="https://account.live.com/consent/manage" target="_blank" rel="noopener">Revoke Consumer permission</a></div>
          </div>
        );
      };
    
      return (
        <div id="App">
          <h2>Microsoft Login Button application</h2>
          <AzureAuthenticationButton onAuthenticated={onAuthenticated} />
          {currentUser && (
            <div>
              <PrettyPrintJson data={currentUser} />
              <ShowPermissionRevokeLinks />
            </div>
          )}
        </div>
      );
    }
    
    export default App;
    

    Después de que un usuario inicia sesión y la autenticación redirige de vuelta a esta aplicación, se muestra el objeto currentUser.

6. Ejecución de la aplicación de página única de React con el botón de inicio de sesión

  1. Inicie la aplicación en el terminal de Visual Studio Code:

    npm run start
    

    Observe VSCode integrado para ver que la aplicación está completamente iniciada.

    Compiled successfully!
    
    You can now view js-e2e-client-azure-login-button in the browser.
    
      Local:            http://localhost:3000
      On Your Network:  http://x.x.x.x:3000
    
    Note that the development build is not optimized.
    To create a production build, use yarn build.
    
  2. Abra la aplicación web en un explorador, http://localhost:3000.

  3. Seleccione el botón de inicio de sesión en el explorador web.

    Selección del botón de inicio de sesión.

  4. Seleccione una cuenta de usuario. No tiene que ser la misma cuenta que usó para acceder a Azure Portal, pero debe ser una cuenta que proporcione autenticación de Microsoft.

    Seleccione una cuenta de usuario. No tiene que ser la misma cuenta que usó para acceder a Azure Portal, pero debe ser una cuenta que proporcione autenticación de Microsoft.

  5. Revise el elemento emergente que muestra 1) nombre de usuario, 2) nombre de aplicación, 3) permisos que va a aceptar y, a continuación, seleccione .

    Revise el elemento emergente que muestra 1) nombre de usuario, 2) nombre de aplicación, 3) permisos que va a aceptar y, a continuación, seleccione

  6. Revise la información de la cuenta de usuario.

    Revise la información de la cuenta de usuario.

  7. Seleccione el botón de cierre de sesión de la aplicación. La aplicación también proporciona vínculos cómodos a las aplicaciones de usuario de Microsoft para revocar los permisos.

7. Almacenamiento de la información de usuario específica de la aplicación

Opcionalmente, puede almacenar el identificador de usuario en su propia base de datos de aplicación para poner en correlación la identidad del proveedor de Microsoft y los datos del usuario necesarios en su propia aplicación. La notificación de token contiene dos identificadores de los que puede que desee realizar un seguimiento:

  • sub: el identificador que es específico del usuario, para el identificador de aplicación de Active Directory específico. Este es el identificador que debe almacenar en su propia base de datos de aplicación, si necesita poner en correlación los datos de la aplicación personalizada con el usuario del proveedor de identidades de Microsoft.
  • oid: se trata del identificador de usuario universal en todas las aplicaciones del proveedor de identidades de Microsoft. Almacene este valor si necesita realizar un seguimiento del usuario en varias aplicaciones del proveedor de identidades.

8. Limpieza de recursos

Cuando haya terminado de usar este tutorial, elimine la aplicación de la lista de registros de aplicaciones de Azure Portal.

Si quiere mantener la aplicación pero desea revocar el permiso dado a la aplicación por su cuenta de usuario específica, use uno de los siguientes vínculos:

Pasos siguientes