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

@boolean-packages/boolean-auth-sdk

v0.1.0

Published

Isomorphic SDK to consume cidi-auth and auth.back from Node (NestJS) and browser (React)

Readme

@boolean/auth-sdk (TypeScript) — Isomórfico para frontend y backend

SDK común para conectar aplicaciones frontend (React/Vite) y backend (NestJS/Express) con los proxies de autenticación cidi-auth y auth.back, que firman JWT a través de tokens.back.

Browser / Node ──▶ cidi-auth / auth.back ──▶ tokens.back (.well-known/jwks.json)
  • @boolean/auth-sdk: cliente HTTP + verificador de JWT con jose.
  • @boolean/auth-sdk/react: <AuthProvider> + useAuth() para gestionar sesión en React.
  • @boolean/auth-sdk/nestjs: AuthModule, AuthGuard, @CurrentUser() para NestJS.

Instalación

npm install @boolean/auth-sdk jose

# Integraciones opcionales
npm install react                               # sólo si usás @boolean/auth-sdk/react
npm install @nestjs/common @nestjs/core rxjs     # sólo si usás @boolean/auth-sdk/nestjs
npm install reflect-metadata                     # NestJS decoradores

Build, tests y typecheck

cd sdks/typescript
npm install
npm run typecheck
npm test
npm run build   # genera ESM + CJS + .d.ts en dist/

Frontend React (Vite)

appId es opcional. Sólo es obligatorio para el flow de loginWithCidi (cidi-auth necesita app_id en el payload). Si tu app habla con auth.back (login con usuario/contraseña, refresh, me, findByCuil, etc.) no necesitás declararlo.

A. App que se autentica vía CIDI (ejemplo juzgado.front)

  1. Variables de entorno (.env):

    VITE_CIDI_API_URL=https://cidi-auth-api.boolean.com.ar/v1
    VITE_CIDI_APP_ID=42
    VITE_CIDI_LOGIN_URL=https://cidi-login.boolean.com.ar
  2. Configurar el cliente y el provider (src/main.tsx):

    import React from "react";
    import ReactDOM from "react-dom/client";
    import App from "./App";
    import { AuthClient, LocalStorageTokenStorage } from "@boolean/auth-sdk";
    import { AuthProvider } from "@boolean/auth-sdk/react";
    
    const authClient = new AuthClient({
      baseUrl: import.meta.env.VITE_CIDI_API_URL,
      appId: Number(import.meta.env.VITE_CIDI_APP_ID), // requerido para loginWithCidi
    });
    
    ReactDOM.createRoot(document.getElementById("root")!).render(
      <AuthProvider client={authClient} storage={new LocalStorageTokenStorage()}>
        <App />
      </AuthProvider>
    );
  3. Callback de CIDI (src/pages/CidiCallback.tsx):

    import { useEffect } from "react";
    import { useAuth } from "@boolean/auth-sdk/react";
    import { useNavigate } from "react-router-dom";
    
    export function CidiCallback() {
      const navigate = useNavigate();
      const { loginWithCidi, isLoading, user } = useAuth();
    
      useEffect(() => {
        const params = new URLSearchParams(window.location.search);
        const cookie = params.get("hashcookie");
        if (cookie) {
          loginWithCidi(cookie).then(() => navigate("/"));
        }
      }, [loginWithCidi, navigate]);
    
      if (isLoading) return <p>Iniciando sesión…</p>;
      if (user) return <p>Sesión iniciada como {user.email}</p>;
      return <p>No se recibió cookie de CIDI</p>;
    }
  4. Login local (desarrollo):

    const { loginWithCredentials } = useAuth();
    await loginWithCredentials(username, password);
  5. Hook useAuth() expone user, isAuthenticated, isLoading, loginWithCidi, loginWithCredentials, refresh, logout, refreshUser, getAccessToken.

  6. Interceptor Axios con auto-refresh:

    import axios from "axios";
    import { AuthClient, MemoryTokenStorage } from "@boolean/auth-sdk";
    
    const api = axios.create({ baseURL: "/api" });
    const authClient = new AuthClient({ baseUrl: import.meta.env.VITE_CIDI_API_URL });
    const storage = new MemoryTokenStorage();
    
    api.interceptors.request.use(async (config) => {
      const token = await storage.getAccessToken();
      if (token) config.headers.Authorization = `Token ${token}`;
      return config;
    });
    
    api.interceptors.response.use(
      (r) => r,
      async (error) => {
        if (error.response?.status === 401) {
          const refreshToken = await storage.getRefreshToken();
          if (refreshToken) {
            const tokens = await authClient.refresh(refreshToken);
            await storage.setTokens({ accessToken: tokens.accessToken, refreshToken: tokens.refreshToken });
            error.config.headers.Authorization = `Token ${tokens.accessToken}`;
            return api.request(error.config);
          }
        }
        return Promise.reject(error);
      }
    );
  7. Ruta protegida:

    function ProtectedRoute({ children }: { children: JSX.Element }) {
      const { isAuthenticated, isLoading } = useAuth();
      if (isLoading) return <p>Verificando sesión…</p>;
      if (!isAuthenticated) return <Navigate to="/login" replace />;
      return children;
    }

B. App que se autentica sólo contra auth.back (sin CIDI)

Si tu frontend usa únicamente los endpoints de auth.back (login con usuario/contraseña, refresh, perfil), no pases appId:

  1. Variables de entorno (.env):

    VITE_AUTH_API_URL=https://auth-api.boolean.com.ar
  2. Configurar el cliente y el provider (src/main.tsx):

    import React from "react";
    import ReactDOM from "react-dom/client";
    import App from "./App";
    import { AuthClient, LocalStorageTokenStorage } from "@boolean/auth-sdk";
    import { AuthProvider } from "@boolean/auth-sdk/react";
    
    // Sin appId: loginWithCredentials, refresh, me, logout, findByCuil funcionan igual.
    const authClient = new AuthClient({
      baseUrl: import.meta.env.VITE_AUTH_API_URL,
    });
    
    ReactDOM.createRoot(document.getElementById("root")!).render(
      <AuthProvider client={authClient} storage={new LocalStorageTokenStorage()}>
        <App />
      </AuthProvider>
    );
  3. Pantalla de login (src/pages/Login.tsx):

    import { useAuth } from "@boolean/auth-sdk/react";
    
    export function Login() {
      const { loginWithCredentials, isLoading, user } = useAuth();
    
      async function onSubmit(e: React.FormEvent<HTMLFormElement>) {
        e.preventDefault();
        const form = new FormData(e.currentTarget);
        await loginWithCredentials(
          String(form.get("username")),
          String(form.get("password")),
        );
      }
    
      if (user) return <p>Sesión activa: {user.email}</p>;
      return (
        <form onSubmit={onSubmit}>
          <input name="username" placeholder="Usuario" />
          <input name="password" type="password" placeholder="Contraseña" />
          <button disabled={isLoading}>Ingresar</button>
        </form>
      );
    }

    Si más adelante esta misma app necesita también loginWithCidi, podés pasar el appId en la llamada puntual: loginWithCidi(cookie, 42). No hace falta reconfigurar el cliente.

Backend NestJS

  1. Módulo global:

    import { Module } from "@nestjs/common";
    import { APP_GUARD, Reflector } from "@nestjs/core";
    import {
      AuthModule,
      AuthGuard,
      AUTH_SDK_VERIFIER,
      AuthModuleOptions,
      IS_PUBLIC_KEY,
    } from "@boolean/auth-sdk/nestjs";
    
    class PublicAwareGuard extends AuthGuard {
      constructor(reflector: Reflector, ...args: ConstructorParameters<typeof AuthGuard>) {
        super(...args);
        this.reflector = reflector;
      }
    
      private reflector: Reflector;
    
      async canActivate(ctx: any) {
          const isPublic = this.reflector.getAllAndOverride<boolean>(IS_PUBLIC_KEY, [
            ctx.getHandler(),
            ctx.getClass(),
          ]);
          if (isPublic) return true;
          return super.canActivate(ctx);
      }
    }
    
    @Module({
      imports: [
        AuthModule.forRoot({
          jwksUrl: process.env.JWKS_URL!,
          algorithms: ["RS256"],
          guard: { headerPrefixes: ["token", "bearer"], requiredTokenType: "access" },
        } satisfies AuthModuleOptions),
      ],
      providers: [
        { provide: APP_GUARD, useClass: PublicAwareGuard },
      ],
    })
    export class AppModule {}
  2. Controller + decorator:

    import { Controller, Get } from "@nestjs/common";
    import { CurrentUser, Public, AuthRequestUser } from "@boolean/auth-sdk/nestjs";
    
    @Controller("cases")
    export class CasesController {
      @Get()
      list(@CurrentUser() user: AuthRequestUser) {
        return { userId: user.userId, roles: user.roles };
      }
    
      @Public()
      @Get("public")
      publicEndpoint() {
        return { ok: true };
      }
    }
  3. Consumir cidi-auth desde NestJS:

    import { Injectable } from "@nestjs/common";
    import { AuthClient } from "@boolean/auth-sdk";
    
    @Injectable()
    export class AuthService {
      private readonly client = new AuthClient({ baseUrl: process.env.CIDI_AUTH_BASE_URL!, appId: Number(process.env.CIDI_APP_ID) });
    
      findByCuil(cuil: string, token: string) {
        return this.client.findByCuil(cuil, token);
      }
    }

Backend Express / Fastify

import express from "express";
import { JWTVerifier } from "@boolean/auth-sdk";

const verifier = new JWTVerifier({ jwksUrl: process.env.JWKS_URL! });
const app = express();

app.use(async (req, res, next) => {
  const header = req.headers.authorization;
  if (!header) return res.status(401).send("Missing token");

  const [scheme, token] = header.split(" ");
  if (!token || !/^token|bearer$/i.test(scheme)) return res.status(401).send("Invalid header");

  try {
    req.user = await verifier.verify(token, { expectedTokenType: "access" });
    next();
  } catch (err) {
    return res.status(401).send((err as Error).message);
  }
});

Uso básico (browser/Node sin frameworks)

import { AuthClient, JWTVerifier, MemoryTokenStorage } from "@boolean/auth-sdk";

const client = new AuthClient({ baseUrl: "https://auth.example.com/v1" });
const storage = new MemoryTokenStorage();

const login = async () => {
  const tokens = await client.loginWithCredentials("alice", "secret");
  await storage.setTokens({ accessToken: tokens.accessToken, refreshToken: tokens.refreshToken });
};

const verifier = new JWTVerifier({ jwksUrl: "https://tokens.example.com/.well-known/jwks.json" });
const payload = await verifier.verify(await storage.getAccessToken()!);
console.log(payload.user_id);

Manejo de errores

Todas las excepciones extienden AuthSDKError:

| Error | Contexto | |--------------------------|----------------------------------------------| | AuthenticationError | 401/403 del servidor de auth | | HTTPError | Otra respuesta HTTP no exitosa | | InvalidTokenError | Firma/claims inválidos | | TokenExpiredError | JWT expirado (subclase de InvalidTokenError)|

Capturalas según la capa (frontend → mostrar login; backend → Unauthorized).

Opciones principales

AuthClient:

  • baseUrl (string) — obligatorio.
  • appId (number | string, opcional) — sólo necesario si vas a usar loginWithCidi. Se toma como default cuando no lo pasás explícitamente. Para apps que sólo hablan con auth.back no lo declares.
  • timeoutMs (number) — default 10 000 ms.
  • tokenHeaderScheme — "Token" por defecto (usar "Bearer" si el backend lo exige).
  • fetch — inyectá un fetch custom (ej. node-fetch).
  • defaultHeaders — headers adicionales.

JWTVerifier:

  • jwksUrl — URL al JWKS de tokens.back.
  • localSecret — PEM (SPKI público o PKCS#8 privado) como fallback.
  • algorithms, audience, issuer, jwksHeaders — validaciones adicionales.

Exports del paquete

@boolean/auth-sdk          → client, verifier, token storage, errores, tipos
@boolean/auth-sdk/react    → AuthProvider, useAuth
@boolean/auth-sdk/nestjs   → AuthModule, AuthGuard, decorators, tipos

Notas de despliegue

  • El verificador usa fetch; Node 18+ lo trae nativo. En Node <18, pasá un fetch custom.
  • El JWKS se cachea siguiendo jose (createRemoteJWKSet). Headers se setean con User-Agent tipo Chrome para evitar bloqueos.
  • Para SSR/Next.js, podés usar MemoryTokenStorage en el servidor y reenviar tokens al cliente via cookies.