The Sistema de Inventario Tecnológico uses a stateless JWT-based authentication flow. On a successful login the server signs a token containing the user’s identity and role, then sets it as an HTTP-only cookie namedDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Danielings/Pasantia-Proyecto/llms.txt
Use this file to discover all available pages before exploring further.
acceso_token. Every subsequent request to a protected endpoint automatically includes this cookie, and the verificarToken middleware validates and decodes it before any route logic runs. Password recovery uses a separate time-limited token stored in MySQL.
Login — POST /api/login
Send the user’s email address and password in the request body. The server looks up the Firestore usuarios collection by correo, confirms the account is active, and verifies the password with bcrypt.compare(). On success, a signed JWT is set as a cookie and a bitacora entry with accion: "Login" is created.
Success Response — HTTP 200
Error Responses
| Scenario | HTTP Status | Message |
|---|---|---|
| Email not found or wrong password | 401 | "Credenciales inválidas" |
| Account is inactive | 403 | "Usuario se encuentra inactivo" |
| Server error | 500 | "Error en el servidor" |
Logout — POST /api/logout
Clears the acceso_token cookie from the browser, effectively ending the session. No server-side token invalidation is performed — the session is stateless.
Response — HTTP 200
Get Session Info — GET /api/me
Returns the lightweight JWT payload for the currently authenticated user. This endpoint is called by the frontend AuthContext on every application load to restore session state without requiring a full Firestore lookup.
Response — HTTP 200
verificarToken middleware intercepts the request before it reaches the route handler and returns HTTP 401.
JWT Structure
The token is signed usingjwt.sign() with the JWT_SECRET environment variable and expires after 1 hour (expiresIn: "1h").
Payload
id field is the Firestore document ID of the user. The sede field is read from the user’s embedded ubicacion.sede at login time and is used by downstream route handlers to scope inventory queries.
The verificarToken Middleware
Every protected route chains verificarToken as the first middleware in its handler array. The middleware reads the acceso_token cookie, verifies the signature, and attaches the decoded payload to req.user so route handlers can access identity and role information.
verificarToken runs successfully, role-specific middleware such as esSuperAdmin or permitirEscritura can further restrict access based on req.user.rol.
Axios must be configured with
withCredentials: true for browsers to include the acceso_token cookie in cross-origin requests. Without this flag, the cookie is silently omitted and every protected endpoint will return HTTP 401. The same applies to the session-check call in AuthContext.Frontend Auth Pattern — AuthContext & RutaProtegida
The frontend maintains a global authentication state using React Context. On application startup, AuthProvider calls GET /api/me with withCredentials: true. If the cookie is still valid, the returned user object is stored in state and the app renders normally. If the call fails (expired or missing token), user is set to null.
cargando becomes false, preventing a flash of unauthenticated UI while the session check is in flight.
RutaProtegida — Route Guard
All authenticated routes are wrapped inside the RutaProtegida component defined in App.jsx. It reads user from AuthContext and redirects to /login if no session exists.
/dashboard, /registro, /bitacora, and /ubicacion are all children of this guard. Public routes (/login, /recuperar-password, /nueva-password) are defined outside of it.
Password Recovery Flow
Password recovery is handled by a separate Express router (recuperarPassword.js) backed by a MySQL database rather than Firestore. The flow consists of three steps:
Request a reset link — POST /api/recuperar-password
Send the user’s email address. The server generates a 32-byte cryptographically random token with The same generic response is returned whether the email is registered or not, to prevent user enumeration.
crypto.randomBytes(32).toString('hex'), stores it alongside a 15-minute expiry timestamp in the MySQL usuarios table, then emails the user a reset link containing the token.Validate the token — GET /api/validar-token?token=<token>
The frontend password-reset page calls this endpoint as soon as it loads (using the Returns
token query parameter from the URL) to confirm the link is still valid before showing the new-password form.{ "valid": false, "message": "Enlace inválido o expirado." } with HTTP 400 if the token does not exist or its token_expires timestamp has passed.Set the new password — POST /api/restablecer-password
Submit the token and the new password (minimum 6 characters). The server re-validates the token and expiry, bcrypt-hashes the new password (10 rounds), updates the MySQL
usuarios row, and clears both reset_token and token_expires to prevent reuse.The password recovery flow uses MySQL (via a connection pool configured in
backend/config/bd.js), not Firestore. This is a separate data store from the main user collection. If you are deploying or migrating the system, ensure the MySQL instance is accessible and the personas and usuarios tables are properly seeded.