UNPKG

sdk-simple-auth

Version:

Universal JavaScript/TypeScript authentication SDK with multi-backend support, automatic token refresh, and React integration

514 lines (432 loc) 12.7 kB
# Guía de Interceptores de Axios ## 🎯 Solución al Problema de Sesiones Persistentes Esta guía te muestra cómo usar los interceptores de Axios para que el SDK detecte automáticamente cuando el servidor rechaza tus tokens (401, 422, 403) y cierre la sesión local. --- ## 🔧 Tu Código Actual (ANTES) ```javascript // src/services/apiClient.js import authSDK from "@/services/sdk-simple-auth"; import { cleanFilters } from "@/utils/cleanFilters"; import { environment } from "@/utils/environment"; import axios from "axios"; const apiClient = axios.create({ baseURL: environment.apiUrl, headers: { "Content-Type": "application/json", }, }) // Interceptor manual apiClient.interceptors.request.use( async (config) => { const token = await authSDK.getAccessToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } if (config.method?.toLowerCase() === "get" && config.params) { config.params = cleanFilters(config.params); } return config; }, (error) => { console.log(error) return Promise.reject(error); } ); export default apiClient; ``` ```javascript // src/services/sdk-simple-auth.js import { environment } from "@/utils/environment"; import { AuthSDK } from "sdk-simple-auth"; const authSDK = new AuthSDK({ authServiceUrl: environment.apiUrl, endpoints: { login: "/login", logout: "/logout", refresh: "/refresh" }, storage: { type: "indexedDB", dbName: "tps-intermotors", storeName: "auth", dbVersion: 1, tokenKey: "tps-intermotors_auth_token", userKey: "tps-intermotors_auth_user", refreshTokenKey: "tps-intermotors_auth_refresh_token", }, tokenRefresh: { enabled: true, bufferTime: 1800 }, sessionValidation: { enabled: true, validateOnFocus: true, validateOnVisibility: true, maxInactivityTime: 300, autoLogoutOnInvalid: true } }) export default authSDK; ``` **Problema:** Si el servidor retorna 401, Axios sigue intentando peticiones y la sesión no se cierra. --- ## ✅ Solución con Interceptores Automáticos (DESPUÉS) ### Opción 1: Interceptores Completos (Recomendada) ```javascript // src/services/apiClient.js import { environment } from "@/utils/environment"; import { AuthSDK } from "sdk-simple-auth"; import { cleanFilters } from "@/utils/cleanFilters"; import axios from "axios"; // 1. Crear instancia de Axios (SIN interceptores manuales aún) const apiClient = axios.create({ baseURL: environment.apiUrl, headers: { "Content-Type": "application/json", }, }); // 2. Configurar AuthSDK con interceptores automáticos const authSDK = new AuthSDK({ authServiceUrl: environment.apiUrl, endpoints: { login: "/login", logout: "/logout", refresh: "/refresh" }, storage: { type: "indexedDB", dbName: "tps-intermotors", storeName: "auth", dbVersion: 1, tokenKey: "tps-intermotors_auth_token", userKey: "tps-intermotors_auth_user", refreshTokenKey: "tps-intermotors_auth_refresh_token", }, tokenRefresh: { enabled: true, bufferTime: 1800 }, sessionValidation: { enabled: true, validateOnFocus: true, validateOnVisibility: true, maxInactivityTime: 300, autoLogoutOnInvalid: true }, // ⭐ NUEVO: Configuración de interceptores interceptors: { enabled: true, // Activar interceptores autoInjectToken: true, // Inyectar token automáticamente handleAuthErrors: true, // Manejar 401/422 automáticamente axiosInstance: apiClient // Tu instancia de Axios } }, { // Callbacks onSessionInvalid: () => { console.warn('❌ Sesión inválida detectada'); window.location.href = '/login?expired=true'; }, onTokenRefresh: (tokens) => { console.log('🔄 Tokens refrescados automáticamente'); } }); // 3. OPCIONAL: Agregar tu interceptor personalizado para cleanFilters // (El SDK ya inyecta el token, solo necesitas cleanFilters) apiClient.interceptors.request.use( (config) => { // Limpiar filtros en GET requests if (config.method?.toLowerCase() === "get" && config.params) { config.params = cleanFilters(config.params); } return config; }, (error) => { return Promise.reject(error); } ); export { authSDK, apiClient }; export default apiClient; ``` **¡Eso es todo!** Ahora el SDK maneja automáticamente: - ✅ Inyección de tokens - ✅ Detección de 401/422/403 - ✅ Cierre de sesión automático - ✅ Intento de refresh antes de cerrar sesión --- ### Opción 2: Solo Detección de Errores Si prefieres mantener tu interceptor manual para inyectar tokens: ```javascript const authSDK = new AuthSDK({ authServiceUrl: environment.apiUrl, // ... resto de la config interceptors: { enabled: true, autoInjectToken: false, // Desactivar inyección automática handleAuthErrors: true, // Solo manejar errores axiosInstance: apiClient } }, { onSessionInvalid: () => { window.location.href = '/login?expired=true'; } }); // Mantén tu interceptor para inyectar token apiClient.interceptors.request.use( async (config) => { const token = await authSDK.getAccessToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } // ... cleanFilters return config; } ); ``` --- ## 🔄 Flujo de Funcionamiento ``` Usuario hace petición ↓ Interceptor Request (SDK) → Inyecta token automáticamente ↓ Petición al servidor ↓ Servidor responde 401 ↓ Interceptor Response (SDK) → Detecta el 401 → Intenta refresh token ↓ ¿Refresh exitoso? ├─ SÍ → Reintenta petición con nuevo token ✅ └─ NO → Limpia sesión local y llama onSessionInvalid() ❌ ``` --- ## 📝 Migración Paso a Paso ### Paso 1: Reconstruir la librería ```bash cd /home/olivio/SoftwareDevelopments/sdk_simple_auth npm run build ``` ### Paso 2: Actualizar tu archivo de configuración del SDK ```javascript // src/services/sdk-simple-auth.js (o donde tengas el SDK) import { AuthSDK } from "sdk-simple-auth"; import { environment } from "@/utils/environment"; import axios from "axios"; // Crear instancia de Axios export const apiClient = axios.create({ baseURL: environment.apiUrl, headers: { "Content-Type": "application/json", }, }); // Configurar SDK con interceptores const authSDK = new AuthSDK({ authServiceUrl: environment.apiUrl, endpoints: { login: "/login", logout: "/logout", refresh: "/refresh" }, storage: { type: "indexedDB", dbName: "tps-intermotors", storeName: "auth", dbVersion: 1, tokenKey: "tps-intermotors_auth_token", userKey: "tps-intermotors_auth_user", refreshTokenKey: "tps-intermotors_auth_refresh_token", }, tokenRefresh: { enabled: true, bufferTime: 1800 }, sessionValidation: { enabled: true, validateOnFocus: true, validateOnVisibility: true, maxInactivityTime: 300, autoLogoutOnInvalid: true }, interceptors: { enabled: true, autoInjectToken: true, handleAuthErrors: true, axiosInstance: apiClient } }, { onSessionInvalid: () => { console.warn('Sesión inválida, redirigiendo...'); window.location.href = '/login?expired=true'; } }); // Interceptor para cleanFilters (opcional) apiClient.interceptors.request.use( (config) => { if (config.method?.toLowerCase() === "get" && config.params) { config.params = cleanFilters(config.params); } return config; } ); export default authSDK; ``` ### Paso 3: Eliminar interceptores manuales antiguos ```javascript // ELIMINAR ESTE CÓDIGO (ya no es necesario): /* apiClient.interceptors.request.use( async (config) => { const token = await authSDK.getAccessToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; } ); */ ``` ### Paso 4: Probar ```javascript // En tu consola del navegador: // 1. Hacer login await authSDK.login({ email: 'test@test.com', password: 'password' }); // 2. Hacer una petición normal const response = await apiClient.get('/providers'); console.log(response.data); // 3. En tu backend, revocar el token o hacerlo expirar // 4. Intentar otra petición await apiClient.get('/providers'); // Deberías ver en consola: // AxiosInterceptor: Authentication error detected (401) // AxiosInterceptor: Attempting token refresh... // Token refresh failed: ... // AxiosInterceptor: Session invalid (HTTP 401), triggering logout // ❌ Sesión inválida detectada // [Redirige a /login] ``` --- ## 🎮 Casos de Uso ### Caso 1: E-commerce ```javascript const authSDK = new AuthSDK({ authServiceUrl: 'https://api.store.com', interceptors: { enabled: true, autoInjectToken: true, handleAuthErrors: true, axiosInstance: apiClient } }, { onSessionInvalid: () => { // Permitir navegar sin cuenta console.warn('Sesión expirada, puedes seguir navegando'); // No redirigir, solo mostrar banner showBanner('Inicia sesión para completar tu compra'); } }); ``` ### Caso 2: Dashboard Admin ```javascript const authSDK = new AuthSDK({ authServiceUrl: 'https://admin.example.com', interceptors: { enabled: true, autoInjectToken: true, handleAuthErrors: true, axiosInstance: apiClient } }, { onSessionInvalid: async () => { // Guardar trabajo pendiente await saveUnsavedWork(); window.location.href = '/login?reason=session_expired'; } }); ``` ### Caso 3: App Móvil (PWA) ```javascript const authSDK = new AuthSDK({ authServiceUrl: 'https://api.mobile.com', interceptors: { enabled: true, autoInjectToken: true, handleAuthErrors: true, axiosInstance: apiClient }, sessionValidation: { enabled: true, maxInactivityTime: 180 // 3 minutos para móvil } }, { onSessionInvalid: () => { showModal('Tu sesión ha expirado. Por favor inicia sesión de nuevo.'); } }); ``` --- ## 🐛 Troubleshooting ### Problema: Interceptores no funcionan **Solución:** 1. Verificar que `interceptors.enabled` sea `true` 2. Verificar que pasaste `axiosInstance` 3. Ver logs en consola: ```javascript console.log('Interceptor activo?', authSDK.axiosInterceptorManager?.isActive()); console.log('Estado:', authSDK.axiosInterceptorManager?.getStatus()); ``` ### Problema: Token no se inyecta **Solución:** 1. Verificar que `autoInjectToken` sea `true` 2. Verificar en Network tab si el header Authorization se envía 3. Asegúrate de que el SDK esté inicializado antes de hacer peticiones ### Problema: 401 pero no cierra sesión **Solución:** 1. Verificar que `handleAuthErrors` sea `true` 2. Ver logs en consola para entender qué pasa 3. Verificar que el callback `onSessionInvalid` esté configurado --- ## 📊 Ventajas vs Código Anterior | Feature | Antes | Ahora | |---------|-------|-------| | Inyección de token | Manual en cada petición | Automática ✅ | | Detección de 401 | No detectada | Automática ✅ | | Cierre de sesión | Manual | Automático ✅ | | Refresh automático | No | Sí ✅ | | Código limpio | ❌ Interceptores en varios lugares | ✅ Centralizado | | Mantenibilidad | ❌ Difícil | ✅ Fácil | --- ## 🔍 Debugging ```javascript // Ver estado de interceptores console.log('Status:', authSDK.axiosInterceptorManager?.getStatus()); // Ver logs detallados // En la consola verás: // AxiosInterceptor: Request interceptor configured // AxiosInterceptor: Response interceptor configured // ✅ Axios interceptors configured successfully // Al hacer peticiones: // AxiosInterceptor: Token injected automatically // Si hay error 401: // AxiosInterceptor: Authentication error detected (401) // AxiosInterceptor: Attempting token refresh... // AxiosInterceptor: Session invalid (HTTP 401), triggering logout ``` --- ## ✅ Checklist de Migración - [ ] Reconstruir librería (`npm run build`) - [ ] Actualizar configuración del SDK con `interceptors` - [ ] Pasar `axiosInstance` al SDK - [ ] Configurar callback `onSessionInvalid` - [ ] Eliminar interceptores manuales antiguos - [ ] Probar login y peticiones - [ ] Probar con token expirado/revocado - [ ] Verificar que redirige a login en 401 - [ ] Verificar logs en consola --- ## 📚 Recursos Adicionales - [Ejemplo completo](../examples/axios-interceptor-example.js) - [Documentación de validación de sesión](./SESSION_VALIDATION.md) - [Guía de desarrollo](../DESARROLLO.md)