┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ apidoc.json │───▶│ AuthProcessor │───▶│ Encrypted HTML │
│ (Config) │ │ (Build-time) │ │ + Bundle │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ User Login │◀───│ AuthManager │◀───│ Browser │
│ (Session) │ │ (Runtime) │ │ (Client-side) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
AuthProcessor
procesa configuración y encripta contenidoAuthManager
maneja autenticación y desencripta contenidolocalStorage
con tokens JWT-likeUbicación: lib/core/auth-processor.ts
class AuthProcessor {
init(loginConfig: LoginConfig): void
processTemplate(templateContent: string, projectData: any): string
protectSensitiveContent(htmlContent: string): string
static validateConfig(loginConfig: LoginConfig): ValidationResult
}
interface LoginConfig {
active: boolean;
admited?: Array<{ email: string; password: string }>;
urlAuth?: string;
value_form?: { email: string; password: string };
response_success?: number;
response_error?: number;
}
Métodos clave:
// Inicializar con configuración
processor.init({
active: true,
admited: [{ email: "user@test.com", password: "test123" }]
});
// Procesar template HTML
const processedHtml = processor.processTemplate(templateContent, projectData);
// Validar configuración
const validation = AuthProcessor.validateConfig(loginConfig);
if (!validation.valid) {
console.error('Config errors:', validation.errors);
}
Ubicación: template/src/components/auth.ts
class AuthManager {
// Inicialización
init(loginConfig: LoginConfig): void
// Estado de autenticación
isLoginRequired(): boolean
isAuthenticated(): boolean
// Proceso de login
login(email: string, password: string): Promise<LoginResult>
logout(): void
// Gestión de sesión
createSession(email: string, method: 'local' | 'remote'): Promise<void>
loadSession(): void
clearSession(): void
// Información de debug
getSessionInfo(): AuthSession | null
}
interface LoginResult {
success: boolean;
message: string;
}
interface AuthSession {
email: string;
authenticated: boolean;
expires: number;
method: 'local' | 'remote';
}
Uso en browser:
// AuthManager está disponible globalmente
const authManager = window.authManager;
// Verificar estado
if (authManager.isAuthenticated()) {
console.log('Usuario autenticado');
}
// Login programático
authManager.login('user@test.com', 'password123')
.then(result => {
if (result.success) {
console.log('Login exitoso');
} else {
console.error('Login falló:', result.message);
}
});
// Información de sesión
const session = authManager.getSessionInfo();
console.log('Sesión actual:', session);
Algoritmo: AES-256-CBC con PBKDF2
Ubicación: AuthProcessor.encryptContent()
interface EncryptedContent {
data: string; // Contenido encriptado (Base64)
iv: string; // Vector de inicialización
salt: string; // Salt para PBKDF2
timestamp: number; // Timestamp de encriptación
version: string; // Versión del algoritmo
}
Proceso:
Algoritmo: SHA-256 con salt personalizado
function hashPassword(password: string, salt: string): string {
return CryptoJS.SHA256(password + salt + 'apidoc-salt').toString();
}
Uso:
'apidoc-salt'
// Eventos disponibles
document.addEventListener('apidoc:auth:init', (event) => {
console.log('Sistema de auth inicializado');
});
document.addEventListener('apidoc:auth:login:success', (event) => {
console.log('Login exitoso:', event.detail);
});
document.addEventListener('apidoc:auth:login:error', (event) => {
console.error('Error de login:', event.detail);
});
document.addEventListener('apidoc:auth:logout', (event) => {
console.log('Usuario desconectado');
});
CSS Custom Properties:
:root {
/* Colores del overlay de login */
--apidoc-auth-overlay-bg: rgba(0, 0, 0, 0.8);
--apidoc-auth-form-bg: white;
--apidoc-auth-form-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
/* Colores del formulario */
--apidoc-auth-input-bg: white;
--apidoc-auth-input-border: #d1d5db;
--apidoc-auth-input-focus: #3b82f6;
/* Botón de login */
--apidoc-auth-button-bg: #3b82f6;
--apidoc-auth-button-hover: #2563eb;
--apidoc-auth-button-text: white;
}
/* Tema oscuro */
[data-theme="dark"] {
--apidoc-auth-form-bg: #1f2937;
--apidoc-auth-input-bg: #374151;
--apidoc-auth-input-border: #4b5563;
}
HTML del Formulario:
<div id="auth-overlay" class="fixed inset-0 bg-black bg-opacity-80 z-50">
<div class="min-h-screen flex items-center justify-center">
<form class="bg-white p-8 rounded-lg shadow-xl max-w-md w-full">
<!-- Form fields -->
</form>
</div>
</div>
El sistema respeta automáticamente el tema dark/light de APIDoc:
// Detectar tema actual
const currentTheme = document.documentElement.getAttribute('data-theme');
// Aplicar estilos según tema
if (currentTheme === 'dark') {
formElement.classList.add('dark-theme');
}
# Validación completa del sistema
node validate-system.js
# Test de credenciales y hashing
node test-credentials.js
# Flujo completo de testing
node test-complete-workflow.js
# Guía final de testing
node final-test-guide.js
// Variables globales disponibles
window.AuthManager // Clase AuthManager
window.authManager // Instancia global
window.LOGIN_CONFIG // Configuración parseada
window.API_DATA // Datos de la API
window.API_PROJECT // Información del proyecto
// Debug de autenticación
console.log('Auth enabled:', window.authManager?.isLoginRequired());
console.log('Authenticated:', window.authManager?.isAuthenticated());
console.log('Session:', window.authManager?.getSessionInfo());
// Debug de configuración
console.log('Login config:', window.LOGIN_CONFIG);
console.log('Local users:', window.LOGIN_CONFIG?.admited?.length);
console.log('Remote URL:', window.LOGIN_CONFIG?.urlAuth);
// Debug de templates
console.log('Templates loaded:', Object.keys(window.Handlebars?.templates || {}));
// Debug del DOM
console.log('Sidenav exists:', !!document.getElementById('sidenav'));
console.log('Templates exist:', !!document.getElementById('template-header'));
// Habilitar debug logging
localStorage.setItem('apidoc-debug', 'true');
// Configurar nivel de log
localStorage.setItem('apidoc-log-level', 'debug'); // debug, info, warn, error
// El sistema mostrará logs detallados en consola
import AuthManager from './components/auth';
class CustomAuthManager extends AuthManager {
constructor() {
super();
this.customFeatures = true;
}
// Sobrescribir método de login
async login(email: string, password: string): Promise<LoginResult> {
// Tu lógica personalizada antes del login
console.log('Custom login logic for:', email);
// Llamar al método padre
const result = await super.login(email, password);
// Tu lógica personalizada después del login
if (result.success) {
this.trackLoginEvent(email);
}
return result;
}
private trackLoginEvent(email: string): void {
// Analytics, logging, etc.
console.log('User logged in:', email);
}
}
// Reemplazar instancia global
window.authManager = new CustomAuthManager();
class AuthMiddleware {
constructor(authManager) {
this.authManager = authManager;
this.setupMiddleware();
}
setupMiddleware() {
// Interceptar todas las requests
const originalFetch = window.fetch;
window.fetch = async (url, options = {}) => {
// Agregar token de autenticación si está disponible
if (this.authManager.isAuthenticated()) {
const session = this.authManager.getSessionInfo();
options.headers = {
...options.headers,
'Authorization': `Bearer ${session.token}`
};
}
return originalFetch(url, options);
};
}
}
// Usar middleware
const middleware = new AuthMiddleware(window.authManager);
class AuthPlugin {
constructor(name, authManager) {
this.name = name;
this.authManager = authManager;
}
install() {
// Registrar hooks
document.addEventListener('apidoc:auth:login:success', (e) => {
this.onLoginSuccess(e.detail);
});
console.log(`Plugin ${this.name} installed`);
}
onLoginSuccess(userInfo) {
// Tu lógica del plugin
}
}
// Registrar plugins
const analyticsPlugin = new AuthPlugin('Analytics', window.authManager);
analyticsPlugin.install();
# Build para producción
npm run build:clean
# Generar documentación
apidoc -i src/ -o dist/docs/
# Verificar integridad
node dist/docs/validate-system.js
# Para configuración dinámica
export APIDOC_AUTH_URL="https://api.produccion.com/auth"
export APIDOC_SESSION_TIMEOUT="7200" # 2 horas
export APIDOC_DEBUG="false"
# Generar con variables
apidoc -i src/ -o dist/docs/ --config production-config.json
Bundle Splitting:
// En esbuild.config.js
export default {
entryPoints: ['template/src/main.ts'],
bundle: true,
splitting: true,
format: 'esm',
outdir: 'assets/',
plugins: [
// Separar AuthManager en chunk propio
{
name: 'auth-chunk',
setup(build) {
build.onResolve({ filter: /auth\.ts$/ }, args => ({
path: args.path,
external: false,
namespace: 'auth-chunk'
}));
}
}
]
};
Lazy Loading:
// Cargar AuthManager solo cuando sea necesario
async function initAuth() {
if (window.LOGIN_CONFIG?.active) {
const { default: AuthManager } = await import('./components/auth');
window.authManager = new AuthManager();
window.authManager.init(window.LOGIN_CONFIG);
}
}
// Métricas de autenticación
class AuthMetrics {
constructor() {
this.metrics = {
loginAttempts: 0,
successfulLogins: 0,
failedLogins: 0,
sessionDuration: []
};
}
trackLogin(success) {
this.metrics.loginAttempts++;
if (success) {
this.metrics.successfulLogins++;
} else {
this.metrics.failedLogins++;
}
// Enviar a analytics
this.sendMetrics();
}
sendMetrics() {
// Tu lógica de analytics
}
}
// Integrar con AuthManager
const metrics = new AuthMetrics();
window.authManager.on('login:attempt', (success) => {
metrics.trackLogin(success);
});
Auth0:
{
"urlAuth": "https://your-domain.auth0.com/oauth/token",
"value_form": {
"email": "username",
"password": "password"
}
}
Firebase Auth:
{
"urlAuth": "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword",
"value_form": {
"email": "email",
"password": "password"
}
}
Custom Laravel:
{
"urlAuth": "https://api.miapp.com/api/auth/login",
"value_form": {
"email": "email",
"password": "password"
},
"response_success": 200,
"response_error": 422
}
¡Sistema completo de autenticación dual implementado y documentado! 🎉