Skip to main content

Documentation 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.

The Sistema de Inventario Tecnológico uses a stateless, cookie-based JWT authentication flow. Calling POST /api/login with valid credentials returns a signed JSON Web Token stored in an HTTP-only cookie named acceso_token. Every subsequent request to a protected endpoint must include that cookie — the server reads and verifies it via the verificarToken middleware before the route handler runs. This page walks through the full lifecycle: logging in, making authenticated requests, inspecting the current session, and logging out.

Login

POST /api/login

Send the user’s email address and plaintext password. On success the server signs a JWT, sets it as an HTTP-only cookie, and returns a JSON body with basic user information.

Request body

correo
string
required
The user’s registered email address.
password
string
required
The user’s plaintext password. The server compares it against the bcrypt hash stored in Firestore.
{
  "correo": "admin@example.com",
  "password": "secret"
}

Success response — 200 OK

message
string
Human-readable confirmation, e.g. "Login exitoso".
token
string
The signed JWT. This is the same value that is set in the cookie. You do not need to store or send it manually — it is included here for debugging or non-browser clients.
user
object
Subset of the authenticated user’s Firestore document.
user.correo
string
Email address.
user.username
string
Display username.
user.sede
string
The office/branch the user belongs to.
user.rol
string
The user’s role (e.g. "superAdmin", "usuario").
{
  "message": "Login exitoso",
  "token": "<signed-jwt>",
  "user": {
    "correo": "admin@example.com",
    "username": "admin",
    "sede": "Sede Principal",
    "rol": "superAdmin"
  }
}
Set-Cookie: acceso_token=<signed-jwt>; HttpOnly; SameSite=Lax; Max-Age=3600
AttributeValuePurpose
HttpOnlytruePrevents JavaScript from reading the cookie
SameSitelaxAllows cross-site navigations while blocking CSRF
Max-Age3600 seconds (1 hour)Cookie expires after one hour; Express converts maxAge: 3600000 ms to seconds automatically
Securefalse (development)See warning below

Error responses

StatusConditionBody
401Email not found or password does not match{ "message": "Credenciales inválidas" }
403Account exists but its estado is not "activo"{ "message": "Usuario se encuentra inactivo" }
500Unexpected Firestore or server error{ "message": "Error en el servidor" }

curl example

curl -c cookies.txt -X POST http://localhost:3001/api/login \
  -H 'Content-Type: application/json' \
  -d '{"correo":"admin@example.com","password":"secret"}'
The -c cookies.txt flag saves the acceso_token cookie to a file so it can be reused in follow-up requests.
The cookie is set with secure: false in the current configuration. This is intentional for local HTTP development but means the cookie will be transmitted over unencrypted connections. In any staging or production environment served over HTTPS, set secure: true in the cookie options to prevent the token from being sent over plain HTTP.

Making Authenticated Requests

Every route protected by verificarToken expects the acceso_token cookie to be present on the request. You do not need to add any custom headers — the browser (or your HTTP client) handles this automatically once the cookie is set.

curl

Pass -b cookies.txt to replay the saved cookie jar:
curl -b cookies.txt http://localhost:3001/api/me

Axios

Create a shared Axios instance with withCredentials: true. This setting instructs the browser to include cookies on all cross-origin requests made through that instance:
import axios from 'axios';

const api = axios.create({
  baseURL: 'http://localhost:3001/api',
  withCredentials: true,
});

const { data } = await api.get('/me');
console.log(data.user.rol);

Fetch API

const response = await fetch('http://localhost:3001/api/me', {
  credentials: 'include',
});
const data = await response.json();
Centralise the Axios instance or a fetch wrapper in a single module so that withCredentials: true (or credentials: 'include') is never forgotten. Missing this option is the most common cause of unexpected 401 responses in browser clients.

Session Introspection Endpoints

GET /api/me

Returns the decoded JWT payload for the current session without hitting Firestore. Use this for quick permission checks or to populate a session store on page load. Requires: valid acceso_token cookie.

Response — 200 OK

autenticado
boolean
Always true when the endpoint returns 200.
user
object
The fields embedded in the JWT at login time.
user.correo
string
Email address.
user.username
string
Display username.
user.rol
string
Role string (e.g. "superAdmin", "usuario").
user.sede
string
Branch/office identifier derived from the user’s location at login.
{
  "autenticado": true,
  "user": {
    "correo": "admin@example.com",
    "username": "admin",
    "rol": "superAdmin",
    "sede": "Sede Principal"
  }
}

GET /api/usuarios/me

Returns the full Firestore document for the currently authenticated user, including all profile fields. Use this when you need data beyond what is encoded in the JWT (e.g. cedula, telefono, nombre, apellido, location details). Requires: valid acceso_token cookie.

Response — 200 OK

autenticado
boolean
Always true on success.
user
object
The complete Firestore document merged with the document’s id.
user.id
string
The Firestore document ID for the user.
user.correo
string
Email address.
user.username
string
Display username.
user.rol
string
Role string.
user.nombre
string
First name.
user.apellido
string
Last name.
user.cedula
string
National ID number.
user.telefono
string
Contact phone number.
user.ubicacion
object
Denormalized location object (region, estado, ciudad, sede, piso, ala).

Error responses

StatusConditionBody
404Token is valid but no matching Firestore document found{ "autenticado": false, "message": "Usuario no encontrado en la base de datos." }
500Firestore read failure{ "message": "Error interno al obtener el perfil" }

Logout

POST /api/logout

Clears the acceso_token cookie from the client. After this call the server will reject any further requests that rely on the old cookie. No request body required.

Response — 200 OK

{
  "message": "Sesión cerrada"
}
curl -b cookies.txt -c cookies.txt -X POST http://localhost:3001/api/logout

JWT Token Structure

The token is signed with the JWT_SECRET environment variable (falls back to "lol" in development). It contains the following claims:
ClaimTypeDescription
idstringFirestore document ID of the user
rolstringUser role at the time of login
sedestring | nullBranch derived from ubicacion.sede
usernamestringDisplay username
correostringEmail address
iatnumberIssued-at timestamp (Unix seconds)
expnumberExpiry timestamp — always iat + 3600 (1 hour)
Decoded payload example:
{
  "id": "aB3kLmNpQrStUvWx",
  "rol": "superAdmin",
  "sede": "Sede Principal",
  "username": "admin",
  "correo": "admin@example.com",
  "iat": 1718000000,
  "exp": 1718003600
}

Middleware Behavior

All protected routes pass through the verificarToken middleware before the route handler executes. The middleware reads req.cookies.acceso_token and branches as follows:
  1. Cookie absent401 { "message": "No autorizado, sesión expirada" }
  2. jwt.verify throws (expired, tampered, or wrong secret) → 401 { "message": "Token inválido" }
  3. Valid token → decoded payload is attached to req.user and next() is called
// Simplified middleware logic (from backend/middleware/verificarToken.js)
const token = req.cookies.acceso_token;

if (!token) {
  return res.status(401).json({ message: "No autorizado, sesión expirada" });
}

try {
  const decoded = jwt.verify(token, process.env.JWT_SECRET || "lol");
  req.user = decoded; // available as req.user in every subsequent handler
  next();
} catch (error) {
  return res.status(401).json({ message: "Token inválido" });
}
Sessions last exactly 1 hour from the moment of login. Once the token expires the next request to a protected endpoint will return 401 and the user must call POST /api/login again to obtain a fresh cookie. There is no silent token refresh mechanism — plan your frontend to detect 401 responses and redirect to the login page.

Build docs developers (and LLMs) love