shared-lib-react
v1.0.8
Published
React/Next.js Librería Framework para autenticación, autorización y componentes de UI
Maintainers
Readme
@mh-dinafi-frmk/shared-lib-react
Librería React / Next.js 15 del Framework institucional del Ministerio de Hacienda de El Salvador (DINAFI).
Provee autenticación OAuth 2 / OIDC (Keycloak), autorización basada en roles y grupos, componentes de Login y Dashboard, configuración dinámica de 3 capas y design tokens institucionales.
📦 Equivalente funcional de
@mh-dinafi-frmk/shared-lib-angularv2.x
Tabla de Contenidos
- Requisitos
- Instalación
- Configuración rápida
- AuthProvider
- Componentes
- Guard de rutas —
withAuth - Hooks
- Servicios
- Zustand Config Store
- Estilos y Design Tokens
- Arquitectura de configuración 3 capas
- Migración desde lib-angular
- Scripts
- Contribución
Requisitos
| Dependencia | Versión mínima | | -------------- | -------------- | | React | ≥ 18 | | Next.js | ≥ 15 (App Router) | | Node.js | ≥ 18 | | Sass | ≥ 1.80 |
Instalación
npm install @mh-dinafi-frmk/shared-lib-reactPeer dependencies que ya deben existir en su proyecto:
npm install react react-dom next oidc-client-tsConfiguración rápida
1. Envolver la aplicación con AuthProvider
// app/layout.tsx (Next.js 15 App Router)
import { AuthProvider } from '@mh-dinafi-frmk/shared-lib-react';
import '@mh-dinafi-frmk/shared-lib-react/dist/index.css';
const frmkConfig = {
configServerHost: 'config-server.ejemplo.gob.sv',
frontendId: 'mi-aplicacion',
realm: 'mi-realm',
requireHttps: true,
loginConfig: {
title: 'Mi Aplicación',
subtitle: 'Ministerio de Hacienda',
brandingTitle: 'Nombre del Sistema',
brandingSubtitle: 'Descripción breve del sistema',
},
dashboardConfig: {
title: 'Mi Aplicación',
institutionName: 'Ministerio de Hacienda',
},
// Opcional: OAuth directo (sin Config Server)
oauth: {
clientId: 'mi-client-id',
keycloakHost: 'keycloak.ejemplo.gob.sv',
authServerHost: 'auth-proxy.ejemplo.gob.sv',
scope: 'openid email profile',
},
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="es">
<body>
<AuthProvider config={frmkConfig}>
{children}
</AuthProvider>
</body>
</html>
);
}2. Página de Login
// app/login/page.tsx
import { Login } from '@mh-dinafi-frmk/shared-lib-react';
export default function LoginPage() {
return <Login dashboardUrl="/dashboard" />;
}3. Dashboard con protección
// app/dashboard/layout.tsx
'use client';
import { Dashboard, withAuth } from '@mh-dinafi-frmk/shared-lib-react';
function DashboardLayout({ children }: { children: React.ReactNode }) {
return <Dashboard>{children}</Dashboard>;
}
export default withAuth(DashboardLayout);AuthProvider
El AuthProvider es el componente raíz que inicializa toda la librería:
<AuthProvider config={frmkConfig}>
{children}
</AuthProvider>Props:
| Prop | Tipo | Descripción |
| -------- | ------------------ | ----------- |
| config | FrmkLibraryConfig | Configuración de la librería (ver interfaz completa abajo) |
| children | ReactNode | Contenido de la aplicación |
Flujo de inicialización:
- Inicializa el Zustand store con los defaults +
config - Solicita configuración al Config Server (
configService.loadConfig()) - Inicializa
oidc-client-tsUserManager con la configuración resultante - Ejecuta
runInitialLoginSequence()(callback handling, silent renew, etc.)
Componentes
Login
Componente de login con diseño institucional de panel dividido (branding panel + formulario).
import { Login } from '@mh-dinafi-frmk/shared-lib-react';
<Login
dashboardUrl="/dashboard"
title="Mi Sistema"
subtitle="Bienvenido al portal"
brandingTitle="Sistema de Gestión"
brandingSubtitle="Plataforma institucional"
showForgotLinks={true}
forgotPasswordUrl="https://keycloak.example.com/forgot-password"
forgotPasswordText="¿Olvidó su contraseña?"
forgotUsernameUrl="https://keycloak.example.com/forgot-username"
forgotUsernameText="¿Olvidó su usuario?"
showLogoutButton={true}
/>Props opcionales: Todos sobreescriben la configuración del store.
| Prop | Tipo | Default |
| -------------------- | -------- | ------- |
| dashboardUrl | string | '/dashboard' |
| title | string | Config del store |
| subtitle | string | Config del store |
| brandingTitle | string | Config del store |
| brandingSubtitle | string | Config del store |
| showForgotLinks | boolean | false |
| forgotPasswordUrl | string | '#' |
| forgotPasswordText | string | '¿Olvidó su contraseña?' |
| forgotUsernameUrl | string | '#' |
| forgotUsernameText | string | '¿Olvidó su usuario?' |
| showLogoutButton | boolean | false |
Dashboard
Layout principal de la aplicación con header, menú de navegación jerárquico, área de contenido y footer.
import { Dashboard } from '@mh-dinafi-frmk/shared-lib-react';
<Dashboard
title="Mi Sistema"
loginUrl="/login"
>
{/* Contenido de la página */}
<h1>Bienvenido</h1>
</Dashboard>Características:
- Header responsivo (mobile top/bottom + desktop)
- Navegación horizontal (desktop) / sidebar (mobile) con menú hamburguesa
- Menú jerárquico dinámico basado en permisos del usuario
- Panel de perfil de usuario con cerrar sesión
- Footer institucional
Guard de rutas — withAuth
HOC (Higher-Order Component) para proteger rutas que requieren autenticación.
import { withAuth } from '@mh-dinafi-frmk/shared-lib-react';
// Protección básica
export default withAuth(MiComponente);
// Con roles específicos
export default withAuth(MiComponente, {
roles: ['ADMIN', 'SUPERVISOR'],
loginUrl: '/login',
});
// Con componentes personalizados de loading y acceso denegado
export default withAuth(MiComponente, {
roles: ['ADMIN'],
loading: <MiSpinner />,
forbidden: <AccesoDenegado />,
});Opciones:
| Opción | Tipo | Default | Descripción |
| ----------- | ------------- | ------- | ----------- |
| loginUrl | string | '/login' | URL de redirección si no está autenticado |
| roles | string[] | undefined | Roles requeridos (any match) |
| loading | ReactNode | Spinner default | Componente mientras verifica autenticación |
| forbidden | ReactNode | Mensaje default | Componente si no tiene permisos |
Hooks
useAuth()
Accede al estado de autenticación reactivo.
import { useAuth } from '@mh-dinafi-frmk/shared-lib-react';
function MiComponente() {
const {
isAuthenticated,
isDoneLoading,
user,
identityClaims,
login,
logout,
getUserRoles,
getUserGroups,
hasRole,
hasAnyRole,
} = useAuth();
return (
<div>
{isAuthenticated ? (
<button onClick={logout}>Cerrar sesión</button>
) : (
<button onClick={login}>Iniciar sesión</button>
)}
</div>
);
}useAuthorization()
Verificación de permisos y menú jerárquico.
import { useAuthorization } from '@mh-dinafi-frmk/shared-lib-react';
function MiComponente() {
const {
verifyPermission,
checkAuthorization,
getMenuHierarchy,
getRootMenuItems,
getChildMenuItems,
hasChildren,
} = useAuthorization();
useEffect(() => {
verifyPermission('/ruta/protegida').then(allowed => {
if (!allowed) router.push('/sin-acceso');
});
}, []);
}useConfig()
Acceso a la configuración de la aplicación.
import { useConfig } from '@mh-dinafi-frmk/shared-lib-react';
function MiComponente() {
const {
appConfig,
loginConfig,
dashboardConfig,
libraryConfig,
isConfigured,
} = useConfig();
return <span>{appConfig.auth.issuer}</span>;
}Servicios
Los servicios se usan internamente pero están expuestos para uso avanzado:
| Servicio | Descripción |
| ---------------------- | ----------- |
| AuthService | Manejo de OAuth2/OIDC con oidc-client-ts (login, logout, tokens, roles) |
| AuthorizationService | Verificación de permisos contra el backend de autorización |
| ConfigService | Carga de configuración del Config Server |
| TokenStorageService | Gestión de tokens en localStorage/sessionStorage |
Zustand Config Store
La librería usa un store Zustand (useFrmkStore) para la gestión de estado:
import { useFrmkStore } from '@mh-dinafi-frmk/shared-lib-react';
// Acceso directo (fuera de React)
const appConfig = useFrmkStore.getState().appConfig();
const authConfig = useFrmkStore.getState().authConfig();
// Dentro de React (reactivo)
function MiComponente() {
const isConfigured = useFrmkStore(state => state.isConfigured());
}Estilos y Design Tokens
La librería incluye CSS compilado y SCSS source tokens para personalización:
CSS compilado (recomendado)
// En su layout raíz
import '@mh-dinafi-frmk/shared-lib-react/dist/index.css';SCSS tokens para personalización
// Importar tokens institucionales
@use '@mh-dinafi-frmk/shared-lib-react/styles' as frmk;
// Los tokens incluyen:
// - Colores institucionales (--frmk-primary, --frmk-secondary, etc.)
// - Tipografías (Bembo, MuseoSans)
// - Espaciado, bordes, sombras
// - Variables para login y dashboardAssets incluidos
- Fuentes: Bembo, MuseoSans
- Iconos: Material Symbols Outlined
- Logo: logo-mh.png (Ministerio de Hacienda)
Arquitectura de configuración 3 capas
┌─────────────────────────────────────────────┐
│ Capa 3: Config Server (runtime) │
│ → Sobreescribe todo en tiempo de ejecución │
├─────────────────────────────────────────────┤
│ Capa 2: FrmkLibraryConfig (compile-time) │
│ → Valores específicos de la aplicación │
├─────────────────────────────────────────────┤
│ Capa 1: Defaults de la librería │
│ → DEFAULT_LOGIN_CONFIG, DEFAULT_DASHBOARD_ │
│ CONFIG, DEFAULT_LIBRARY_CONFIG │
└─────────────────────────────────────────────┘Flujo:Defaults → se mezclan con → FrmkLibraryConfig → se sobreescriben por → ConfigServerResponse
Claves del Config Server
| Clave del servidor | Campo de configuración |
| ----------------------------- | ---------------------- |
| security.url.auth | hosts.authServer |
| security.url.keycloak | hosts.keycloak |
| security.url.callback | hosts.localApp |
| security.realm | paths.realm |
| security.scope | auth.scope |
| security.url.authz | authorization.baseUrl |
| security.url.https | environment.requireHttps |
| quarkus.oidc.client-id | auth.clientId |
Migración desde lib-angular
| Concepto Angular | Equivalente React |
| ----------------------------------- | ----------------- |
| FrmkModule.forRoot(config) | <AuthProvider config={config}> |
| FrmkAuthGuard | withAuth(Component, options) |
| FrmkOauthService (DI) | useAuth() hook |
| FrmkAuthzService (DI) | useAuthorization() hook |
| FrmkConfigService (DI) | useConfig() hook |
| FrmkLoginComponent | <Login /> |
| FrmkDashboardComponent | <Dashboard>{children}</Dashboard> |
| Angular Signals (computed()) | useMemo() / Zustand selectors |
| BehaviorSubject / RxJS | Zustand store + useSyncExternalStore |
| <router-outlet> | {children} prop |
| routerLink | next/link |
| *ngTemplateOutlet recursivo | Función recursiva React |
| @Input() | Props |
| @HostListener | useEffect + addEventListener |
Scripts
# Build (ESM + CJS + .d.ts + CSS)
npm run build
# Tests (Jest + RTL)
npm test
# Sincronizar versión package.json → version.ts
npm run sync-version
# Lint
npm run lintEstructura del proyecto
lib-react/
├── src/
│ ├── index.ts # Entry point (exports todo)
│ ├── version.ts # VERSION constante auto-sincronizada
│ ├── types.d.ts # Declaraciones de módulos (.scss, .png, etc.)
│ ├── models/ # Interfaces TypeScript
│ │ ├── app-config.interface.ts
│ │ ├── authorization.interface.ts
│ │ ├── dashboard-config.interface.ts
│ │ ├── library-config.interface.ts
│ │ └── login-config.interface.ts
│ ├── stores/
│ │ └── config-store.ts # Zustand store principal
│ ├── services/
│ │ ├── auth.service.ts # OAuth2/OIDC (oidc-client-ts)
│ │ ├── authorization.service.ts
│ │ ├── config.service.ts # Carga Config Server
│ │ └── token-storage.service.ts
│ ├── hooks/
│ │ └── index.ts # useAuth, useAuthorization, useConfig
│ ├── components/
│ │ ├── AuthProvider.tsx
│ │ ├── Login/
│ │ │ ├── Login.tsx
│ │ │ └── Login.module.scss
│ │ └── Dashboard/
│ │ ├── Dashboard.tsx
│ │ └── Dashboard.module.scss
│ ├── guards/
│ │ └── withAuth.tsx # HOC de protección de rutas
│ ├── styles/ # Design tokens SCSS
│ │ ├── _tokens.scss
│ │ ├── _auth.scss
│ │ ├── _dashboard.scss
│ │ └── _index.scss
│ └── assets/ # Fuentes, iconos, logos
├── scripts/
│ ├── sync-version.js
│ └── postbuild.js
├── jest.config.js
├── tsconfig.json
├── tsup.config.ts
└── package.jsonLicencia
Uso interno — Ministerio de Hacienda de El Salvador, DINAFI.
