ReactJS

MSAL la nueva librería de autenticación de Azure AD

Desde hace mucho tiempo, para los desarrollos que requerían de autenticación contra el Azure Active Directory se hacía uso de la librería ADAL (de la que hemos hablado en multitud de ocasiones).   Sin embargo, Microsoft anunció un nuevo endpoint de autenticación con una finalidad en la que añade novedades importantes que trataremos de desgranar en este artículo.


En primer lugar, se añadió soporte para más plataformas de identidad compatibles. El punto de conexión v1.0 solo permite iniciar sesión en la aplicación con cuentas profesionales y educativas (Azure AD). El punto de conexión de plataforma de identidad de Microsoft permite trabajo y cuentas educativas de Azure AD y cuentas personales de Microsoft (MSA), como hotmail.com, outlook.com y msn.com, para iniciar sesión.


Otra de las mejoras que se incorporó fueron  los permisos sobre las API. En la primera versión se pedían permisos sobre el «resource» y en la versión v2 es sobre el «scope».

Es decir, identificador de recursos o AppID URI: https://graph.windows.net/

Los ámbitos, o oAuth2Permissions: Directory.Read, Directory.Write, etc.
Estos son cambios internos que se hacen tanto para pedir los permisos implícitos, como en la generación del token para poder acceder, pero que hacen que la aplicación sea más segura y cumpla con los estándares de OpenID y Oauth2.0.

Todos estos cambios provocaron que la librería ADAL se quedara obsoleta. Desde el equipo de identidad de Microsoft optaron por la reescritura total de la librería para adaptarse a los nuevos requerimientos. Esta librería se llama MSAL y desde el anuncio en el Build el pasado mayo ya se puede utilizar en nuestros desarrollos sin ningún problema.

Cómo empezar a utilizarla en nuestros desarrollos

En este artículo desgranaremos cómo empezar a utilizarla en los  desarrollos en ReactJS y veremos todo lo que debemos  realizar desde el FrontEnd. En el caso de que necesitéis hacer uso en C# disponéis de este artículo que se públicó en la revista Compartimoss a cargo de Luis Mañez.

Requerimientos previos 

  • Se debe dar de alta una aplicación en el directorio activo de Azure. Si no sabéis cómo, debéis seguir estos pasos.
  • Tener la aplicación React creada. Para ello utilizaremos la herramienta proporcionada por Facebook tal y como se indica aquí.

Manos a la obra

En  toda aplicación en JS/TS  bastaría con añadir la dependencia de MSAL en el fichero package.json :

 "msal": "1.0.2"

Para el tema de autenticar nuestra aplicación vamos a crear un Higher Order Component(HOC) en React. Un HOC es una función que como entrada tiene un componente, y como salida de la misma, un nuevo componente. Este comportamiento es relativamente común dentro de los desarrollos en React.

En este ejemplo ¿qué sentido tiene tener un HOC para la autenticación? Al encapsular la autenticación en un componente podemos ceder la responsabilidad de la autenticación a este componte. Es decir, este HOC  se encargará de ver si el usuario está autenticado o no en la aplicación. En caso de que esté autenticado, le mostrará el componente que quiere visualizar, y en caso de que no esté autenticado, se le pedirá al usuario que introduzca sus credenciales. De esta forma se evitará que el usuario continúe con la aplicación hasta que finalmente le de un error porque no tiene permisos para realizar determinada acción.

Antes de meternos en faena, vamos a ver qué características tiene MSAL, qué opciones nos proporciona y cómo empezar a utilizarla. Al igual que hacíamos con ADAL, tenemos que tener un fichero de configuración en el que le indicaremos qué opciones necesitamos. Aquí están todas las opciones que se soportan hasta ahora.

type storage = "localStorage" | "sessionStorage";
// Protocol Support
export type AuthOptions = {
    clientId: string;
    authority?: string;
    validateAuthority?: boolean;
    redirectUri?: string | (() => string);
    postLogoutRedirectUri?: string | (() => string);
    navigateToLoginRequestUrl?: boolean;
};
// Cache Support
export type CacheOptions = {
    cacheLocation?: CacheLocation;
    storeAuthStateInCookie?: boolean;
};
// Library support
export type SystemOptions = {
    logger?: Logger;
    loadFrameTimeout?: number;
    tokenRenewalOffsetSeconds?: number;
};
// Developer App Environment Support
export type FrameworkOptions = {
    isAngular?: boolean;
    unprotectedResources?: Array<string>;
    protectedResourceMap?: Map<string, Array<string>>;
};
// Configuration Object
export type Configuration = {
    auth: AuthOptions,
    cache?: CacheOptions,
    system?: SystemOptions,
    framework?: FrameworkOptions
};

No es necesario que incluyamos todas estas opciones. Realmente, las únicas indispensables son las opciones de AuthOptions. Para una primera toma de contacto se podría hacer uso de un fichero de configuración similar al siguiente.

export const msalConfig = {
    authority: process.env.REACT_APP_MSALAUTHORITY,
    clientId: process.env.REACT_APP_MSALCLIENTID
  };

export const msalInstance = new UserAgentApplication({
  auth: msalConfig
});

Una vez tenemos el objeto msalInstance creado, tendremos que ver en qué estado está. Para ello disponemos de una serie de métodos en los que podemos saber en cada momento el estado en que se encuentra la autenticación. Por un lado, podemos ver el usuario que está autenticado con el método msalInstance.getAccount(). También podemos redirigir a la página de login invocando al método msalInstance.loginRedirect({}) o bien mostrarlo en un Popup. En definitiva, tenemos multitud de métodos que nos aportan mucho más control sobre la misma.

Creando el HOC

Junto con estos métodos más simples, también se incluyen la interacción de la librería cuando pierde el contexto, debido a que está en la propia página de Microsoft autenticando. Disponemos de métodos para saber el estado de la autenticación y el resultado de la misma. Partiendo de este supuesto mostramos el siguiente componente de ejemplo :

import * as React from 'react';
import { msalConfig } from '../msal/msalConfig';
import { LoginError,LoginInProgress } from '../login';
import { UserAgentApplication, AuthError } from 'msal';

export const msalInstance = new UserAgentApplication({
  auth: msalConfig
});

interface IState {
  authenticated: boolean;
  renewIframe: boolean;
  errorMessage: string;
  hasError: boolean;
}

export function withAuth<TOriginalProps>(
  WrappedComponent: React.ComponentClass<TOriginalProps> | React.StatelessComponent<TOriginalProps>
): React.ComponentClass<TOriginalProps> {
  return class Auth extends React.Component<TOriginalProps, IState> {
    constructor(props: TOriginalProps) {
      super(props);
      this.state = {
        authenticated: false,
        renewIframe: false,
        hasError: false,
        errorMessage: null
      };
    }

    public componentWillMount(): void {      
      msalInstance.handleRedirectCallback(() => { // on success
        this.setState({
          authenticated: true
        });       
      }, (authErr: AuthError, accountState: string) => {  // on fail
        console.log(authErr);
        this.setState({
          hasError: true,
          errorMessage: authErr.errorMessage
        });
      });

      if (msalInstance.isCallback(window.location.hash)) {
        this.setState({
          renewIframe: true
        });
        return;
      }      

      if (!msalInstance.getAccount()) {
        msalInstance.loginRedirect({});
        return;
      } else {
        this.setState({
          authenticated: true
        });        
      }
    }

    public render(): JSX.Element {
      if (this.state.renewIframe) {
        return <div>hidden renew iframe - not visible</div>;
      }
      if (this.state.authenticated) {
        return <WrappedComponent {...this.props} />;
      }
      if (this.state.hasError) {
        return <LoginError message={this.state.errorMessage} />;
      }
      return <LoginInProgress />;
    }    
  };
}

Ahora bien como podemos usar esta autenticación, pues antes de cargar un componente si invocamos a la función withAuth tendremos la funcionalidad del login. Quedaría de la siguiente forma:

export const routes =
    <Layout>
            <Route exact path='/' strict={false} component={withAuth(HomeContainer)} />
    </Layout>

El funcionamiento sería el siguiente:

  • Cuando el usuario vaya a la ruta raíz, se comprueba si el usuario está autenticado.
  • Si lo está, mostraría el componente HomeContainer.
  • En caso de que no lo esté, se redirigirá a la página de login del Azure Active Directory y cuando vuelva a la aplicación se controlará el resultado del login a través de los métodos handleRedirectCallback y/o isCallback.

Siguientes pasos

Una vez ya vemos cómo podemos empezar a utilizar MSAL, la siguiente pregunta que nos surge es si debemos migrar nuestros desarrollos de ADAL a MSAL… La respuesta es que es MUY recomendable hacerlo, principalmente porque MSAL cumple mejor con los estándares de OpenIdConnect y oAuth2.

Desde la propia documentación oficial de Microsoft indica los pasos que hay que realizar para migrar tanto de .NET como desde JS en los siguientes enlaces:
– https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-compare-msal-js-and-adal-js
-https://docs.microsoft.com/en-us/azure/active-directory/develop/msal-net-migration

Happy codding 🙂

Compartir
Publicado por
Adrián Díaz

Este sitio web utiliza cookies para que tengas la mejor experiencia de usuario. Si continuas navegando, estás dando tu consentimiento para aceptar las cookies y también nuestra política de cookies (esperemos que no te empaches con tanta cookie 😊)