Skip to content

Implement JWT authentication with blacklist and refactor error handling - #190

Merged
Benjakr04 merged 4 commits into
devfrom
OM-555-SEC-A07
Jun 2, 2026
Merged

Benjakr04 merged 4 commits into
devfrom
OM-555-SEC-A07

Conversation

@CrisNAC

@CrisNAC CrisNAC commented Jun 2, 2026 •

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

Release Notes

  • Seguridad

    • Los tokens se bloquean al cerrar sesión, previniendo su reutilización.
    • Validación mejorada de sesiones expiradas con mensajes diferenciados.
    • Cookies de autenticación configuradas con protecciones httpOnly y secure.
  • Refactor

    • Consolidación de la API pública en un índice centralizado.
    • Implementación de manejo global de errores en controladores.
    • Eliminación de registros innecesarios en la consola.

@coderabbitai

coderabbitai Bot commented Jun 2, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Se implementa un mecanismo de blacklist de tokens JWT con almacenamiento interno en Set, se refactoriza la sesión de usuario para integrar blacklist en login/logout, se centraliza la API pública mediante reexports en index.js, y se elimina sistemáticamente el logging en favor del manejo centralizado de errores en el middleware global.

Changes

Seguridad de sesión y consolidación de API

Layer / File(s) Summary
Mecanismo de blacklist de tokens
src/lib/tokenBlacklist.js, src/config/jwt.config.js
Se crea módulo de blacklist con Set privado; authenticate valida tokens contra la blacklist retornando 401 "Sesión inválida" si existe, e importa isBlacklisted para la verificación.
Consolidación de API pública mediante barrel exports
src/lib/index.js
Se centraliza la reexportación de utilidades (password, errores, validación, Prisma, pricing, horarios, autenticación, zona horaria, blacklist, validadores) bajo una única entrada consolidando la API de la librería.
Refactorización del controlador de sesión
src/modules/session/controllers/session.controllers.js
Se refactorizan imports consolidados desde lib/index.js; login mejora manejo de errores genéricos con UnauthorizedError y configura cookie con maxAge y path; logout integra blacklist (extrae token, agrega a blacklist, ejecuta limpieza); userSession diferencia TokenExpiredError de token inválido retornando mensajes específicos y delega errores finales a middleware global.
Eliminación de logs y migración a error handler global
src/lib/utils/password.utils.js, src/middlewares/errorHandler.js, src/modules/commerce/commerces/store.controller.js, src/modules/commerce/commerces/store.service.js, src/modules/commerce/product-tags/product-tag.controller.js, src/modules/commerce/products/product.controller.js, src/modules/global/categories/product-categories/product-category.controller.js, src/modules/global/categories/store-categories/store-category.controller.js, src/modules/global/distances/controllers/distance.controller.js, src/modules/images/services/product-image.service.js, src/modules/images/services/store-image.service.js, src/modules/images/services/user-image.service.js
Se eliminan sistemáticamente console.error, console.warn, console.info en múltiples archivos centralizando logging en errorHandler; se actualizan getProductCategories y getStoreCategories para aceptar next y delegar errores mediante next(error) en lugar de respuestas manuales.
Limpieza menor: comentarios
src/lib/store-timezone.js
Se eliminan comentarios descriptivos iniciales sin modificar funcionalidad.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • CrisNAC/BackendMarketplace#185: Ambos PRs modifican directamente src/modules/session/controllers/session.controllers.js en login/logout/userSession con cambios en manejo de JWT y flujo de sesión.
  • CrisNAC/BackendMarketplace#188: Ambos PRs tocan src/lib/utils/password.utils.js eliminando logs en funciones de hash y verificación de contraseña.
  • CrisNAC/BackendMarketplace#183: Ambas PRs modifican src/middlewares/errorHandler.js en el manejo de errores inesperados y console.error.

Suggested reviewers

  • SebaKisser

Poem

🐰 Un conejito de seguridad saltó sin miedo,
Tokens en blacklist, sesiones sin fuego,
Logs que se van, manejadores que crecen,
La librería brilla cuando sus APIs se mecen. 🔐✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed El título describe acertadamente los dos cambios principales del PR: la implementación de autenticación JWT con blacklist y la refactorización del manejo de errores eliminando logs de consola.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch OM-555-SEC-A07

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud

sonarqubecloud Bot commented Jun 2, 2026

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/modules/commerce/products/product.controller.js (1)

13-29: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Inconsistencia con el objetivo del PR: los errores no se delegan al error handler global.

El PR declara "centralizar el logging en el errorHandler global", pero estos endpoints capturan errores localmente y responden directamente con res.status(...).json(...), evitando que el middleware global de errores los vea o registre.

Solo deleteProduct (línea 79) delega correctamente con next(error). Para cumplir el objetivo del PR, todos los endpoints deberían seguir ese patrón: capturar el error y pasarlo a next(error) en lugar de construir la respuesta manualmente.

🔄 Patrón recomendado para alinear con el error handler global

Ejemplo para createProduct:

  export const createProduct = async (req, res, next) => {
    try {
      if (!req.user?.id_user) {
        return res.status(401).json({
          success: false,
          message: "Usuario autenticado requerido"
        });
      }
  
      const product = await createProductService(req.user.id_user, req.body);
      return res.status(201).json(product);
    } catch (error) {
-     return res.status(error.status || 500).json({
-       message: error.message || "Error interno del servidor"
-     });
+     next(error);
    }
  };

Aplicar el mismo cambio a updateProduct, getProductsSearch, getProductById, compareProducts, filterProducts y getRelatedProducts.

Also applies to: 31-49, 57-65, 84-101, 105-163

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/commerce/products/product.controller.js` around lines 13 - 29,
The endpoints (e.g., createProduct) currently handle errors locally in their
catch blocks and send res.status(...).json(...), which prevents the global
errorHandler from logging/handling them; update each controller function
(createProduct, updateProduct, getProductsSearch, getProductById,
compareProducts, filterProducts, getRelatedProducts and any others noted) to
accept the third parameter next (req, res, next) if not already present, remove
the local res response in the catch, and instead call next(error) so errors are
delegated to the global error handler; ensure you do this consistently across
all controller functions mentioned and keep successful responses unchanged.
src/modules/session/controllers/session.controllers.js (1)

147-155: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Unificá la validación de sesión con la política central de blacklist.

Acá se vuelve a hacer jwt.verify, pero no se consulta la blacklist que agregaste en src/config/jwt.config.js. Eso ya deja dos caminos de autenticación con reglas distintas. Reutilizá authenticate/un helper compartido y consumí req.user, o replicá explícitamente el check de blacklist acá.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/session/controllers/session.controllers.js` around lines 147 -
155, Aquí se está volviendo a llamar a jwt.verify en session.controllers.js
creando una segunda vía de autenticación que ignora la blacklist definida en
src/config/jwt.config.js; reemplazá esta verificación por la política central: o
1) reutilizá el middleware authenticate (o el helper compartido) y consumí
req.user en vez de llamar a jwt.verify aquí, o 2) si necesitás verificar el
token aquí, importá y llamá al chequeo de blacklist desde
src/config/jwt.config.js (por ejemplo isTokenBlacklisted o la función
equivalente) antes de aceptar el token y manejá TokenExpiredError igual que
ahora; en cualquiera de los dos casos eliminá la doble verificación con
jwt.verify para que todas las rutas usen la misma lógica de
blacklist/validación.
🧹 Nitpick comments (5)
src/modules/commerce/commerces/store.service.js (1)

546-548: ⚡ Quick win

Se perdió la distinción entre errores esperados e inesperados.

El código anterior distinguía entre errores ENOENT (archivo no encontrado, esperado) y otros errores inesperados como permisos insuficientes, disco lleno o problemas de red. Ahora todos los errores se silencian indiscriminadamente, lo que elimina visibilidad operativa de problemas reales en el storage.

Aunque esto se alinea con el objetivo de eliminar logs, considerar si el error handler global debería ver errores inesperados de storage para monitoreo de infraestructura.

🔍 Alternativa sugerida para mantener visibilidad de errores inesperados
  try {
    await fs.unlink(previousLogoPath);
- } catch {
-   // silencioso
+ } catch (error) {
+   // ENOENT es esperado (archivo ya borrado), otros errores podrían indicar problemas
+   if (error.code !== 'ENOENT') {
+     // Delegar al error handler global sin interrumpir el flujo
+     // (requiere contexto de request para usar next, o un logger centralizado)
+   }
  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/commerce/commerces/store.service.js` around lines 546 - 548,
Reemplaza el bloque catch silencioso por uno que capture el error (catch (err))
y distinga errores esperados de inesperados: si err.code === 'ENOENT'
simplemente ignora/retorna como antes, pero para cualquier otro error relánzalo
(throw err) o pásalo al manejador global para que se registre/monitoree; esto
aplica al catch en src/modules/commerce/commerces/store.service.js donde se
manejan operaciones de storage.
src/modules/images/services/store-image.service.js (2)

80-80: ⚡ Quick win

Riesgo de archivos huérfanos en storage.

Si falla deleteImage aquí, el logo se elimina de la BD pero el archivo permanece en storage, acumulando archivos huérfanos con el tiempo. El catch silencioso impide detectar estos escenarios.

Considerar:

  1. Loguear el error para monitorear la tasa de fallos de limpieza
  2. Implementar un proceso periódico de limpieza de archivos huérfanos
  3. O al menos documentar explícitamente que este riesgo es aceptado
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/images/services/store-image.service.js` at line 80, The silent
catch on await deleteImage(BUCKET, filePath) can leave orphaned files in storage
when deletion fails; instead of swallowing errors, catch the error and log it
(use the module's logger — e.g., processLogger or the existing logger instance)
with context including BUCKET and filePath, and emit/record a metric/alert for
failed deletions; additionally add a TODO comment referencing a periodic
orphaned-file cleanup job (or document acceptance of the risk) so maintainers
know to implement background cleanup if needed (update the call site
deleteImage(...) to .catch(err => { logger.error(..., { err, bucket: BUCKET,
path: filePath }); incrementMetric('storage.delete.failure'); })).

52-54: ⚡ Quick win

Se perdió visibilidad de fallos al eliminar el logo anterior.

El catch silencioso oculta todos los errores al borrar el logo antiguo del storage, incluyendo problemas de permisos, cuota excedida o fallos del servicio. Aunque el nuevo logo ya está en BD, estos errores podrían indicar problemas operativos que deberían monitorearse.

Considerar si el error handler global debería recibir estos errores para observabilidad de infraestructura.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/images/services/store-image.service.js` around lines 52 - 54,
Silent catch in the deletion of the previous logo (in
src/modules/images/services/store-image.service.js) hides operational failures;
change the empty catch to catch the error (e.g., catch (err)) and either log it
with the module's logger (e.g., this.logger.error or logger.error) including
err.stack/message and context ("deleting previous logo"), or forward it to the
centralized error/monitoring handler (e.g.,
reportError/monitoringService.capture/globalErrorHandler) so infrastructure
issues are observable; do not swallow the error silently.
src/modules/images/services/product-image.service.js (1)

54-56: ⚡ Quick win

Se perdió visibilidad de fallos al eliminar la imagen anterior.

El catch silencioso oculta todos los errores al borrar la imagen antigua del storage, incluyendo problemas de permisos, cuota excedida o fallos del servicio de storage. Aunque la nueva imagen ya está guardada en BD, estos errores podrían indicar problemas operativos que deberían monitorearse.

Considerar si el error handler global debería recibir estos errores para observabilidad de infraestructura, o documentar explícitamente que los fallos de limpieza se ignoran intencionalmente.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/images/services/product-image.service.js` around lines 54 - 56,
El bloque catch que actualmente silencia errores al eliminar la imagen anterior
en product-image.service.js debe dejar de ocultarlos: reemplaza el catch vacío
por un manejo que registre el error con contexto (p. ej. "failed to delete
previous product image" + error) y además propague el fallo al mecanismo de
observabilidad/global (por ejemplo enviar a reportError/logger de la app o
relanzar/forwardear el error) para no perder visibilidad operativa; modifica el
catch que envuelve la llamada de eliminación de la imagen anterior para incluir
logging detallado y/o invocar el handler global en lugar de ignorar la
excepción.
src/modules/images/services/user-image.service.js (1)

52-54: ⚡ Quick win

Se perdió visibilidad de fallos al eliminar el avatar anterior.

El catch silencioso oculta todos los errores al borrar el avatar antiguo del storage, incluyendo problemas de permisos, cuota excedida o fallos del servicio. Aunque el nuevo avatar ya está en BD, estos errores podrían indicar problemas operativos que deberían monitorearse.

Considerar si el error handler global debería recibir estos errores para observabilidad de infraestructura.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/images/services/user-image.service.js` around lines 52 - 54, The
empty catch that swallows errors when deleting the previous avatar must be
replaced so failures are observable: change the catch to capture the error
(catch (err)) and log it with context (e.g., user id, avatar key) using the
project's logger (or processLogger), and forward it to your monitoring/exporter
(e.g., Sentry.captureException or metrics) or the global error handler (rethrow
or call next(err)) instead of silently ignoring it; update the catch in the
function that saves the new avatar to DB (the catch following the old-avatar
deletion block) to implement these logging/monitoring and propagation steps.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/tokenBlacklist.js`:
- Line 1: The in-memory const blacklist = new Set() is not shared across
processes/restarts; replace it with a shared store that supports TTL (e.g.,
Redis) and expose functions like addToBlacklist(token, ttl) and
isBlacklisted(token) in tokenBlacklist.js so logout writes the token with an
expiration and auth checks query the shared store; remove reliance on the local
Set variable (blacklist) and ensure exported APIs accept/return Promises to work
with async Redis calls.

---

Outside diff comments:
In `@src/modules/commerce/products/product.controller.js`:
- Around line 13-29: The endpoints (e.g., createProduct) currently handle errors
locally in their catch blocks and send res.status(...).json(...), which prevents
the global errorHandler from logging/handling them; update each controller
function (createProduct, updateProduct, getProductsSearch, getProductById,
compareProducts, filterProducts, getRelatedProducts and any others noted) to
accept the third parameter next (req, res, next) if not already present, remove
the local res response in the catch, and instead call next(error) so errors are
delegated to the global error handler; ensure you do this consistently across
all controller functions mentioned and keep successful responses unchanged.

In `@src/modules/session/controllers/session.controllers.js`:
- Around line 147-155: Aquí se está volviendo a llamar a jwt.verify en
session.controllers.js creando una segunda vía de autenticación que ignora la
blacklist definida en src/config/jwt.config.js; reemplazá esta verificación por
la política central: o 1) reutilizá el middleware authenticate (o el helper
compartido) y consumí req.user en vez de llamar a jwt.verify aquí, o 2) si
necesitás verificar el token aquí, importá y llamá al chequeo de blacklist desde
src/config/jwt.config.js (por ejemplo isTokenBlacklisted o la función
equivalente) antes de aceptar el token y manejá TokenExpiredError igual que
ahora; en cualquiera de los dos casos eliminá la doble verificación con
jwt.verify para que todas las rutas usen la misma lógica de
blacklist/validación.

---

Nitpick comments:
In `@src/modules/commerce/commerces/store.service.js`:
- Around line 546-548: Reemplaza el bloque catch silencioso por uno que capture
el error (catch (err)) y distinga errores esperados de inesperados: si err.code
=== 'ENOENT' simplemente ignora/retorna como antes, pero para cualquier otro
error relánzalo (throw err) o pásalo al manejador global para que se
registre/monitoree; esto aplica al catch en
src/modules/commerce/commerces/store.service.js donde se manejan operaciones de
storage.

In `@src/modules/images/services/product-image.service.js`:
- Around line 54-56: El bloque catch que actualmente silencia errores al
eliminar la imagen anterior en product-image.service.js debe dejar de
ocultarlos: reemplaza el catch vacío por un manejo que registre el error con
contexto (p. ej. "failed to delete previous product image" + error) y además
propague el fallo al mecanismo de observabilidad/global (por ejemplo enviar a
reportError/logger de la app o relanzar/forwardear el error) para no perder
visibilidad operativa; modifica el catch que envuelve la llamada de eliminación
de la imagen anterior para incluir logging detallado y/o invocar el handler
global en lugar de ignorar la excepción.

In `@src/modules/images/services/store-image.service.js`:
- Line 80: The silent catch on await deleteImage(BUCKET, filePath) can leave
orphaned files in storage when deletion fails; instead of swallowing errors,
catch the error and log it (use the module's logger — e.g., processLogger or the
existing logger instance) with context including BUCKET and filePath, and
emit/record a metric/alert for failed deletions; additionally add a TODO comment
referencing a periodic orphaned-file cleanup job (or document acceptance of the
risk) so maintainers know to implement background cleanup if needed (update the
call site deleteImage(...) to .catch(err => { logger.error(..., { err, bucket:
BUCKET, path: filePath }); incrementMetric('storage.delete.failure'); })).
- Around line 52-54: Silent catch in the deletion of the previous logo (in
src/modules/images/services/store-image.service.js) hides operational failures;
change the empty catch to catch the error (e.g., catch (err)) and either log it
with the module's logger (e.g., this.logger.error or logger.error) including
err.stack/message and context ("deleting previous logo"), or forward it to the
centralized error/monitoring handler (e.g.,
reportError/monitoringService.capture/globalErrorHandler) so infrastructure
issues are observable; do not swallow the error silently.

In `@src/modules/images/services/user-image.service.js`:
- Around line 52-54: The empty catch that swallows errors when deleting the
previous avatar must be replaced so failures are observable: change the catch to
capture the error (catch (err)) and log it with context (e.g., user id, avatar
key) using the project's logger (or processLogger), and forward it to your
monitoring/exporter (e.g., Sentry.captureException or metrics) or the global
error handler (rethrow or call next(err)) instead of silently ignoring it;
update the catch in the function that saves the new avatar to DB (the catch
following the old-avatar deletion block) to implement these logging/monitoring
and propagation steps.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cb3a72b9-69ae-4b97-b4df-25dfd20a1482

📥 Commits

Reviewing files that changed from the base of the PR and between fbc7b7d and 0ffc4a7.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (17)
  • src/config/jwt.config.js
  • src/lib/index.js
  • src/lib/store-timezone.js
  • src/lib/tokenBlacklist.js
  • src/lib/utils/password.utils.js
  • src/middlewares/errorHandler.js
  • src/modules/commerce/commerces/store.controller.js
  • src/modules/commerce/commerces/store.service.js
  • src/modules/commerce/product-tags/product-tag.controller.js
  • src/modules/commerce/products/product.controller.js
  • src/modules/global/categories/product-categories/product-category.controller.js
  • src/modules/global/categories/store-categories/store-category.controller.js
  • src/modules/global/distances/controllers/distance.controller.js
  • src/modules/images/services/product-image.service.js
  • src/modules/images/services/store-image.service.js
  • src/modules/images/services/user-image.service.js
  • src/modules/session/controllers/session.controllers.js
💤 Files with no reviewable changes (8)
  • src/modules/global/categories/product-categories/product-category.controller.js
  • src/lib/store-timezone.js
  • src/modules/global/distances/controllers/distance.controller.js
  • src/modules/global/categories/store-categories/store-category.controller.js
  • src/middlewares/errorHandler.js
  • src/modules/commerce/commerces/store.controller.js
  • src/modules/commerce/product-tags/product-tag.controller.js
  • src/lib/utils/password.utils.js

Comment thread src/lib/tokenBlacklist.js
@Benjakr04
Benjakr04 merged commit 8648409 into dev Jun 2, 2026
3 of 4 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants