npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

imansi-auth-react

v0.1.3

Published

Hooks de autenticación para React: login, registro, 2FA, OAuth (GitHub y Google), recuperación de contraseña y gestión de sesiones. Sin componentes visuales. Compatible con imansi-auth-node.

Readme

imansi-auth-react

Hooks de autenticación para React: login, registro, verificación en dos pasos (2FA), OAuth (GitHub y Google), recuperación de contraseña, cambio de contraseña, edición de perfil y gestión de sesiones.

Sin componentes visuales. Solo la lógica. Vos armás la UI como quieras.

Compatible con imansi-auth-node — el servidor de autenticación completo para Node.js.

Parte del ecosistema Imansi — autenticación moderna para tus apps.


⚠️ Recomendación importante

Esta librería está diseñada para funcionar con imansi-auth-node, el servidor de autenticación que la complementa.

Se recomienda fuertemente usar ambas librerías juntas. El frontend (imansi-auth-react) y el backend (imansi-auth-node) están diseñados para trabajar en conjunto sin configuración adicional. Comparten el mismo contrato HTTP y los mismos nombres en español.

┌─────────────────────┐         ┌─────────────────────┐
│  imansi-auth-react  │ ──────► │  imansi-auth-node   │
│   (tu frontend)     │  HTTP   │    (tu backend)     │
└─────────────────────┘         └─────────────────────┘

También funciona con cualquier backend que respete los endpoints documentados en la sección Endpoints esperados.


✨ Características

  • Hooks específicos para cada flujo: login, registro, 2FA, recuperación y más.
  • Sin componentes visuales: máxima libertad para diseñar tu UI.
  • OAuth con GitHub y Google listo para enchufar.
  • Sesión persistente con cookies httpOnly.
  • Refresh tokens automáticos sin que el usuario note nada.
  • Protección de rutas (<ProtegerRuta> y <RedirigirSiAutenticado>).
  • 100% configurable desde props y configuración.
  • JavaScript puro. Sin TypeScript, sin sorpresas.
  • Cero dependencias de UI. Vos decidís cómo se ve todo.

📦 Instalación

npm install imansi-auth-react

Requiere React 18+ y react-router-dom 6+ (van como peerDependencies):

npm install react react-dom react-router-dom

Para el backend (recomendado):

npm install imansi-auth-node

🚀 Uso rápido

1. Envolvé tu app con <ProveedorAutenticacion>

// src/App.jsx
import { BrowserRouter } from "react-router-dom";
import { ProveedorAutenticacion } from "imansi-auth-react";

const configuracion = {
  urlBase: "http://localhost:3000",
  modoAlmacenamiento: "httpOnly",
  incluirCredenciales: true,
  rutaLogin: "/login",
  rutaTrasLogin: "/panel",
  rutaTrasRegistro: "/panel"
};

export default function App() {
  return (
    <BrowserRouter>
      <ProveedorAutenticacion configuracion={configuracion}>
        {/* Tu app */}
      </ProveedorAutenticacion>
    </BrowserRouter>
  );
}

2. Armá tu propio login con useLogin

// src/paginas/MiLogin.jsx
import { useState } from "react";
import { useNavigate, Link } from "react-router-dom";
import { useLogin } from "imansi-auth-react";

export function MiLogin() {
  const { iniciarSesion, requiereVerificacion2fa, error } = useLogin();
  const navegar = useNavigate();

  const [correo, setCorreo] = useState("");
  const [contrasena, setContrasena] = useState("");
  const [enviando, setEnviando] = useState(false);

  // Si el backend pide 2FA, mostramos nuestra pantalla de verificación.
  if (requiereVerificacion2fa) {
    return <MiVerificacion2FA />;
  }

  async function manejar(evento) {
    evento.preventDefault();
    setEnviando(true);

    const resultado = await iniciarSesion({ correo, contrasena });

    setEnviando(false);

    if (!resultado.ok) {
      alert(resultado.error.mensaje);
      return;
    }

    if (resultado.requiere2fa) return;

    navegar("/panel");
  }

  return (
    <form onSubmit={manejar}>
      <h1>Iniciar sesión</h1>

      <input
        type="email"
        placeholder="Correo"
        value={correo}
        onChange={(e) => setCorreo(e.target.value)}
      />

      <input
        type="password"
        placeholder="Contraseña"
        value={contrasena}
        onChange={(e) => setContrasena(e.target.value)}
      />

      {error && <p style={{ color: "red" }}>{error.mensaje}</p>}

      <button type="submit" disabled={enviando}>
        {enviando ? "Entrando..." : "Entrar"}
      </button>

      <Link to="/recuperar">¿Olvidaste tu contraseña?</Link>
      <Link to="/registro">Crear cuenta</Link>
    </form>
  );
}

3. Protegé tus rutas

// src/App.jsx (continuación)
import { Routes, Route } from "react-router-dom";
import {
  ProtegerRuta,
  RedirigirSiAutenticado
} from "imansi-auth-react";

<Routes>
  <Route
    path="/login"
    element={
      <RedirigirSiAutenticado>
        <MiLogin />
      </RedirigirSiAutenticado>
    }
  />

  <Route
    path="/panel"
    element={
      <ProtegerRuta cargando={<p>Cargando...</p>}>
        <MiPanel />
      </ProtegerRuta>
    }
  />
</Routes>

Listo. Con eso ya tenés el flujo básico funcionando.


🪝 Hooks disponibles

Todos los hooks se importan desde imansi-auth-react.

useLogin()

Login con email y contraseña.

const {
  iniciarSesion,
  cargando,
  error,
  requiereVerificacion2fa,
  idPendiente2fa,
  cancelar2fa,
  autenticado
} = useLogin();

await iniciarSesion({ correo, contrasena });
// → { ok: true, usuario } | { ok: true, requiere2fa: true } | { ok: false, error }

useRegistro()

Registro con email, contraseña y nombre opcional.

const { registrarUsuario, error, requiereVerificacion2fa } = useRegistro();

await registrarUsuario({ nombre, correo, contrasena });
// → { ok: true, usuario } | { ok: true, requiere2fa: true } | { ok: false, error }

useVerificar2FA()

Verifica el código de 2FA.

const {
  verificarDosFactores,
  reenviarDosFactores,
  cancelar2fa,
  requiereVerificacion2fa,
  error
} = useVerificar2FA();

await verificarDosFactores("353251");
// → { ok: true, usuario } | { ok: false, error }

await reenviarDosFactores();
// → { ok: true } | { ok: false, error }

useRecuperacion()

Solicitar y restablecer contraseña.

const { solicitarRecuperacion, restablecerContrasena, error } = useRecuperacion();

// Paso 1: pedir el link
await solicitarRecuperacion("[email protected]");
// → { ok: true, mensaje }

// Paso 2: con el token del link
await restablecerContrasena({ token, contrasena: "nueva123" });
// → { ok: true, mensaje } | { ok: false, error }

useCambioContrasena()

Cambiar la contraseña estando logueado.

const { cambiarContrasena, usuario, error, autenticado } = useCambioContrasena();

// Verificar que el usuario tenga contraseña (no sea solo-OAuth)
if (usuario?.tieneContrasena) {
  await cambiarContrasena({ contrasenaActual, contrasenaNueva });
  // → { ok: true, mensaje } | { ok: false, error }
}

Importante: los usuarios que se registraron solo con OAuth (GitHub/Google) tienen usuario.tieneContrasena === false. No pueden cambiar la contraseña porque no tienen.


useEditarPerfil()

Editar el nombre del usuario logueado.

const { editarPerfil, usuario, error } = useEditarPerfil();

await editarPerfil({ nombre: "Ivan Mansilla" });
// → { ok: true, usuario, mensaje } | { ok: false, error }

useSesiones()

Listar y revocar sesiones activas.

const { listarSesiones, revocarSesion, error } = useSesiones();

const sesiones = await listarSesiones();
// → [{ id, dispositivo, ip, actual, creadoEn, ultimaActividad }, ...]

await revocarSesion(sesiones[0].id);
// → { ok: true } | { ok: false, error }

useOAuth()

Iniciar el flujo OAuth con GitHub o Google.

const { iniciarOAuth, proveedores, textos } = useOAuth();

// proveedores → ["github", "google"] (los que configuraste)

<button onClick={() => iniciarOAuth("github")}>
  Continuar con GitHub
</button>

// O con redirección específica:
<button onClick={() => iniciarOAuth("github", "/panel")}>
  Continuar con GitHub
</button>

Nota: iniciarOAuth redirige el navegador al backend. No es un fetch, es un window.location.href.


useAutenticacion()

Hook completo con todo. Útil si preferís un solo hook en lugar de los específicos.

const {
  // Estado
  usuario,
  token,
  sesion,
  autenticado,
  cargando,
  error,

  // Acciones
  iniciarSesion,
  registrarUsuario,
  cerrarSesion,
  recargarUsuario,

  // 2FA
  idPendiente2fa,
  requiereVerificacion2fa,
  verificarDosFactores,
  reenviarDosFactores,
  cancelar2fa,

  // Sesiones
  listarSesiones,
  revocarSesion,

  // Recuperación
  solicitarRecuperacion,
  restablecerContrasena,

  // Cuenta
  cambiarContrasena,
  editarPerfil,

  // Configuración efectiva
  configuracion
} = useAutenticacion();

🛡️ Protección de rutas

<ProtegerRuta>

Bloquea rutas privadas. Si no hay sesión, redirige al login.

import { ProtegerRuta } from "imansi-auth-react";

<Route
  path="/panel"
  element={
    <ProtegerRuta cargando={<p>Cargando...</p>}>
      <Panel />
    </ProtegerRuta>
  }
/>

| Prop | Tipo | Descripción | |---|---|---| | redireccion | string | Ruta a la que redirigir si no hay sesión (por defecto configuracion.rutaLogin) | | cargando | ReactNode | Qué mostrar mientras se valida la sesión inicial |


<RedirigirSiAutenticado>

Para rutas públicas (login, registro). Si ya hay sesión, redirige.

import { RedirigirSiAutenticado } from "imansi-auth-react";

<Route
  path="/login"
  element={
    <RedirigirSiAutenticado>
      <MiLogin />
    </RedirigirSiAutenticado>
  }
/>

| Prop | Tipo | Descripción | |---|---|---| | redireccion | string | Ruta a la que redirigir si hay sesión (por defecto configuracion.rutaTrasLogin) | | cargando | ReactNode | Qué mostrar mientras se valida la sesión |


📚 Ejemplos completos

Ejemplo 1: Registro con validación

import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useRegistro } from "imansi-auth-react";

export function MiRegistro() {
  const { registrarUsuario, error, requiereVerificacion2fa } = useRegistro();
  const navegar = useNavigate();

  const [nombre, setNombre] = useState("");
  const [correo, setCorreo] = useState("");
  const [contrasena, setContrasena] = useState("");
  const [contrasenaRepetir, setContrasenaRepetir] = useState("");
  const [errorLocal, setErrorLocal] = useState(null);

  if (requiereVerificacion2fa) {
    return <MiVerificacion2FA />;
  }

  async function manejar(evento) {
    evento.preventDefault();
    setErrorLocal(null);

    if (contrasena !== contrasenaRepetir) {
      setErrorLocal("Las contraseñas no coinciden");
      return;
    }

    if (contrasena.length < 6) {
      setErrorLocal("La contraseña debe tener al menos 6 caracteres");
      return;
    }

    const resultado = await registrarUsuario({ nombre, correo, contrasena });

    if (!resultado.ok) {
      setErrorLocal(resultado.error.mensaje);
      return;
    }

    if (resultado.requiere2fa) return;

    navegar("/panel");
  }

  return (
    <form onSubmit={manejar}>
      <h1>Crear cuenta</h1>

      <input
        placeholder="Nombre (opcional)"
        value={nombre}
        onChange={(e) => setNombre(e.target.value)}
      />

      <input
        type="email"
        placeholder="Correo"
        value={correo}
        onChange={(e) => setCorreo(e.target.value)}
        required
      />

      <input
        type="password"
        placeholder="Contraseña"
        value={contrasena}
        onChange={(e) => setContrasena(e.target.value)}
        required
      />

      <input
        type="password"
        placeholder="Repetir contraseña"
        value={contrasenaRepetir}
        onChange={(e) => setContrasenaRepetir(e.target.value)}
        required
      />

      {(errorLocal || error) && (
        <p style={{ color: "red" }}>{errorLocal || error.mensaje}</p>
      )}

      <button type="submit">Registrarme</button>
    </form>
  );
}

Ejemplo 2: Verificación 2FA

import { useState } from "react";
import { useNavigate } from "react-router-dom";
import { useVerificar2FA } from "imansi-auth-react";

export function MiVerificacion2FA() {
  const {
    verificarDosFactores,
    reenviarDosFactores,
    cancelar2fa,
    error
  } = useVerificar2FA();

  const navegar = useNavigate();
  const [codigo, setCodigo] = useState("");
  const [aviso, setAviso] = useState(null);

  async function manejar(evento) {
    evento.preventDefault();

    const resultado = await verificarDosFactores(codigo);

    if (!resultado.ok) {
      return;
    }

    navegar("/panel");
  }

  async function reenviar() {
    await reenviarDosFactores();
    setAviso("Código reenviado. Revisá tu correo.");
  }

  function cancelar() {
    cancelar2fa();
    navegar("/login");
  }

  return (
    <form onSubmit={manejar}>
      <h1>Verificación en dos pasos</h1>
      <p>Ingresá el código que enviamos a tu correo.</p>

      <input
        inputMode="numeric"
        placeholder="Código de 6 dígitos"
        value={codigo}
        onChange={(e) => setCodigo(e.target.value)}
        autoFocus
        required
      />

      {error && <p style={{ color: "red" }}>{error.mensaje}</p>}
      {aviso && <p style={{ color: "green" }}>{aviso}</p>}

      <button type="submit">Verificar</button>
      <button type="button" onClick={reenviar}>Reenviar código</button>
      <button type="button" onClick={cancelar}>Cancelar</button>
    </form>
  );
}

Ejemplo 3: Recuperación de contraseña

Paso 1: solicitar

import { useState } from "react";
import { useRecuperacion } from "imansi-auth-react";

export function MiSolicitarRecuperacion() {
  const { solicitarRecuperacion } = useRecuperacion();
  const [correo, setCorreo] = useState("");
  const [exito, setExito] = useState(false);

  async function manejar(evento) {
    evento.preventDefault();
    await solicitarRecuperacion(correo);
    setExito(true);
  }

  if (exito) {
    return (
      <p>
        Si el correo está registrado, te enviamos un link para restablecer
        tu contraseña. Revisá tu bandeja de entrada (y spam).
      </p>
    );
  }

  return (
    <form onSubmit={manejar}>
      <h1>Recuperar contraseña</h1>

      <input
        type="email"
        placeholder="Tu correo"
        value={correo}
        onChange={(e) => setCorreo(e.target.value)}
        required
      />

      <button type="submit">Enviar link</button>
    </form>
  );
}

Paso 2: restablecer

import { useState } from "react";
import { useSearchParams, useNavigate } from "react-router-dom";
import { useRecuperacion } from "imansi-auth-react";

export function MiRestablecer() {
  const { restablecerContrasena, error } = useRecuperacion();
  const navegar = useNavigate();
  const [searchParams] = useSearchParams();

  const token = searchParams.get("token");
  const [contrasena, setContrasena] = useState("");
  const [contrasenaRepetir, setContrasenaRepetir] = useState("");
  const [errorLocal, setErrorLocal] = useState(null);

  async function manejar(evento) {
    evento.preventDefault();
    setErrorLocal(null);

    if (contrasena !== contrasenaRepetir) {
      setErrorLocal("Las contraseñas no coinciden");
      return;
    }

    if (contrasena.length < 6) {
      setErrorLocal("La contraseña debe tener al menos 6 caracteres");
      return;
    }

    const resultado = await restablecerContrasena({ token, contrasena });

    if (resultado.ok) {
      alert("Contraseña actualizada. Ya podés iniciar sesión.");
      navegar("/login");
    }
  }

  if (!token) {
    return <p>El link no es válido. Pedí uno nuevo.</p>;
  }

  return (
    <form onSubmit={manejar}>
      <h1>Nueva contraseña</h1>

      <input
        type="password"
        placeholder="Nueva contraseña"
        value={contrasena}
        onChange={(e) => setContrasena(e.target.value)}
        required
      />

      <input
        type="password"
        placeholder="Repetir contraseña"
        value={contrasenaRepetir}
        onChange={(e) => setContrasenaRepetir(e.target.value)}
        required
      />

      {(errorLocal || error) && (
        <p style={{ color: "red" }}>{errorLocal || error.mensaje}</p>
      )}

      <button type="submit">Restablecer contraseña</button>
    </form>
  );
}

Ejemplo 4: OAuth (GitHub + Google)

import { useOAuth } from "imansi-auth-react";

export function MisBotonesOAuth() {
  const { iniciarOAuth, proveedores, textos } = useOAuth();

  return (
    <div>
      {proveedores.includes("github") && (
        <button onClick={() => iniciarOAuth("github")}>
          {textos.github || "Continuar con GitHub"}
        </button>
      )}

      {proveedores.includes("google") && (
        <button onClick={() => iniciarOAuth("google")}>
          {textos.google || "Continuar con Google"}
        </button>
      )}
    </div>
  );
}

Requisitos:

  • El backend debe tener configuradas las credenciales de GitHub/Google.
  • El urlBase en la configuración debe apuntar al backend correcto.
  • La URL del callback en GitHub/Google debe ser {urlBase}/auth/oauth/{proveedor}/callback.

Ejemplo 5: Mi cuenta (perfil + seguridad + sesiones)

import { useState, useEffect } from "react";
import {
  useEditarPerfil,
  useCambioContrasena,
  useSesiones,
  useAutenticacion
} from "imansi-auth-react";

export function MiCuenta() {
  const { usuario, cerrarSesion } = useAutenticacion();
  const { editarPerfil } = useEditarPerfil();
  const { cambiarContrasena } = useCambioContrasena();
  const { listarSesiones, revocarSesion } = useSesiones();

  const [nombre, setNombre] = useState(usuario?.nombre || "");
  const [sesiones, setSesiones] = useState([]);

  useEffect(() => {
    listarSesiones().then(setSesiones);
  }, []);

  async function guardarPerfil(evento) {
    evento.preventDefault();
    await editarPerfil({ nombre });
    alert("Perfil actualizado");
  }

  async function revocar(sesionId) {
    await revocarSesion(sesionId);
    setSesiones(sesiones.filter((s) => s.id !== sesionId));
  }

  return (
    <div>
      <h1>Mi cuenta</h1>

      {/* Perfil */}
      <section>
        <h2>Perfil</h2>
        <form onSubmit={guardarPerfil}>
          <input
            value={nombre}
            onChange={(e) => setNombre(e.target.value)}
          />
          <button>Guardar</button>
        </form>
      </section>

      {/* Cambiar contraseña (solo si tiene) */}
      {usuario?.tieneContrasena && <MiCambiarContrasena />}

      {/* Sesiones activas */}
      <section>
        <h2>Sesiones activas</h2>
        {sesiones.map((s) => (
          <div key={s.id}>
            <span>{s.dispositivo || "Dispositivo desconocido"}</span>
            {s.actual && <span> (esta sesión)</span>}
            {!s.actual && (
              <button onClick={() => revocar(s.id)}>Revocar</button>
            )}
          </div>
        ))}
      </section>

      <button onClick={cerrarSesion}>Cerrar sesión</button>
    </div>
  );
}

function MiCambiarContrasena() {
  const { cambiarContrasena, error } = useCambioContrasena();
  const [actual, setActual] = useState("");
  const [nueva, setNueva] = useState("");

  async function manejar(evento) {
    evento.preventDefault();
    const resultado = await cambiarContrasena({
      contrasenaActual: actual,
      contrasenaNueva: nueva
    });
    if (resultado.ok) alert("Contraseña cambiada");
  }

  return (
    <section>
      <h2>Cambiar contraseña</h2>
      <form onSubmit={manejar}>
        <input
          type="password"
          placeholder="Contraseña actual"
          value={actual}
          onChange={(e) => setActual(e.target.value)}
        />
        <input
          type="password"
          placeholder="Nueva contraseña"
          value={nueva}
          onChange={(e) => setNueva(e.target.value)}
        />
        {error && <p style={{ color: "red" }}>{error.mensaje}</p>}
        <button>Cambiar</button>
      </form>
    </section>
  );
}

⚙️ Configuración

Todo se pasa al <ProveedorAutenticacion> mediante la prop configuracion:

<ProveedorAutenticacion configuracion={{
  urlBase: "http://localhost:3000",
  // ...
}}>

Propiedades

| Propiedad | Por defecto | Descripción | |---|---|---| | urlBase | "" | URL del backend | | endpoints | {...} | Rutas de los endpoints (raramente se cambian) | | campos | {...} | Nombres de los campos enviados al backend | | modoAlmacenamiento | "httpOnly" | "httpOnly", "cookie", "local", "sesion", "memoria" | | incluirCredenciales | true | Enviar cookies en cada petición | | rutaLogin | "/login" | Ruta del login para ProtegerRuta | | rutaTrasLogin | "/" | Ruta tras login exitoso | | rutaTrasRegistro | "/" | Ruta tras registro exitoso | | rutaTrasRestablecer | "/login" | Ruta tras restablecer contraseña | | oauth.proveedores | [] | Qué proveedores mostrar: ["github", "google"] | | oauth.textos | {...} | Textos de los botones OAuth |

Ejemplo completo

const configuracion = {
  urlBase: import.meta.env.VITE_URL_API_AUTENTICACION,
  modoAlmacenamiento: "httpOnly",
  incluirCredenciales: true,
  rutaLogin: "/login",
  rutaTrasLogin: "/panel",
  rutaTrasRegistro: "/panel",
  rutaTrasRestablecer: "/login",

  oauth: {
    proveedores: ["github", "google"],
    textos: {
      github: "Entrar con GitHub",
      google: "Entrar con Google",
      o: "o también"
    }
  }
};

🔌 Endpoints esperados

imansi-auth-react se comunica con el backend a través de estos endpoints. Todos son manejados por imansi-auth-node por defecto.

| Método | Endpoint | Propósito | |---|---|---| | POST | /auth/registro | Crear usuario | | POST | /auth/login | Iniciar sesión | | POST | /auth/logout | Cerrar sesión | | POST | /auth/refrescar | Renovar access token | | GET | /auth/usuario | Obtener usuario actual | | POST | /auth/verificar-2fa | Verificar código 2FA | | POST | /auth/reenviar-2fa | Reenviar código 2FA | | GET | /auth/sesiones | Listar sesiones | | DELETE | /auth/sesiones/:id | Revocar sesión | | POST | /auth/solicitar-recuperacion | Pedir link de recuperación | | POST | /auth/restablecer-contrasena | Restablecer contraseña | | POST | /auth/cambiar-contrasena | Cambiar contraseña (logueado) | | PATCH | /auth/perfil | Editar perfil | | GET | /auth/oauth/github | Iniciar OAuth GitHub | | GET | /auth/oauth/google | Iniciar OAuth Google |

Si usás otro backend, tiene que respetar estos endpoints y los formatos de respuesta.


🔒 Seguridad

La librería está diseñada para máxima seguridad:

  • Cookies httpOnly: el token nunca es accesible desde JavaScript.
  • Refresh tokens automáticos: el access token dura poco y se renueva solo.
  • Detección automática de 401: si el access token expira, el hook lo renueva sin que el usuario note nada.
  • Deduplicación de refrescos concurrentes: si varias peticiones reciben 401 al mismo tiempo, se hace un solo refresh.
  • Sin almacenamiento inseguro por defecto: no usamos localStorage a menos que lo pidas explícitamente.
  • OAuth sin tokens en la URL: el backend setea cookies httpOnly y redirige al frontend.

🌐 CORS

Si el frontend y el backend están en dominios distintos, el backend debe permitir CORS con credenciales:

Access-Control-Allow-Origin: https://tu-frontend.com
Access-Control-Allow-Credentials: true

⚠️ No uses Access-Control-Allow-Origin: * con credenciales. Los navegadores lo rechazan.

imansi-auth-node ya maneja esto automáticamente si configuras URL_FRONTEND correctamente.


🌍 Multi-idioma

Los textos de la UI los manejás vos. La librería no impone ningún idioma.

Los correos que envía el backend (imansi-auth-node) soportan es, en y pt. Se configura en el correos.json del backend.


🧬 Ecosistema Imansi

Todos comparten el mismo contrato HTTP y los mismos nombres en español.


📚 API pública

Contexto

  • ProveedorAutenticacion — Envuelve tu app.
  • ContextoAutenticacion — Contexto puro (avanzado).

Hooks

  • useAutenticacion — Todo en uno.
  • useLogin — Login.
  • useRegistro — Registro.
  • useVerificar2FA — Verificación 2FA.
  • useRecuperacion — Recuperar y restablecer contraseña.
  • useCambioContrasena — Cambiar contraseña.
  • useEditarPerfil — Editar nombre.
  • useSesiones — Sesiones activas.
  • useOAuth — OAuth.

Protección de rutas

  • ProtegerRuta — Bloquea rutas privadas.
  • RedirigirSiAutenticado — Redirige rutas públicas.

Servicios (uso avanzado sin hooks)

  • iniciarSesion, registrarUsuario, obtenerUsuario
  • cerrarSesion, refrescarSesion
  • verificarDosFactores, reenviarDosFactores
  • listarSesiones, revocarSesion
  • solicitarRecuperacion, restablecerContrasena
  • cambiarContrasena, editarPerfil

Utilidades

  • guardarSesion, obtenerSesion, eliminarSesion
  • configuracionPorDefecto, crearConfiguracion
  • normalizarError

🛠️ Desarrollo

Clonar y probar localmente

git clone https://github.com/imansi-pro/imansi-auth-react.git
cd imansi-auth-react
npm install
npm run dev

Scripts disponibles

| Script | Qué hace | |---|---| | npm run dev | Arranca el demo con Vite | | npm run build | Compila la librería a dist/ | | npm run preview | Previsualiza el build | | npm run lint | Corre oxlint |

Estructura del proyecto

imansi-auth-react/
├── src/
│   ├── demo/                    ← App de ejemplo (no se publica)
│   └── libreria/                ← Código del paquete
│       ├── almacenamiento/
│       ├── componentes/         ← (uso interno, no se exportan)
│       ├── configuracion/
│       ├── contexto/
│       ├── hooks/
│       │   ├── useAutenticacion.js
│       │   ├── useLogin.js
│       │   ├── useRegistro.js
│       │   ├── useVerificar2FA.js
│       │   ├── useRecuperacion.js
│       │   ├── useCambioContrasena.js
│       │   ├── useEditarPerfil.js
│       │   ├── useSesiones.js
│       │   └── useOAuth.js
│       ├── rutas/
│       ├── servicios/
│       ├── utilidades/
│       └── index.js
├── LICENSE
├── package.json
├── README.md
└── vite.config.js

📄 Licencia

MIT © 2026 Ivan Mansilla


💬 Soporte