fix: remove hardcoded JWT secrets, add auth guards, and implement password reset - #1363
Conversation
…sword reset - BE-111: Removed all hardcoded fallback JWT secrets from AuthService, JwtStrategy, and AuthModule; app now fails at startup if JWT_SECRET or JWT_REFRESH_SECRET are missing or too short - BE-112: Added @UseGuards(JwtAuthGuard) to all 12 unguarded controllers (branches, vendors, purchase-orders, transfers, departments, categories, locations, inventory, audits, maintenance, asset-maintenance, licenses, notes-docs) - BE-113: Implemented full password reset flow: forgotPassword generates a hashed, time-limited token stored on the User entity; resetPassword validates and consumes the token; POST /auth/reset-password endpoint added; tokens invalidated on password change Closes DistinctCodes#1253 Closes DistinctCodes#1254 Closes DistinctCodes#1255
|
@OladipupuHussein7 is attempting to deploy a commit to the naijabuz's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
@OladipupuHussein7 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
forgotPassword() returned the raw resetToken directly in the JSON response with only a comment noting it should be emailed instead. As written, anyone who knows a user's email could call this endpoint, read the token back, and take over the account via resetPassword() - an unauthenticated account-takeover path with no gating. Gate the token behind NODE_ENV !== 'production', matching the existing convention in main.ts for the Swagger docs gate. Dev/test flows that rely on reading the token back from this response keep working; production callers get only the generic message until real email delivery is wired up.
Resolve import-line conflicts in branches.controller.ts and purchase-orders.controller.ts against already-merged DistinctCodes#1361 (Patch/ Delete/Req imports vs. this PR's UseGuards import) - both sets are needed, so combined them. transfers.controller.ts merged cleanly.
yusuftomilola
left a comment
There was a problem hiding this comment.
Reviewed. Strong security hardening overall:
- Removed hardcoded fallback JWT secrets ('secretKey' / 'refreshSecretKey') from auth.service.ts, auth.module.ts, and jwt.strategy.ts — previously anyone could forge valid tokens using the well-known default if the env var was ever unset. Now
main.tsfails fast at boot if JWT_SECRET/JWT_REFRESH_SECRET are missing or under 32 characters, so the app can no longer run with a weak/missing secret. - Real password-reset flow: random 32-byte token, hashed before storage, 1-hour expiry checked on use, bcrypt for the new password, and the reset token is cleared after use or after a normal password change.
- @UseGuards(JwtAuthGuard) added to branches, categories, departments, inventory, licenses, locations, maintenance, audits, notes-docs, transfers, purchase-orders, and vendors controllers — these were previously reachable with no authentication at all.
Found and fixed one issue before merging: forgotPassword() was returning the raw resetToken directly in the API response in all environments (comment said 'In production, send via email' but nothing gated it) — that's an unauthenticated account-takeover path for anyone who knows a user's email. Pushed a fix gating the token behind NODE_ENV !== 'production', matching the existing Swagger-gating convention in main.ts. Also worth a follow-up (not fixed here, lower severity): forgotPassword currently 404s for unknown emails vs 200 for known ones, which lets an attacker enumerate registered emails — the common fix is to return the same generic response either way.
Approving.
…-pagination Resolve import/method conflicts against DistinctCodes#1361 (branches delete, PO cancel, transfers cancel/complete) and DistinctCodes#1363 (JwtAuthGuard on branches/departments/purchase-orders/transfers/vendors) - all additive, combined both sides. purchase-orders.service.ts findAll now paginates AND still eager-loads lineItems (was dropped by the pagination rewrite otherwise). Also fixes a compile bug: create-purchase-order.dto.ts imported and used @isMin(0), which does not exist in class-validator (the correct decorator is @min, as already used elsewhere in this exact repo's pagination.dto.ts). Renamed to @min(0).
Summary
This PR addresses three critical authentication and security issues:
BE-111: Hardcoded fallback JWT secrets in AuthService and JwtStrategy
auth.service.ts,jwt.strategy.ts, andauth.module.tsJWT_SECRETorJWT_REFRESH_SECRETare missing or shorter than 32 charactersBE-112: Twelve controllers have no authentication guard at all
@UseGuards(JwtAuthGuard)to all 12 unguarded controllers:BE-113: AuthService.forgotPassword() is a no-op stub; there is no resetPassword endpoint
forgotPassword()now generates a cryptographically random, single-use, time-limited (1 hour) reset tokenUserentity with an expiry timestampPOST /auth/reset-passwordendpoint that validates and consumes the tokenupdateMe()Closes #1253
Closes #1254
Closes #1255
Closed #1120