Skip to main content

Autenticacion

La app implementa un sistema de autenticacion custom basado en JWT, sin depender de servicios externos como Clerk, Auth0 o Supabase. Toda la logica se concentra en el modulo src/modules/auth/ y el cliente API en src/shared/services/api/.

Flujo de Autenticacion

  1. El usuario ingresa email y contrasena 2. Se llama a authApi.login(email, password) 3. La API devuelve: token, refreshToken, tokenExpiresIn, datos de usuario 4. Se calcula tokenExpiry = Date.now() + tokenExpiresIn * 1000 5. Se almacenan tokens en SecureStore y datos de usuario en SecureStore 6. Se limpia el cache de tokens del API client (clearTokenCache) 7. Se actualiza el authStore con isAuthenticated: true 8. Se identifica al usuario en PostHog analytics 9. Se inicializa la conexion a Stream Chat (no bloqueante) 10. Se invalidan todas las queries de React Query

Gestion de Tokens

Token Refresh Automatico

Cuando el access token expira, el sistema gestiona la renovacion automaticamente:
Las requests que llegan durante el proceso de refresh se encolan en requestQueue. Una vez completado el refresh exitosamente, todas las requests encoladas se reintentan con el nuevo token. refreshTokens() es single-flight: si ya hay un refresh en curso, las llamadas concurrentes se unen a la promesa existente en lugar de gastar el mismo refresh token en paralelo.

Resiliencia de Sesion

Un fallo durante el refresh ya no cierra la sesion automaticamente. El sistema distingue entre un rechazo real de la sesion y un fallo de transporte transitorio:
Cuando se agotan los reintentos por fallos transitorios, la sesion se conserva y se marca en memoria como sessionUnverified: true (no se persiste). El hook useSessionResilience observa la reconexion de red (offline→online) y el retorno de la app a primer plano, y llama a verifySession() para re-confirmar la sesion en cuanto vuelve la conectividad. Un corte de red nunca desemboca en un logout.
Los endpoints de auth (/auth/login, /auth/register, /auth/refresh-token) nunca disparan un refresh cuando responden UNAUTHENTICATED (ver isAuthEndpoint). Incluir el propio /auth/refresh-token evita un deadlock en el arranque en frio.

Token Caching

El API client implementa un cache de token en memoria para evitar lecturas frecuentes a SecureStore:

API Client

El cliente HTTP (src/shared/services/api/client.ts) esta construido sobre Axios con la siguiente configuracion:

Interceptors

Request Interceptor

  • Agrega parametro lang a la URL segun idioma actual
  • Cambia baseURL a chat backend si useChatBackend: true
  • Obtiene token de auth y agrega header Authorization: Bearer <token>
  • Respeta requiresAuth: false en headers para requests publicos

Response Interceptor

  • Verifica el campo code en la respuesta de la API
  • Si code !== SUCCESS, lanza ApiError
  • Si code === UNAUTHENTICATED, inicia flujo de token refresh
  • Para errores de red (sin response), clasifica el tipo de fallo

Manejo de Errores

El error handler (src/shared/services/api/errorHandler.ts) clasifica los errores de red en categorias: Todos los errores se reportan a Sentry con contexto adicional (endpoint, metodo, plataforma, diagnosticos de red).

Codigos de API

El sistema maneja los siguientes codigos de respuesta:

Almacenamiento Seguro

Datos cifrados en el dispositivo:
  • Access token
  • Refresh token
  • Token expiry
  • Datos de usuario (id, email, displayName, phone)
Se accede a traves del wrapper secureStorage en src/shared/services/storage/secureStorage.ts.
Nunca almacenar tokens reales, URLs de API, ni credenciales en el codigo fuente ni en archivos de configuracion que se commiteen al repositorio. Los valores sensibles se gestionan mediante variables de entorno.

Pantallas de Auth

Las pantallas de autenticacion se encuentran en el grupo (auth):

Inicializacion de Auth

Al iniciar la app, authStore.initializeAuth() ejecuta el siguiente flujo:
  1. Espera a que el store se hidrate desde almacenamiento persistido (con techo de 3s: si la hidratacion no reporta, continua igualmente)
  2. Verifica si hay tokens en el estado hidratado
  3. Si hay tokens pero estan expirados, intenta refresh automatico dentro de un presupuesto de 12s (STARTUP_REFRESH_BUDGET_MS); si se excede, deja de esperar (no de refrescar), marca la sesion como sessionUnverified y libera el splash
  4. Si no hay tokens en estado, busca directamente en SecureStore con techo de 5s por lectura (fallback)
  5. Si encuentra tokens validos, marca isAuthenticated: true e identifica al usuario en analytics
  6. Si no encuentra tokens, establece estado no autenticado
La inicializacion tiene un timeout de 15 segundos cuyo unico rol es liberar el spinner: ya no destruye la sesion. Una API lenta tras un deploy (init >15s) solia cerrar la sesion sin que ningun request hubiera sido rechazado; ahora se conservan user, tokens e isAuthenticated. Los arranques genuinamente colgados siguen cubiertos por los dos timeouts externos de 2 min (useAppInitialization / useNavigation), que ademas reportan a Sentry que pasos quedaron pendientes.

Logout

El proceso de logout realiza las siguientes operaciones:
  1. Llama a authApi.logout() para invalidar tokens en el servidor
  2. Limpia la cola de requests pendientes (clearRequestQueue)
  3. Reset de analytics (analytics.reset())
  4. Limpia el estado del store
  5. Cancela y limpia todas las queries de React Query
  6. Desconecta y resetea el chat de Stream
  7. Limpia todos los datos de auth en SecureStore