Ir al contenido

AuthManager

AuthManager es el broker del SDK responsable de autenticar al usuario contra el broker remoto, registrar el dispositivo, persistir el token de sesión y los headers HMAC de CouchDB, y mantener el aislamiento multi-tenant del dispositivo. Es el primer eslabón del arranque del SDK: sin un token válido en localStorage no hay sincronización.

Forma parte del conjunto de brokers del SDK y se apoya en la infraestructura descrita en plataforma e infraestructura.

  • Autenticación de usuario (login, loginWithGoogle, registerUser, createUser).
  • Persistencia y renovación de tokens (saveToken, getToken, refreshAuthentication).
  • Registro y estado del dispositivo (registerDevice, updateDeviceStatus, updateDeviceInfo).
  • Cierre de sesión y limpieza del estado local (logout, signOut).
  • Lectura de roles y verificación de permisos desde el JWT (getCurrentRoles, hasPermission).
  • Hooks de ciclo de vida para que el SDK reaccione a login, refresh y logout.

Todo el estado de sesión se guarda en window.localStorage bajo el prefijo tablesProxyToken (expuesto como AuthManager.tokenPrefix):

ClaveContenido
tablesProxyToken-AuthJWT de acceso
tablesProxyToken-Refreshrefresh token
tablesProxyToken-expiresAt-Authtimestamp de expiración del JWT (ms)
X-Couchdb-Headers-Remoteheaders HMAC para CouchDB remoto
X-Couchdb-Headers-Localheaders HMAC para CouchDB local

El JWT codifica companyId, userId y los roles. AuthManager lo decodifica con jwtDecode para resolver la compañía y el usuario actuales sin llamadas extra al servidor.

  1. El cliente llama login(credentials) con username, password; el SDK adjunta deviceInfo y hace POST /auth.

  2. Si la respuesta no es 200, se devuelve { success: false, error } sin tocar el estado local.

  3. En éxito se ejecuta _cleanupBeforeLogin(): corre los handlers de signOut y destruye todas las PouchDB locales trackeadas (destroyAllLocalDatabases). Esto es deliberadamente agresivo para impedir que datos de un tenant anterior contaminen al nuevo usuario.

  4. Se persisten los headers de CouchDB (Remote y Local) y los tokens (Auth con expiresIn, Refresh con refreshExpiresIn).

  5. Se ejecutan los handlers de login (_runLoginHandlers) para invalidar caches dependientes del usuario (permisos, preferencias).

loginWithGoogle(credential) sigue exactamente la misma lógica contra POST /auth/google.

AuthManager produce los insumos que el resto del arranque consume:

  • El token habilita las llamadas HTTP autenticadas y el proxy de CouchDB.
  • Los headers HMAC (X-Couchdb-Headers-*) autorizan la replicación.
  • registerDevice() hace POST /auth/connect y, si el servidor devuelve deviceId, actualiza deviceInfo.id. Requiere token previo; si no hay, retorna error.

El handshake de inicialización (sdk:initready) que coordina el ruteo de base de datos vive en el SyncManager y en tables-socket, y se apoya en la sesión que AuthManager dejó establecida. Ver sincronización para el detalle del flujo realtime.

Tres registradores permiten al SDK enganchar lógica sin acoplar AuthManager a otros módulos. Los handlers corren en secuencia y cada error queda aislado (no aborta el resto):

authManager.registerOnLogin(async () => { /* invalidar caches de usuario */ });
authManager.registerOnTokenRefresh(async () => { /* revalidar roles/permisos */ });
authManager.registerOnSignOut(async () => { /* destruir PouchDB local, reset singleton */ });
  • onLogin: tras un login exitoso, una vez persistido el token.
  • onTokenRefresh: tras un refresh exitoso (el backend podría re-emitir roles distintos).
  • onSignOut: durante el logout, después de notificar al servidor y antes de limpiar el estado local. También se dispara al detectar cambio de companyId en login o refresh.

refreshAuthentication() hace POST a proxyRoutes.refreshToken con el refresh token y reescribe Auth y Refresh. Si detecta que el companyId del nuevo token difiere del anterior, fuerza el cleanup completo (_cleanupBeforeLogin) igual que en login; si el tenant no cambia, no toca la base de datos local para no perder datos offline.

getToken() es el accessor canónico: si isTokenExpired() es true, intenta refrescar; si el refresh falla, llama logout() y devuelve null. Usa un margen de seguridad de 1 minuto, replicado en isAboutToExpire().

logout() y signOut() delegan en _clearAllAuth(), que:

  1. Notifica al servidor con POST /auth/logout (solo si hay token), enviando deviceId.

  2. Ejecuta los handlers de signOut antes de limpiar tokens, porque alguno puede necesitar el token aún válido (por ejemplo, confirmar la replicación final).

  3. Limpia el estado en memoria (token, lastCredentials, currentCompany).

  4. Borra de localStorage los tokens Auth y Refresh y los headers X-Couchdb-Headers-Remote / X-Couchdb-Headers-Local.

MétodoDescripción
login(credentials)Autentica vía POST /auth; persiste token y headers HMAC.
loginWithGoogle(credential)Autentica con Google vía POST /auth/google.
registerUser(credentials)Registra usuario vía POST /auth/register.
createUser(credentials)Crea usuario vía POST /register-user.
logout() / signOut()Cierra sesión y limpia todo el estado local.
getToken()Devuelve el JWT vigente; refresca si está expirado.
getRefreshToken()Lee el refresh token desde localStorage.
refreshAuthentication()Renueva el JWT con el refresh token.
isTokenExpired() / isAboutToExpire()Estado de vigencia del JWT (margen 1 min).
checkIfLoggedIn()true si existe token Auth en localStorage.
getCurrentUser()JWT decodificado del usuario autenticado.
getCurrentCompany()Compañía actual (cacheada; resuelve companyId del JWT).
getCurrentRoles()Roles extraídos del JWT ([] si no hay token).
hasPermission(action)Verificación asíncrona de permiso por roles.
requirePermission(action)Verificación sincrónica; lanza si no autoriza.
registerDevice()Registra el dispositivo vía POST /auth/connect.
updateDeviceStatus({ isOnline })Actualiza estado del dispositivo vía PUT /auth/status.
updateDeviceInfo(partial)Mergea cambios en deviceInfo en memoria.
addDevice / deleteDevice / getDevicesGestión de dispositivos del usuario.
getCompanies / getCompany / updateCompany / deleteCompanyGestión de compañías.
updateUser / deleteUser / listUsers / getMyUserGestión de usuarios.
updatePaymentReference(companyId, saleId, data)Actualiza la referencia de un pago.