JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 39
  • Score
    100M100P100Q78088F
  • License MIT

JWT token validation package with offline JWKS validation and Redis-based token revocation support

Package Exports

    This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@theoptimalpartner/jwt-auth-validator) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

    Readme

    JWT Auth Validator

    Un package de Node.js para validación offline de tokens JWT con soporte para JWKS y lista negra de tokens en Redis. Diseñado especialmente para tokens de AWS Cognito.

    Características

    • Validación offline de JWT con verificación de firma usando JWKS
    • Cache inteligente de claves públicas para mejor rendimiento
    • Lista negra de tokens usando Redis para revocación inmediata
    • Soporte completo para AWS Cognito incluyendo client secret
    • Cognito Client Secret para configuraciones seguras
    • Enriquecimiento de datos de usuario con información contextual desde Redis
    • Integración completa con auth-service (permisos, organizaciones, aplicaciones)
    • TypeScript con tipado completo
    • Validación flexible (modo desarrollo vs producción)
    • Compatible con Node.js 18+ (última versión LTS)

    Instalación

    npm install @theoptimalpartner/jwt-auth-validator ioredis

    Nota: ioredis es requerido ya que el paquete siempre verifica la lista negra de tokens revocados para máxima seguridad.

    Uso Rápido

    Configuración básica para AWS Cognito

    import { createCognitoValidator } from "@theoptimalpartner/jwt-auth-validator";
    
    // Configuración usando variables de entorno (recomendado)
    const validator = createCognitoValidator(
      "us-east-1", // AWS region
      "us-east-1_XXXXXXXXX", // User Pool ID
      "your-client-id", // Client ID (opcional)
      "your-client-secret" // Client Secret (opcional, para mayor seguridad)
      // Redis se configura automáticamente con variables de entorno
    );
    
    // O configuración manual con Redis
    const validator = createCognitoValidator(
      "us-east-1",
      "us-east-1_XXXXXXXXX",
      "your-client-id",
      "your-client-secret", // Client Secret opcional
      {
        host: "your-redis-host.com",
        port: 6379,
        password: "your-password",
        tls: true,
        caCertPath: "/path/to/certs",
        caCertName: "redis-ca.crt"
      }
    );
    
    // Validar un token
    const authHeader = "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...";
    const token = validator.extractTokenFromHeader(authHeader);
    
    if (token) {
      const result = await validator.validateToken(token);
    
      if (result.valid) {
        console.log("Token válido:", result.decoded);
        // result.decoded contiene el payload del token
      } else {
        console.log("Token inválido:", result.error);
      }
    }

    Configuración avanzada con Redis

    import { createCognitoValidator } from "@theoptimalpartner/jwt-auth-validator";
    
    // Configuración con Redis para lista negra de tokens
    const validator = createCognitoValidator(
      "us-east-1",
      "us-east-1_XXXXXXXXX",
      "your-client-id",
      {
        host: "your-redis-host.com",
        port: 6380,
        password: "your-redis-password",
        tls: true, // Para conexiones seguras
      }
    );
    
    // El validator ahora verificará automáticamente la lista negra
    const result = await validator.validateToken(token);

    Enriquecimiento de Datos de Usuario

    Nuevo en v1.4.0: Validación con Contexto de Usuario

    Este paquete ahora incluye funcionalidad para enriquecer tokens JWT con datos de usuario almacenados en Redis, siguiendo los mismos patrones de clave que el auth-service.

    Configuración de UserDataService

    import { createCognitoValidator } from "@theoptimalpartner/jwt-auth-validator";
    
    // Configuración con enriquecimiento de datos habilitado
    const validator = createCognitoValidator(
      "us-east-1",
      "us-east-1_XXXXXXXXX",
      "your-client-id",
      "your-client-secret",
      {
        host: "your-redis-host.com",
        port: 6379,
        password: "your-password",
        tls: true,
      },
      {
        enableUserDataRetrieval: true,
        includeApplications: true,
        includeOrganizations: true, 
        includeRoles: true,
        includeEffectivePermissions: false, // Opcional, para máximo rendimiento
        cacheTimeout: 300, // Cache por 5 minutos
      }
    );

    Validación Enriquecida

    // Usa validateTokenEnriched en lugar de validateToken
    const result = await validator.validateTokenEnriched(token);
    
    if (result.valid) {
      console.log("Usuario autenticado:", result.decoded?.sub);
      
      // Datos enriquecidos disponibles
      console.log("Permisos:", result.userPermissions);
      console.log("Organizaciones:", result.userOrganizations);
      console.log("Aplicaciones:", result.applications);
      
      // Ejemplo de autorización
      const hasPermission = result.userOrganizations?.some(org => 
        org.appId === 'my-app' && org.roles.includes('admin')
      );
      
      if (hasPermission) {
        // Usuario tiene permisos de administrador
      }
    } else {
      console.log("Token inválido:", result.error);
    }

    Acceso Directo a Datos de Usuario

    // Obtener permisos de un usuario específico
    const userPermissions = await validator.getUserPermissions("user-123");
    
    // Obtener organizaciones del usuario
    const userOrganizations = await validator.getUserOrganizations("user-123");
    
    // Obtener aplicaciones a las que tiene acceso
    const userApplications = await validator.getUserApplications("user-123");
    
    // Obtener datos completos del usuario
    const comprehensiveData = await validator.getComprehensiveUserData("user-123");

    Patrones de Clave Redis Compatibles

    El paquete es totalmente compatible con los patrones de clave de auth-service:

    • Permisos de usuario: user:permissions:{userId}
    • Aplicaciones: app:{appId}
    • Organizaciones: org:{appId}:{organizationId}
    • Roles de aplicación: app:roles:{appId}:{organizationId}
    • Esquemas de aplicación: app-schemas (clave global)
    • Permisos efectivos: permissions:cache:{userId}:{appId}:{orgId}

    Interfaces TypeScript para Datos de Usuario

    interface EnrichedValidationResult extends ValidationResult {
      userPermissions?: UserPermissions | null;
      userOrganizations?: UserOrganization[];
      applications?: Application[];
    }
    
    interface UserPermissions {
      userId: string;
      permissions: {
        [appId: string]: {
          [organizationId: string]: OrganizationPermissions;
        };
      };
      cacheVersion?: number;
    }
    
    interface UserOrganization {
      appId: string;
      organizationId: string;
      roles: string[];
      status: 'active' | 'suspended' | 'revoked';
      effectivePermissions?: string[];
    }
    
    interface Application {
      appId: string;
      name: string;
      description?: string;
      isActive: boolean;
      allowedDomains?: string[];
      redirectUrls?: string[];
      schema: AppSchema;
      createdAt: number;
      updatedAt: number;
      metadata?: Record<string, unknown>;
    }

    Gestión de Cache

    // Limpiar cache específico del usuario
    validator.clearUserCache("user-123");
    
    // Limpiar todo el cache
    validator.clearAllCache();
    
    // Obtener estadísticas del cache
    const stats = validator.getUserDataStats();
    console.log(`Cache hits: ${stats.cacheHits}, misses: ${stats.cacheMisses}`);

    Rendimiento y Fallback

    • Cache local con TTL configurable para reducir consultas Redis
    • Fallback graceful: Si Redis no está disponible, devuelve validación básica
    • Lazy loading: Solo carga datos cuando se solicita enriquecimiento
    • Optimización de memoria: Cache inteligente con limpieza automática

    API Completa

    JWTValidator

    Métodos principales

    // Validación principal (automática según ambiente)
    await validator.validateToken(token: string, forceSecure?: boolean): Promise<ValidationResult>
    
    // Validación con API Key opcional
    await validator.validateTokenWithApiKey(token: string, apiKey?: string, forceSecure?: boolean): Promise<ValidationResult>
    
    // NUEVO: Validación con enriquecimiento de datos de usuario
    await validator.validateTokenEnriched(token: string, apiKey?: string, forceSecure?: boolean): Promise<EnrichedValidationResult>
    
    // Validación específica para Access Tokens
    await validator.validateAccessToken(token: string): Promise<ValidationResult>
    
    // Validación específica para ID Tokens
    await validator.validateIdToken(token: string): Promise<ValidationResult>
    
    // Validación segura (siempre usa JWKS)
    await validator.validateTokenSecure(token: string): Promise<ValidationResult>
    
    // Validación básica (sin verificación JWKS)
    await validator.validateTokenBasic(token: string): Promise<ValidationResult>
    
    // Validación múltiple en lote
    await validator.validateMultipleTokens(tokens: string[]): Promise<ValidationResult[]>

    Utilidades

    // Extraer token del header Authorization
    validator.extractTokenFromHeader(authHeader: string): string | null
    
    // Extraer API key del header X-API-Key
    validator.extractApiKeyFromHeader(apiKeyHeader: string): string | null
    
    // Extraer API key de headers múltiples (X-API-Key, X-Api-Key, API-Key)
    validator.extractApiKeyFromHeaders(headers: Record<string, string>): string | null
    
    // Verificar si un token está expirado
    validator.isTokenExpired(token: string): boolean
    
    // Obtener tiempo restante hasta expiración (en segundos)
    validator.getTimeToExpiry(token: string): number
    
    // Decodificar token sin validar
    validator.decodeToken(token: string): DecodedToken | null
    
    // Obtener información completa del token
    validator.getTokenInfo(token: string): object | null

    NUEVO: Métodos de datos de usuario

    // Obtener permisos de usuario desde Redis
    await validator.getUserPermissions(userId: string): Promise<UserPermissions | null>
    
    // Obtener organizaciones del usuario
    await validator.getUserOrganizations(userId: string): Promise<UserOrganization[]>
    
    // Obtener aplicaciones accesibles por el usuario
    await validator.getUserApplications(userId: string): Promise<Application[]>
    
    // Obtener datos completos del usuario
    await validator.getComprehensiveUserData(userId: string): Promise<{
      permissions: UserPermissions | null;
      organizations: UserOrganization[];
      applications: Application[];
    }>
    
    // Gestión de cache de datos de usuario
    validator.clearUserCache(userId: string): void
    validator.clearAllCache(): void
    validator.getUserDataStats(): UserDataStats
    
    // Verificar si el enriquecimiento de datos está habilitado
    validator.isUserDataEnabled(): boolean

    Métodos de Client Secret (Cognito)

    // Verificar si hay client secret configurado
    validator.hasClientSecret(): boolean
    
    // Obtener client secret (si está configurado)
    validator.getClientSecret(): string | undefined
    
    // Calcular hash secreto para operaciones Cognito
    validator.calculateSecretHash(identifier: string): string

    Gestión de lista negra (requiere Redis)

    // Revocar un token específico
    await validator.revokeToken(token: string): Promise<void>
    
    // Revocar todos los tokens de un usuario
    await validator.revokeUserTokens(userId: string, tokens: string[]): Promise<void>

    Estadísticas y monitoreo

    // Estadísticas del cache JWKS
    validator.getCacheStats();
    
    // Estadísticas de la lista negra
    await validator.getBlacklistStats();
    
    // Cerrar conexiones
    await validator.disconnect();

    Configuración Manual

    Para casos de uso avanzados, puedes configurar manualmente:

    import { JWTValidator } from "@theoptimalpartner/jwt-auth-validator";
    
    const validator = new JWTValidator({
      jwks: {
        jwksUri:
          "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXXXXX/.well-known/jwks.json",
        issuer: "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXXXXX",
        audience: "your-client-id",
        clientSecret: "your-client-secret", // Opcional, para mayor seguridad
        cacheTimeout: 3600, // Cache de claves por 1 hora
      },
      enableRedisBlacklist: true,
      forceSecureValidation: true, // Siempre usar validación JWKS
      redis: {
        host: "your-redis-host.com",
        port: 6380,
        password: "your-password",
        tls: {
          rejectUnauthorized: true,
          checkServerIdentity: () => undefined,
          servername: "your-redis-host.com",
          minVersion: "TLSv1.2",
          maxVersion: "TLSv1.3",
        },
        family: 4,
        connectTimeout: 60000,
        commandTimeout: 30000,
        maxRetriesPerRequest: 3,
        reconnectOnError: (err) => {
          const reconnectErrors = ["READONLY", "ECONNRESET", "EPIPE"];
          return reconnectErrors.some((target) => err.message.includes(target));
        },
      },
    });

    Ejemplos de Uso

    Express.js Middleware

    import express from "express";
    import { createCognitoValidator } from "@theoptimalpartner/jwt-auth-validator";
    
    const app = express();
    const validator = createCognitoValidator("us-east-1", "us-east-1_XXXXXXXXX");
    
    // Middleware de autenticación
    const authMiddleware = async (req: any, res: any, next: any) => {
      try {
        const authHeader = req.headers.authorization;
        const token = validator.extractTokenFromHeader(authHeader);
    
        if (!token) {
          return res.status(401).json({ error: "Token missing" });
        }
    
        const result = await validator.validateAccessToken(token);
    
        if (!result.valid) {
          return res.status(401).json({ error: result.error });
        }
    
        req.user = result.decoded;
        next();
      } catch (error) {
        res.status(500).json({ error: "Authentication error" });
      }
    };
    
    app.use("/api/protected", authMiddleware);
    
    app.get("/api/protected/profile", (req: any, res: any) => {
      res.json({ user: req.user });
    });

    Nest.js Guard

    import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
    import { createCognitoValidator } from "@theoptimalpartner/jwt-auth-validator";
    
    @Injectable()
    export class JwtAuthGuard implements CanActivate {
      private validator = createCognitoValidator(
        process.env.AWS_REGION!,
        process.env.COGNITO_USER_POOL_ID!,
        process.env.COGNITO_CLIENT_ID
      );
    
      async canActivate(context: ExecutionContext): Promise<boolean> {
        const request = context.switchToHttp().getRequest();
        const authHeader = request.headers.authorization;
    
        const token = this.validator.extractTokenFromHeader(authHeader);
        if (!token) return false;
    
        const result = await this.validator.validateToken(token);
    
        if (result.valid) {
          request.user = result.decoded;
          return true;
        }
    
        return false;
      }
    }

    NUEVO: Express.js Middleware con Enriquecimiento de Datos

    import express from "express";
    import { createCognitoValidator } from "@theoptimalpartner/jwt-auth-validator";
    
    const app = express();
    
    // Validator con configuración de datos de usuario
    const validator = createCognitoValidator(
      process.env.AWS_REGION!,
      process.env.COGNITO_USER_POOL_ID!,
      process.env.COGNITO_CLIENT_ID,
      process.env.COGNITO_CLIENT_SECRET,
      {
        host: process.env.REDIS_HOST!,
        password: process.env.REDIS_PASSWORD,
        tls: process.env.REDIS_TLS === 'true',
      },
      {
        enableUserDataRetrieval: true,
        includeApplications: true,
        includeOrganizations: true,
        includeRoles: true,
        cacheTimeout: 300,
      }
    );
    
    // Middleware de autenticación con datos de usuario
    const enrichedAuthMiddleware = async (req: any, res: any, next: any) => {
      try {
        const authHeader = req.headers.authorization;
        const token = validator.extractTokenFromHeader(authHeader);
    
        if (!token) {
          return res.status(401).json({ error: "Token missing" });
        }
    
        // Usa validateTokenEnriched para obtener datos de usuario
        const result = await validator.validateTokenEnriched(token);
    
        if (!result.valid) {
          return res.status(401).json({ error: result.error });
        }
    
        // Contexto enriquecido disponible
        req.user = result.decoded;
        req.userPermissions = result.userPermissions;
        req.userOrganizations = result.userOrganizations;
        req.userApplications = result.applications;
    
        next();
      } catch (error) {
        res.status(500).json({ error: "Authentication error" });
      }
    };
    
    // Middleware de autorización por rol
    const requireRole = (appId: string, role: string) => {
      return (req: any, res: any, next: any) => {
        const hasRole = req.userOrganizations?.some((org: any) => 
          org.appId === appId && org.roles.includes(role)
        );
    
        if (!hasRole) {
          return res.status(403).json({ error: "Insufficient permissions" });
        }
    
        next();
      };
    };
    
    // Uso del middleware
    app.use("/api/protected", enrichedAuthMiddleware);
    app.use("/api/admin", requireRole("my-app", "admin"));
    
    app.get("/api/protected/profile", (req: any, res: any) => {
      res.json({
        user: req.user,
        organizations: req.userOrganizations,
        applications: req.userApplications,
      });
    });
    
    app.get("/api/admin/dashboard", (req: any, res: any) => {
      res.json({ message: "Welcome admin!", user: req.user });
    });

    Lambda Authorizer

    import { createCognitoValidator } from "@theoptimalpartner/jwt-auth-validator";
    
    const validator = createCognitoValidator(
      process.env.AWS_REGION!,
      process.env.COGNITO_USER_POOL_ID!
    );
    
    export const handler = async (event: any) => {
      try {
        const token = validator.extractTokenFromHeader(event.authorizationToken);
    
        if (!token) {
          throw new Error("Unauthorized");
        }
    
        const result = await validator.validateToken(token);
    
        if (!result.valid) {
          throw new Error("Unauthorized");
        }
    
        return {
          principalId: result.decoded!.sub,
          policyDocument: {
            Version: "2012-10-17",
            Statement: [
              {
                Action: "execute-api:Invoke",
                Effect: "Allow",
                Resource: event.methodArn,
              },
            ],
          },
          context: {
            userId: result.decoded!.sub,
            email: result.decoded!.email,
          },
        };
      } catch (error) {
        throw new Error("Unauthorized");
      }
    };

    Validación con API Keys

    import express from "express";
    import { JWTValidator } from "@theoptimalpartner/jwt-auth-validator";
    
    // Validator con validación de API Keys habilitada
    const validator = new JWTValidator({
      jwks: {
        jwksUri: "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXXXXX/.well-known/jwks.json",
        issuer: "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_XXXXXXXXX",
        audience: "your-client-id",
      },
      redis: {
        host: process.env.REDIS_HOST!,
        password: process.env.REDIS_PASSWORD,
        tls: process.env.REDIS_TLS === 'true',
      },
      enableApiKeyValidation: true, // Habilitar validación de API Keys
      enableRedisBlacklist: true,
    });
    
    const app = express();
    
    // Middleware que valida JWT con API Key opcional
    const authWithApiKeyMiddleware = async (req: any, res: any, next: any) => {
      try {
        const authHeader = req.headers.authorization;
        const token = validator.extractTokenFromHeader(authHeader);
        
        if (!token) {
          return res.status(401).json({ error: "Token missing" });
        }
    
        // Extraer API key de headers
        const apiKey = validator.extractApiKeyFromHeaders(req.headers);
        
        // Validar token con API key opcional
        const result = await validator.validateTokenWithApiKey(token, apiKey);
        
        if (!result.valid) {
          return res.status(401).json({ error: result.error });
        }
    
        req.user = result.decoded;
        req.apiKey = result.apiKey; // Información del API key si se usó
        
        next();
      } catch (error) {
        res.status(500).json({ error: "Authentication error" });
      }
    };
    
    app.use("/api/protected", authWithApiKeyMiddleware);
    
    app.get("/api/protected/data", (req: any, res: any) => {
      res.json({
        user: req.user,
        apiKeyUsed: !!req.apiKey,
        apiKeyInfo: req.apiKey ? {
          name: req.apiKey.name,
          scope: req.apiKey.scope,
          permissions: req.apiKey.permissions
        } : null
      });
    });

    Variables de Entorno

    # Configuración básica
    AWS_REGION=us-east-1
    COGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
    COGNITO_CLIENT_ID=your-client-id
    COGNITO_CLIENT_SECRET=your-client-secret  # Opcional, para configuraciones seguras
    
    # Configuración Redis (requerido)
    REDIS_HOST=localhost
    REDIS_PORT=6379
    REDIS_PASSWORD=''
    REDIS_TLS=false
    REDIS_CA_CERT_PATH=/path/to/certs
    REDIS_CA_CERT_NAME=redis-ca.crt
    
    # Forzar validación segura
    NODE_ENV=production  # Automáticamente usa validación JWKS en producción

    AWS Cognito Client Secret

    ¿Qué es el Client Secret?

    El Client Secret es una configuración adicional de seguridad en AWS Cognito que requiere que todas las operaciones incluyan un hash calculado usando HMAC-SHA256. Esto añade una capa extra de seguridad a tu aplicación.

    Cuándo usar Client Secret

    • Aplicaciones del lado del servidor: Donde puedes mantener el secret seguro
    • Microservicios: Para validación entre servicios
    • Entornos altamente seguros: Donde se requiere autenticación adicional

    Configuración con Client Secret

    import { createCognitoValidator } from "@theoptimalpartner/jwt-auth-validator";
    
    // Método 1: Parámetro directo
    const validator = createCognitoValidator(
      "us-east-1",
      "us-east-1_XXXXXXXXX",
      "your-client-id",
      "your-client-secret"
    );
    
    // Método 2: Variable de entorno (recomendado)
    process.env.COGNITO_CLIENT_SECRET = "your-client-secret";
    const validator = createCognitoValidator(
      "us-east-1",
      "us-east-1_XXXXXXXXX", 
      "your-client-id"
    );
    
    // Verificar si el client secret está configurado
    console.log("Client secret configurado:", validator.hasClientSecret());
    
    // Calcular secret hash para operaciones de Cognito
    const secretHash = validator.calculateSecretHash("user@example.com");
    console.log("Secret hash:", secretHash);

    Funciones utilitarias para Client Secret

    import { 
      calculateSecretHash, 
      hasClientSecret, 
      safeCalculateSecretHash 
    } from "@theoptimalpartner/jwt-auth-validator";
    
    // Calcular hash manualmente
    const hash = calculateSecretHash({
      identifier: "user@example.com",
      clientId: "your-client-id", 
      clientSecret: "your-client-secret"
    });
    
    // Verificar si un secret está disponible
    const hasSecret = hasClientSecret("your-client-secret");
    
    // Calcular hash de forma segura (maneja errores)
    const safeHash = safeCalculateSecretHash(
      "user@example.com",
      "your-client-id",
      "your-client-secret"
    );

    Notas importantes sobre Client Secret

    • Seguridad: Nunca expongas el client secret en el frontend
    • Compatibilidad: Solo usar cuando tu configuración de Cognito lo requiera
    • Opcional: La librería funciona perfectamente sin client secret
    • Consistencia: Los hashes generados son compatibles con AWS SDK

    Tipos TypeScript

    interface DecodedToken {
      sub: string;
      email?: string;
      email_verified?: boolean;
      phone_number?: string;
      phone_number_verified?: boolean;
      aud: string;
      iss: string;
      exp: number;
      iat: number;
      token_use: "access" | "id";
      scope?: string;
      auth_time?: number;
      jti?: string;
      username?: string;
      "cognito:username"?: string;
      "cognito:groups"?: string[];
      [key: string]: unknown;
    }
    
    interface ValidationResult {
      valid: boolean;
      decoded?: DecodedToken;
      error?: string;
    }
    
    // NUEVO: Utilidades de manejo de errores
    interface ErrorDetails {
      message: string;
      code?: string;
      context?: string;
    }
    
    // Funciones de utilidad exportadas
    function extractErrorDetails(error: unknown, context?: string): ErrorDetails
    function getUserFriendlyErrorMessage(error: unknown): string
    function logError(error: unknown, context?: string): void
    
    // Constantes de mensajes de error
    const JWT_ERROR_MESSAGES = {
      TOKEN_EXPIRED: 'Token has expired',
      INVALID_TOKEN: 'Invalid token format',
      TOKEN_NOT_ACTIVE: 'Token not active yet',
      INVALID_SIGNATURE: 'Invalid token signature',
      // ... más constantes disponibles
    } as const

    Rendimiento

    Cache JWKS

    • Las claves públicas se cachean por 1 hora por defecto
    • Reduce significativamente las consultas a los endpoints JWKS
    • Cache configurable por necesidades específicas

    Validación offline

    • No requiere llamadas al servicio de autenticación para validar tokens
    • Validación local usando claves públicas cacheadas
    • Ideal para microservicios y alta concurrencia

    Lista negra eficiente

    • Usa Redis para almacenamiento distribuido de tokens revocados
    • TTL automático basado en la expiración del token
    • Optimización de memoria usando hashes de tokens

    Manejo de errores mejorado

    • Mensajes de error amigables y descriptivos
    • Logging limpio sin información técnica excesiva
    • Traducción inteligente de errores JWT a mensajes claros
    • Códigos de error estructurados para manejo programático

    Seguridad

    Validación robusta

    • Verificación de firma usando claves públicas
    • Validación de claims estándar (exp, iss, aud)
    • Verificación específica de tokens Cognito

    Modo de desarrollo

    • Validación básica sin verificación de firma para desarrollo
    • Advertencias claras cuando no se usa validación segura
    • Automáticamente usa validación segura en producción

    Licencia

    MIT

    Contribuciones

    Las contribuciones son bienvenidas. Por favor:

    1. Fork el repositorio
    2. Crea una rama para tu feature (git checkout -b feature/nueva-funcionalidad)
    3. Commit tus cambios (git commit -am 'Agregar nueva funcionalidad')
    4. Push a la rama (git push origin feature/nueva-funcionalidad)
    5. Crea un Pull Request

    Soporte

    Para reportar bugs o solicitar features, por favor crea un issue en el repositorio.