Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Nieto2020/portalhub/llms.txt

Use this file to discover all available pages before exploring further.

The Authentication module handles every aspect of identity management in PortalHub. It covers credential validation and session creation at login, safe session teardown at logout, lightweight session-health checks for the frontend, CSRF token retrieval, mandatory first-login password changes, and administrator-driven user provisioning. All endpoints live under backend/modules/auth/ and follow the standard JSON envelope documented in the API Overview.
Every endpoint marked Auth required: Yes depends on the PHPSESSID session cookie being present and valid. Browsers handle this automatically for same-origin requests, but HTTP clients (mobile apps, scripts, Postman) must enable cookie persistence or supply the Cookie: PHPSESSID=<value> header manually on every authenticated call.

POST /auth/login.php

Validates email and password credentials, starts a PHP session, and returns the authenticated user’s identity. No prior authentication is required. Method: POST
Path: /backend/modules/auth/login.php
Auth required: No

Request Body

correo
string
required
The registered email address of the user. Must be a valid RFC 5322 email format.
password
string
required
The user’s plaintext password. It is verified server-side with password_verify() against the stored bcrypt hash.
curl -X POST http://localhost:8000/backend/modules/auth/login.php \
  -H "Content-Type: application/json" \
  -c cookies.txt \
  -d '{
    "correo": "user@example.com",
    "password": "secret"
  }'

Responses

status
integer
200 on success.
message
string
"Inicio de sesión exitoso" on success.
data
object
200 — Success
{
  "status": 200,
  "message": "Inicio de sesión exitoso",
  "data": {
    "id_usuario": 1,
    "id_rol": 1,
    "require_password_change": false
  }
}
400 — Missing Fields
{
  "status": 400,
  "message": "Faltan campos requeridos",
  "data": null
}
401 — Invalid Credentials
{
  "status": 401,
  "message": "Credenciales incorrectas",
  "data": null
}
403 — Inactive Account
{
  "status": 403,
  "message": "Usuario inactivo, contacte al administrador",
  "data": null
}

Session State After Login

On a successful login the following keys are written to $_SESSION:
KeyValue
id_usuarioAuthenticated user’s primary key.
id_rolUser’s role constant value.
require_password_change0 or 1 from the database.
ultima_actividadUnix timestamp of login time.
csrf_token64-character hex token (bin2hex(random_bytes(32))).
session_regenerate_id(true) is called immediately before setting session variables to prevent session-fixation attacks. On failed credential checks a deliberate sleep(1) delay is applied to mitigate brute-force timing attacks.

POST /auth/logout.php

Destroys the active session and expires the PHPSESSID cookie. The endpoint does not call checkAuth(), so it can be called even when the session has already expired — a safe-logout pattern. Method: POST
Path: /backend/modules/auth/logout.php
Auth required: No (safe to call with an expired session)
curl -X POST http://localhost:8000/backend/modules/auth/logout.php \
  -b cookies.txt

Response

200 — Session Destroyed
{
  "status": 200,
  "message": "Sesión cerrada exitosamente",
  "data": null
}
After this call the server invokes session_destroy() and overwrites the PHPSESSID cookie with an expiry in the past. The client’s cookie jar will no longer contain a valid session identifier.

GET /auth/check_session.php

Performs a lightweight session-validity check without updating the ultima_actividad timestamp. Use this endpoint for polling the session state from the frontend without inadvertently extending the inactivity timeout. Method: GET
Path: /backend/modules/auth/check_session.php
Auth required: Yes
curl -X GET http://localhost:8000/backend/modules/auth/check_session.php \
  -b cookies.txt

Response

status
integer
200 when the session is active and has not exceeded the inactivity timeout.
message
string
"Sesión activa".
data
null
No payload is returned. The 200 status itself confirms validity.
200 — Session Active
{
  "status": 200,
  "message": "Sesión activa",
  "data": null
}
401 — No Session / Expired
{
  "status": 401,
  "message": "Sesión expirada por inactividad. Por favor, ingrese de nuevo.",
  "data": null
}
Poll this endpoint at a cadence shorter than the 1 800-second timeout (e.g. every 5 minutes) to detect session expiry and redirect to the login page proactively. Because activity is not updated, a user who has left their browser idle will still see a 401 at the correct time.

GET /auth/csrf_token.php

Returns the CSRF token currently stored in the session. If the session does not yet contain a token (unusual after login, but possible in edge cases) a new one is generated and stored before responding. Method: GET
Path: /backend/modules/auth/csrf_token.php
Auth required: Yes
curl -X GET http://localhost:8000/backend/modules/auth/csrf_token.php \
  -b cookies.txt

Response

status
integer
200 on success.
message
string
"OK".
data
object
200 — Token Retrieved
{
  "status": 200,
  "message": "OK",
  "data": {
    "csrf_token": "a3f2c1d4e5b6078912345678abcdef90a3f2c1d4e5b6078912345678abcdef90"
  }
}
401 — Not Authenticated
{
  "status": 401,
  "message": "No autorizado. Inicie sesión Primero.",
  "data": null
}

POST /auth/update_password.php

Updates the authenticated user’s password. This endpoint is the only mutating action available to users whose require_password_change flag is 1. It requires a CSRF token and enforces a minimum password length of 8 characters. On success the require_password_change flag is cleared in both the database and the active session, and a new CSRF token is issued. Method: POST
Path: /backend/modules/auth/update_password.php
Auth required: Yes (accessible even when require_password_change = 1)

Request Body

new_password
string
required
The desired new password. Must be at least 8 characters long.
confirm_password
string
required
Must exactly match new_password. The server compares these server-side before hashing.
csrf_token
string
required
The CSRF token from $_SESSION['csrf_token']. Compared using hash_equals() to prevent timing attacks.
curl -X POST http://localhost:8000/backend/modules/auth/update_password.php \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{
    "new_password": "newPassword123",
    "confirm_password": "newPassword123",
    "csrf_token": "a3f2c1d4e5b6078912345678abcdef90a3f2c1d4e5b6078912345678abcdef90"
  }'

Responses

status
integer
200 on success.
message
string
"Contraseña actualizada exitosamente".
data
object
200 — Password Updated
{
  "status": 200,
  "message": "Contraseña actualizada exitosamente",
  "data": {
    "redirect": "../admin/dashboard.html"
  }
}
400 — Passwords Do Not Match
{
  "status": 400,
  "message": "Las contraseñas no coinciden",
  "data": null
}
400 — Password Too Short
{
  "status": 400,
  "message": "La contraseña debe tener al menos 8 caracteres",
  "data": null
}
403 — Missing CSRF Token
{
  "status": 403,
  "message": "Token CSRF faltante",
  "data": null
}
403 — Invalid CSRF Token
{
  "status": 403,
  "message": "Token CSRF inválido",
  "data": null
}
After a successful password update, $_SESSION['require_password_change'] is set to 0 and $_SESSION['csrf_token'] is rotated. Clients should discard the old CSRF token and fetch a fresh one from csrf_token.php if further mutating requests are needed in the same session.

POST /auth/register.php

Creates a new user account. Restricted exclusively to users with ROL_ADMIN (id_rol = 1). New accounts are always created with require_password_change = 1, forcing the new user to set their own password on first login. The password supplied here is hashed with bcrypt and stored, but serves only as a temporary credential. Method: POST
Path: /backend/modules/auth/register.php
Auth required: Yes — ROL_ADMIN only

Request Body

correo
string
required
Email address for the new account. Must be a valid email format and must not already exist in the usuarios table.
password
string
required
Temporary password for the new account. Hashed with PASSWORD_BCRYPT before storage. The user will be required to change it on first login.
id_rol
integer
required
Role to assign: 1 (Admin), 2 (Asesor), or 3 (Cliente).
numero_cliente
string
Optional client identifier (e.g. "CLI-001"). Stored in the numero_cliente column. Pass null or omit for non-client roles.
curl -X POST http://localhost:8000/backend/modules/auth/register.php \
  -H "Content-Type: application/json" \
  -b cookies.txt \
  -d '{
    "correo": "nuevo.cliente@example.com",
    "password": "temp123",
    "id_rol": 3,
    "numero_cliente": "CLI-001"
  }'

Responses

status
integer
201 on successful creation.
message
string
"Usuario registrado exitosamente".
data
null
No additional payload is returned on registration.
201 — User Created
{
  "status": 201,
  "message": "Usuario registrado exitosamente",
  "data": null
}
400 — Email Already Registered
{
  "status": 400,
  "message": "El correo ya está registrado",
  "data": null
}
400 — Invalid Email Format
{
  "status": 400,
  "message": "Formato de correo inválido",
  "data": null
}
403 — Insufficient Role
{
  "status": 403,
  "message": "Acceso denegado. Permisos insuficientes.",
  "data": null
}
The require_password_change flag is hard-coded to 1 at the database INSERT level and cannot be overridden via the request body. All provisioned users must change their password on first login before they can access any other part of the API.

Build docs developers (and LLMs) love