Saltar a contenido

architecture-it/react-auth

Descripción

Librería React-Auth que permite autenticarte con diferentes proveedores de identidad siempre que utilicen los protocolos de autorización y autenticación OAuth2 y OIDC. Se tomó como base la librería react-oauth2-code-pkce y se adaptó para que trabaje con client-secret en los casos que lo requiera (es configurable). Además se incorporó las funcionalidades necesarias para interpretar todos los flujos de Azure AD B2C y el manejo de las custom policies (también configurables).

Features

  • Permite realizar una llamada previa y/o posterior al inicio de sesión.
  • Actualiza silenciosamente los tokens de acceso de corta duración en segundo plano
  • Decodifica JWT
  • Permite adaptarse a idps que no cumplan con el standar por medio de los extraParameters.
  • Exporta funciones utiles como: isB2C : Si el proveedor es Azure B2C DecodeJWT : Recibe un token y retorna el decode.

Configuración

Parámetros

El AuthProvider recibe un objeto que trabaja con estos parametros:

export type TAuthConfig = {
  clientId: string;
  authorizationEndpoint: string;
  redirectUri: string;
  scope?: string;
  state?: string;
  logoutRedirect?: string;
  tokenEndpoint?: string; // don´t use for AD B2C
  logoutEndpoint?: string; // don´t use for AD B2C
  tokenRevokeEndpoint?: string; //don´t use for AD B2C
  preLogin?: () => void; //action to execute pre login
  postLogin?: () => void;//action to execute post login
  decodeToken?: boolean; //default true
  autoLogin?: boolean;  //redirects to login when launching the app. Default true
  clearURL?: boolean;
  extraAuthParams?: { [key: string]: string | boolean | number };
  extraAuthParameters?: { [key: string]: string | boolean | number };
  extraTokenParameters?: { [key: string]: string | boolean | number };
  extraLogoutParameters?: { [key: string]: string | boolean | number };
  tokenExpiresIn?: number;
  refreshTokenExpiresIn?: number;
  storage?: "session" | "local";
  customPolicies?: ICustomPolicies; //use only for ADB2C
  clientSecret?: string;
};

Importante

Para la configuracion con azure B2C el unico endpoint que se debe configurar es el authorizationEndpoint con la url del TenantDomain. Tambien es importante enviar las customPolicie ya que no toma ninguna por default. Las properties extraParameters permiten adaptar aquellos idp que no forman parte del standar.

Enviroment

En el template se adaptó un modelo que toma en cuenta las variables de entorno utilizando la librería interna para las variables de entorno architecture-it/react-env para realizar la comunicacion con el identity provider que se desee implementar.

Por lo que se deben completar las variables de entorno de acuerdo al provider que se va a utilizar, a continuación un ejemplo de la configuración con el Provider standar dentro de la compañia:

.env
REACT_APP_CLIENT_ID=58fb34da-0a3e-45f6-aa35-f806a684433a
REACT_APP_AUTH_DOMAIN=https://andreanib2cdev.b2clogin.com/andreanib2cdev.onmicrosoft.com
REACT_APP_REDIRECTURI=http://localhost:3000
REACT_APP_B2C_SIGNUPSIGNIN=B2C_1A_SIGNUP_SIGNINAAD
REACT_APP_B2C_FORGOTPASSWORD=B2C_1A_PASSWORDRESET
REACT_APP_B2C_EDITPROFILE=B2C_1A_PROFILEEDIT
.env
REACT_APP_CLIENT_ID=745628305089-gclpkcac8vnrk5clo1e32g9tnh5l6tvv.apps.googleusercontent.com
REACT_APP_AUTH_DOMAIN=https://accounts.google.com/o/oauth2/v2/auth
REACT_APP_TOKEN_ENDPOINT=https://oauth2.googleapis.com/token
REACT_APP_TOKEN_REVOKE=https://oauth2.googleapis.com/revoke
REACT_APP_REDIRECTURI=http://localhost:3000
REACT_APP_CLIENT_SECRET=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
REACT_APP_SCOPE='email openid profile'
.env
REACT_APP_CLIENT_ID=745628305089-gclpkcac8vnrk5clo1e32g9tnh5l6tvv.apps.googleusercontent.com
REACT_APP_AUTH_DOMAIN=https://accounts.google.com/o/oauth2/v2/auth
REACT_APP_TOKEN_ENDPOINT=https://oauth2.googleapis.com/token
REACT_APP_TOKEN_REVOKE=https://oauth2.googleapis.com/revoke
REACT_APP_REDIRECTURI=http://localhost:3000
REACT_APP_CLIENT_SECRET=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
REACT_APP_SCOPE='email openid profile'

IAuthContext

A continuación se muestra el objeto que retorna useContext(AuthContext) provides con estos valores;

export interface IAuthContext {
  token: string;
  logOut: (_state?: string, _logoutHint?: string) => void;
  login: (_state?: string) => void;
  refreshAccessToken: (_initial?: boolean, _force?: boolean) => void;
  editProfile: () => void; //use only for AD B2C
  error: string | null;
  tokenData?: TTokenData; //accessToken decoded
  idTokenData?: TTokenData; //idToken decoded
  idToken?: string;
  loginInProgress: boolean;
}

AuthProvider

App.tsx
import { AuthProvider, TAuthConfig } from "@architecture-it/react-auth";

const authConfig : TAuthConfig={
  //my configuration
  clientId="";
  authorizationEndpoint="";
  //....
}

  return (
    <StyleSystemProvider>
      <AuthProvider authConfig={authConfig}>
        <Main>
          <ErrorBoundary>
            <AppRoutes />
          </ErrorBoundary>
        </Main>
      </AuthProvider>
    </StyleSystemProvider>
  );

useContext

index.tsx
import { AuthContext } from "@architecture-it/react-auth";

function Home() {
  const { token , tokenData} = useContext(AuthContext);

  return (
    <>
      {token ? (
        <Box className={styles.page} data-testid="home-page">
          <Typography className={styles.text} data-testid="text" variant="h1">
            <span className={styles.span}>Hola</span> {tokenData?.name}!
          </Typography>
        </Box>
      ) : (
        <Box className={styles.page} data-testid="home-page">
          <Typography className={styles.text} data-testid="text" variant="h2">
            Por favor inicia sesión para ver la información de usuario.
          </Typography>
        </Box>
      )}{" "}
    </>
  );
}

Important

Cosas a tener en cuenta:

Para la adaptacion a azureB2C que manejamos hoy en la compañia, fue necesario agregar customPolicies en el modelo de configuracion. Para la utilizacion de customPolicies hay que tener en cuenta que:

  • si usa customPolicies se asume que utiliza Azure B2C
  • Si usa customPolicies tendra disponible el editProfile ya que es exclusivo de dicho idp.
  • Si usa customPolicies no se debe agregar los endpoint de token, logout y revoke.
  • Los endpoint logout y revoke no son compatibles, es posible que solo debas utilizar uno.
  • Se agrego el ClientSecret para que permita conexiones

Migración desde architecture-it/azure-b2c

Frente a la disponibilización de una nueva librería multiprovider para proyectos frontend, aquellas aplicaciones que utilizan @architecture-it/azure-b2c deberán migrar a @architecture-it/react-auth.

Para ello se describen a continuación los pasos a seguir para realizar la migración de forma correcta y configurar nuevamente el provider de autenticación.

Paso 1 - Eliminar Azure B2C

En primera instancia se deberá eliminar todas las configuraciones relacionadas a la librería @architecture-it/azure-b2c correspondientes a la autenticación con B2C.

  1. Desinstalar architecture-it/azure-b2c.
  2. Eliminar archivo msalInstance.ts.
  3. Eliminar el provider de MSAL que se encuentra en App.tsx y la instancia de msal que se le pasa por parámetro desde index.tsx.

Paso 2 - Instalar React Auth

Luego de eliminar las configuraciones relacionadas con la librería anterior, se deberá instalar la nueva librería:

// Usar manejador de paquetes correspondiente a su aplicación (pnpm, npm)
pnpm add @architecture-it/react-auth

Paso 3 - Configuraciones

Con la librería ya instalada se detallan a continuación las configuraciones que se deben realizar para su correcto funcionamiento:

  1. Agregar como wrapper general el provider de autenticación en el archivo principal de la aplicación (por lo general App.tsx).

    ... // other imports
    import { AuthProvider } from "@architecture-it/react-auth";
    
    export default function App() {
      return (
        <AuthProvider>
          <Main>
            ...
              <AppRoutes />
            ...
          </Main>
        </AuthProvider>
      );
    }
    
  2. Agregar las variables de entorno de acuerdo al provider de autenticación que se quiera utilizar, en este ejemplo se configurará Azure B2C:

# AUTH
REACT_APP_CLIENT_ID=token-client-id
REACT_APP_REDIRECTURI=http://localhost:3000
REACT_APP_AUTH_DOMAIN=url-auth-domain
REACT_APP_SCOPE=openid profile offline_access
REACT_APP_B2C_SIGNUPSIGNIN=B2C_1A_SIGNUP_SIGNINAAD
REACT_APP_B2C_FORGOTPASSWORD=B2C_1A_PASSWORDRESET
REACT_APP_B2C_EDITPROFILE=B2C_1A_PROFILEEDIT
!!! important "Importante"

    De acuerdo al provider que se quiera configurar, las variables de entorno que se deben agregar difieren. Se recuerda que en el apartado [Environment](https://architecture-it.github.io/docs/Platform/Front/Librerias/react-auth/#enviroment) se explica.
  1. Modificar el interceptor de los servicios, por el nuevo que se deberá importar desde la librería react-auth. Si utilizan los templates disponibilizados por Arquitectura en su aplicación, los archivos de servicios utilizan el interceptor de axios bajo la función addResponseInterceptorRefreshToken a la que se le pasa como parámetro la instancia de msal que previamente eliminamos.

    Esta función deberá ser reemplazada por addResponseInterceptorAxios como se muestra a continuación:

    import { addResponseInterceptorAxios } from "@architecture-it/react-auth";
    import { ServiceBase } from "@architecture-it/core";
    import env from "@architecture-it/react-env";
    import axios from "axios";
    
    ...
    
    class _UserService extends ServiceBase {
      constructor() {
        super(BASE_URL);
    
        addResponseInterceptorAxios(this.client, axios);
      }
    
    ...
    }
    
    const UserService = new _UserService();
    
    export default UserService;
    
  2. Reemplazar los hooks y componentes que utilicen en la aplicación provenientes de azure-b2c como puede ser el caso del hook useUser() o los componentes <UserAvatar /> o <SidebarWrapper />. Su importación deberá ser actualizada desde la librería react-auth.

useReactAuth

//index.tsx

import { useReactAuth } from "@architecture-it/react-auth";

function Home() {
  const { token , tokenData} = useReactAuth();

  return (
    <>
      {token ? (
        <Box className={styles.page} data-testid="home-page">
          <Typography className={styles.text} data-testid="text" variant="h1">
            <span className={styles.span}>Hola</span> {tokenData?.name}!
          </Typography>
        </Box>
      ) : (
        <Box className={styles.page} data-testid="home-page">
          <Typography className={styles.text} data-testid="text" variant="h2">
            Por favor inicia sesión para ver la información de usuario.
          </Typography>
        </Box>
      )}{" "}
    </>
  );
}

Componentes Visuales

SidebarWrapper

Abstracción del Sidebar del Stylesystem, tiene dos propiedades aparte authenticateRoutes y unauthenticatedRoutes.

El objeto authenticatedRoutes tiene los mismos del SidebarItemProp ( el que recibe el Sidebar) pero se le agrega una propiedad más que es isAuthorized, obligatorio por si es necesario manejar roles y permisos especificos en el sidebar.

Uso Habitual

      <SidebarWrapper
        authenticatedRoutes={[
          {
            item: "Item que se ve autenticado",
            onClick: () => {
              if (pathname !== "/") {
                navigate("");
              }
              handleClose();
            },
            selected: pathname === "/",
            className: styles.listItem,
            divider: true,
            isAuthorized: true,
          },
          {
            item: "Administración de Usuarios",
            onClick: () => {
              navigate("/person");

              handleClose();
            },
            className: styles.listItem,
            //Aca se puede colocar un hook o funcion que determine si está autorizado o no
            isAuthorized: true,
          },
        ]}
        open={open}
        onClose={handleClose}
        onOpen={handleOpen}
      />

UserAvatar

  • Component that show and handle login and logout.
  • Shows only an icon on determinate viewport (max-width:425px)

Componente que dispone internamente el login y el logout.

Esta hecho para ser usado en el navBar en principio pero nada limita utilizarlo en otro lugar.

En determinado viewport ( (max-width:425px)) solo muestra un icono

//layout

<Header onClickButton={handleOpen}>
    <UserAvatar />
</Header>

!IMPORTANTE! Cosas a tener en cuenta: Para la adaptacion a azureB2C que manejamos hoy en la compañia, fue necesario agregar customPolicies en el modelo de configuracion. Para la utilizacion de customPolicies hay que tener en cuenta que:

  • si usa customPolicies se asume que utiliza Azure B2C
  • Si usa customPolicies tendra disponible el editProfile ya que es exclusivo de dicho idp.
  • Si usa customPolicies no se debe agregar los endpoint de token, logout y revoke.
  • Los endpoint logout y revoke no son compatibles, es posible que solo debas utilizar uno.
  • Se agrego el ClientSecret para que permita conexiones

Casos avanzados

Agregar query Params al login

Agregando simplemente una variable de entorno en tu archivo .env con el prefijo REACT_APP_EXTRA_AUTH_PARAM_.

Se construye d ela siguiente manera:

REACT_APP_EXTRA_AUTH_PARAM_{NOMBRE_DE_PARAMETRO}=VALOR

Por lo que daría cómo resultado el siguiente queryParam: https://...?{NOMBRE_DE_PARAMETRO}=VALOR

Casos de Uso

Login directo con Azure:

Agregando el siguiente valor en el archivo .env:

REACT_APP_EXTRA_AUTH_PARAM_DOMAIN_HINT=Andreani

Va a bypassear el login y va a redirigir directamente al login de Azure (microsoft) con el dominio de Azure B2C de Andreani.