Protected Routes¶
Relevant source files * src/middlewares/verifyAdmin.js * src/middlewares/verifyToken.js * src/router.js
Purpose and Scope¶
This document details all HTTP routes in the application that require authentication or authorization to access. Protected routes implement access control using JWT-based middleware to verify user identity and role permissions before allowing access to sensitive resources.
For information about public routes that do not require authentication, see Public Routes. For details on the authentication mechanisms themselves, see Authentication & Authorization. For API-specific endpoints, see API Endpoints.
Overview¶
Protected routes in this application fall into two authorization tiers:
- User-level protection - Requires valid JWT authentication via
verifyTokenmiddleware - Admin-level protection - Requires valid JWT authentication and
rol='admin'viaverifyAdminmiddleware
All protected routes extract JWT tokens from HTTP-only cookies (cookie name: token) and validate them before processing the request. Invalid or missing tokens result in 401 (Unauthorized) or 403 (Forbidden) responses.
Sources: src/router.js L16-L17
src/middlewares/verifyToken.js L1-L18
src/middlewares/verifyAdmin.js L1-L45
Authentication Middleware Chain¶
The following diagram shows how authentication middleware is applied to protected routes:
flowchart TD
Request["HTTP Request"]
CookieParser["cookie-parser<br>Extracts cookies"]
RouteMatch["Route Matches<br>Protected Endpoint?"]
PublicRoute["Public Route Handler"]
AuthTier["Authorization<br>Tier?"]
VerifyToken["verifyToken Middleware<br>src/middlewares/verifyToken.js"]
VerifyAdmin["verifyAdmin Middleware<br>src/middlewares/verifyAdmin.js"]
TokenCheck["token in<br>req.cookies?"]
Return401["401 Unauthorized<br>'No autenticado'"]
JWTVerify["jwt.verify()<br>Validate token"]
Return403Token["403 Forbidden<br>'Token inválido'"]
AttachUser["Attach decoded payload<br>to req.user"]
RouteHandler["Route Handler<br>Execute business logic"]
AdminTokenCheck["token in<br>req.cookies?"]
Return403Access["403 Forbidden<br>'Acceso denegado'"]
AdminJWTVerify["jwt.verify()<br>Validate token"]
Return403Invalid["403 Forbidden<br>'Token inválido'"]
RoleCheck["decoded.rol<br>== 'admin'?"]
Return403AdminOnly["403 Forbidden<br>'Acceso solo para administradores'"]
AttachAdminUser["Attach decoded payload<br>to req.user"]
Request --> CookieParser
CookieParser --> RouteMatch
RouteMatch --> PublicRoute
RouteMatch --> AuthTier
AuthTier --> VerifyToken
AuthTier --> VerifyAdmin
VerifyToken --> TokenCheck
TokenCheck --> Return401
TokenCheck --> JWTVerify
JWTVerify --> Return403Token
JWTVerify --> AttachUser
AttachUser --> RouteHandler
VerifyAdmin --> AdminTokenCheck
AdminTokenCheck --> Return403Access
AdminTokenCheck --> AdminJWTVerify
AdminJWTVerify --> Return403Invalid
AdminJWTVerify --> RoleCheck
RoleCheck --> Return403AdminOnly
RoleCheck --> AttachAdminUser
AttachAdminUser --> RouteHandler
Sources: src/middlewares/verifyToken.js L3-L15
src/middlewares/verifyAdmin.js L24-L42
Protected Routes by Authorization Level¶
User-Level Protected Routes¶
These routes require a valid JWT token but do not enforce role restrictions. Any authenticated user (admin or regular user) can access them.
| Route | Method | Handler Location | Purpose | Response Type |
|---|---|---|---|---|
/admin |
GET | src/router.js L119-L134 | Product management dashboard | HTML (admin.ejs) |
/pdfAdmin |
GET | src/router.js L136-L151 | PDF preview of products table | HTML (pdfTabla.ejs) |
/soporte |
GET | src/router.js L220-L227 | Support chat interface | HTML (soporte.ejs) |
/api/mensajes/mios |
GET | src/router.js L256-L280 | Retrieve current user's messages | JSON |
/pdf/descargar |
GET | src/router.js L317-L353 | Download products PDF (Puppeteer) | PDF file |
/pdfkit/descargar |
GET | src/router.js L355-L396 | Download products PDF (PDFKit) | PDF file |
Sources: src/router.js L119-L396
src/middlewares/verifyToken.js L3-L15
Admin-Level Protected Routes¶
These routes require both a valid JWT token and the rol='admin' claim. Regular users receive a 403 Forbidden response.
| Route | Method | Handler Location | Purpose | Response Type |
|---|---|---|---|---|
/api/mensajes |
GET | src/router.js L229-L253 | Retrieve messages for specific user (query param: ?con=username) |
JSON |
/api/usuarios-conversaciones |
GET | src/router.js L283-L315 | List all users with admin conversations | JSON array |
Sources: src/router.js L229-L315
src/middlewares/verifyAdmin.js L24-L42
Detailed Route Documentation¶
GET /admin¶
Middleware: verifyToken
Purpose: Renders the product management dashboard for authenticated users.
Request Flow:
- Validates JWT token via
verifyToken - Queries all products from
productostable - Renders
admin.ejswith products and user information
Response Data:
{
productos: Array, // All rows from productos table
user: Object, // Decoded JWT payload (req.user)
login: true,
rol: String // User's role from JWT
}
Database Query:
SELECT * FROM productos
Sources: src/router.js L119-L134
GET /pdfAdmin¶
Middleware: verifyToken
Purpose: Renders a PDF-optimized HTML view of the products table (used by Puppeteer for PDF generation).
Request Flow:
- Validates JWT token via
verifyToken - Queries all products from
productostable - Renders
pdfTabla.ejstemplate
Response: HTML document formatted for print media
Sources: src/router.js L136-L151
GET /soporte¶
Middleware: verifyToken
Purpose: Renders the support chat interface for both users and administrators.
Request Flow:
- Validates JWT token via
verifyToken - Extracts username and role from
req.user - Renders
soporte.ejswith user context
Response Data:
{
user: {
username: String, // From req.user.user
role: String // From req.user.rol
}
}
Note: The view adapts its UI based on the role field. Admins see a multi-conversation interface; regular users see a single chat window.
Sources: src/router.js L220-L227
GET /api/mensajes¶
Middleware: verifyAdmin
Purpose: Retrieves all messages involving a specific user (for admin conversation view).
Query Parameters:
con(required) - The username to retrieve messages for
Request Flow:
- Validates JWT and admin role via
verifyAdmin - Extracts
conparameter from query string - Returns 400 if
conis missing - Queries messages where user is sender or recipient
- Returns messages ordered by date ascending
Database Query:
SELECT de_usuario, para_usuario, mensaje, fecha
FROM mensajes
WHERE (de_usuario = ? OR para_usuario = ?)
ORDER BY fecha ASC
Response: JSON array of message objects
Error Responses:
400- Missing?con=usuarioparameter403- User is not an admin500- Database query error
Sources: src/router.js L229-L253
GET /api/mensajes/mios¶
Middleware: verifyToken
Purpose: Retrieves all messages for the currently authenticated user.
Request Flow:
- Validates JWT via
verifyToken - Extracts username from
req.user.user - Queries messages where authenticated user is sender or recipient
- Returns messages ordered by date ascending
Database Query:
SELECT de_usuario, para_usuario, mensaje, fecha
FROM mensajes
WHERE (de_usuario = ? OR para_usuario = ?)
ORDER BY fecha ASC
Response: JSON array of message objects
Error Responses:
401- No valid token403- Token invalid500- Database query error
Sources: src/router.js L256-L280
GET /api/usuarios-conversaciones¶
Middleware: verifyAdmin
Purpose: Returns a list of all non-admin users who have conversations with administrators.
Request Flow:
- Validates JWT and admin role via
verifyAdmin - Executes complex query to find users in admin conversations
- Returns array of usernames
Database Query:
SELECT DISTINCT usuario
FROM (
SELECT de_usuario AS usuario FROM mensajes
WHERE para_usuario IN (SELECT usuario FROM usuarios WHERE rol = 'admin')
UNION
SELECT para_usuario AS usuario FROM mensajes
WHERE de_usuario IN (SELECT usuario FROM usuarios WHERE rol = 'admin')
) AS conversaciones
WHERE usuario NOT IN (SELECT usuario FROM usuarios WHERE rol = 'admin')
Response: JSON array of usernames (strings)
Query Logic:
- Finds messages sent TO admins (extracts sender)
- Finds messages sent FROM admins (extracts recipient)
- Combines with UNION to eliminate duplicates
- Filters out admin usernames from results
Error Responses:
403- User is not an admin500- Database query error
Sources: src/router.js L283-L315
PDF Generation Routes¶
GET /pdf/descargar¶
Middleware: verifyToken
Purpose: Generates and downloads a PDF of the products table using Puppeteer (headless Chrome).
Request Flow:
- Validates JWT via
verifyToken - Queries all products from database
- Renders
pdfTabla.ejstemplate to HTML string - Launches headless Chrome browser
- Loads HTML content into browser page
- Generates PDF from rendered page
- Closes browser
- Sends PDF as downloadable file
PDF Generation Configuration:
{
format: "A4",
printBackground: true,
margin: { top: "20px", bottom: "20px" }
}
Response Headers:
Content-Type: application/pdf
Content-Disposition: attachment; filename="productos.pdf"
Note: Uses Puppeteer for high-fidelity HTML-to-PDF conversion with full CSS support.
Sources: src/router.js L317-L353
GET /pdfkit/descargar¶
Middleware: verifyToken
Purpose: Generates and downloads a PDF of the products table using PDFKit (programmatic generation).
Request Flow:
- Validates JWT via
verifyToken - Queries all products from database
- Creates PDFDocument instance
- Writes title and table headers
- Iterates through products, writing each row
- Streams PDF directly to response
PDF Layout:
- Document size: A4
- Margin: 40 points
- Title: 18pt font, centered
- Table headers: Helvetica-Bold, 12pt
- Table data: Helvetica, 11pt
- Column positions: Ref (50), Nombre (150), Precio (300), Stock (380)
- Row spacing: 20 points
Response Headers:
Content-Type: application/pdf
Content-Disposition: attachment; filename="productos_desde_cero.pdf"
Note: Uses PDFKit for direct PDF generation without HTML rendering. More performant but less flexible than Puppeteer approach.
Sources: src/router.js L355-L396
Authorization Flow Diagram¶
sequenceDiagram
participant Client Browser
participant Express Router
participant src/router.js
participant verifyToken MW
participant src/middlewares/verifyToken.js
participant verifyAdmin MW
participant src/middlewares/verifyAdmin.js
participant Route Handler
participant MySQL Database
note over Client Browser,MySQL Database: User-Level Protected Route (/admin)
Client Browser->>Express Router: GET /admin
Express Router->>verifyToken MW: Call middleware
verifyToken MW->>verifyToken MW: Extract req.cookies.token
loop [Invalid token]
verifyToken MW->>Client Browser: 401 "No autenticado"
verifyToken MW->>verifyToken MW: jwt.verify(token, JWT_SECRET)
verifyToken MW->>Client Browser: 403 "Token inválido"
verifyToken MW->>verifyToken MW: Attach req.user = decoded
verifyToken MW->>Route Handler: next()
Route Handler->>MySQL Database: SELECT * FROM productos
MySQL Database->>Route Handler: Return results
Route Handler->>Client Browser: Render admin.ejs
note over Client Browser,MySQL Database: Admin-Level Protected Route (/api/mensajes)
Client Browser->>Express Router: GET /api/mensajes?con=user1
Express Router->>verifyAdmin MW: Call middleware
verifyAdmin MW->>verifyAdmin MW: Extract req.cookies.token
verifyAdmin MW->>Client Browser: 403 "Acceso denegado"
verifyAdmin MW->>verifyAdmin MW: jwt.verify(token, JWT_SECRET)
verifyAdmin MW->>Client Browser: 403 "Token inválido"
verifyAdmin MW->>verifyAdmin MW: Check decoded.rol
verifyAdmin MW->>Client Browser: 403 "Acceso solo para administradores"
verifyAdmin MW->>verifyAdmin MW: Attach req.user = decoded
verifyAdmin MW->>Route Handler: next()
Route Handler->>MySQL Database: SELECT ... FROM mensajes WHERE ...
MySQL Database->>Route Handler: Return messages
Route Handler->>Client Browser: JSON response
end
Sources: src/router.js L119-L396
src/middlewares/verifyToken.js L3-L15
src/middlewares/verifyAdmin.js L24-L42
JWT Payload Structure¶
When authentication succeeds, the decoded JWT payload is attached to req.user and contains:
| Field | Type | Description | Source |
|---|---|---|---|
user |
String | Username (primary key) | usuarios.usuario |
name |
String | Display name | usuarios.nombre |
rol |
String | User role ('admin' or 'user') | usuarios.rol |
imagen |
String | Profile image filename | usuarios.imagen |
This payload is available to all route handlers after successful authentication.
Sources: src/router.js L559-L564
Error Response Summary¶
| Status Code | Middleware | Error Message | Condition |
|---|---|---|---|
| 401 | verifyToken | "No autenticado" | Token missing from cookies |
| 403 | verifyToken | "Token inválido" | JWT verification failed |
| 403 | verifyAdmin | "Acceso denegado" | Token missing from cookies |
| 403 | verifyAdmin | "Token inválido" | JWT verification failed |
| 403 | verifyAdmin | "Acceso solo para administradores" | Valid token but rol !== 'admin' |
Sources: src/middlewares/verifyToken.js L6-L14
src/middlewares/verifyAdmin.js L28-L40
Security Considerations¶
- HTTP-only Cookies: JWT tokens are stored in HTTP-only cookies, preventing JavaScript access and XSS attacks
- Token Expiration: Tokens expire after 1 hour (configured in authentication flow)
- Secret Key: Token verification uses
process.env.JWT_SECRETenvironment variable - Role-based Access: Admin routes enforce role checking after token validation
- No Token in Response: Tokens are never included in JSON responses, only in Set-Cookie headers