Harden Authentication and Account Security Policy - #139
Merged
3m1n3nc3 merged 7 commits intoAug 20, 2026
Merged
Conversation
… on verified email
Author
|
close #132 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#132
Summary
Hardens authentication and account security across the API — closes the Phase 1 roadmap issue by enforcing verification, account status, roles, password policy, JWT handling, key rotation, and rate limits consistently, all in one coherent set of changes.
The 7 commits, in plain terms
JWT config, centralized — one place (src/config/jwt.ts) now owns signing/verification: fixed algorithm (HS256), required issuer/audience, and support for rotating signing keys via a kid header. No more silent fallback secret — the app refuses to boot without a real JWT_SECRET outside tests.
Auth middleware fixed — tokens are verified through that new config, and authorize() now checks a user's current role and account status from the database instead of trusting whatever the JWT claims. This closed a real gap: someone demoted or deactivated could previously keep employer-route access until their old token expired.
Password policy — passwords must now be 8+ chars with upper/lower/number/symbol, enforced at the schema level. Bcrypt cost factor is configurable and can be raised over time without a mass reset.
Controllers cleaned up — both auth and account controllers dropped their old hardcoded fallback JWT secret, switched to the shared hashing/signing helpers, and now transparently re-hash a user's password on login if it was stored at a weaker cost than current config.
Rate limits + verified-email gates — /register and /reset-password previously had no rate limiting at all; now they do. Reward withdrawals and all employer routes now require a verified email, not just a valid login.
Policy doc — docs/AUTH_POLICY.md lays out, in table form, exactly which account statuses can access what, which operations need email verification, and the rate-limit rules — this is the "verification evidence" the issue asked for.
Tests — new coverage for JWT rotation/rejection, persisted-role authorization (including the stale-JWT-role case), and password policy, plus updates to existing tests.
Scope and honesty notes
Explicitly not included: refresh-token rotation (blocked by issue #130, not implemented anywhere yet), a PIN policy (no PIN feature exists in the codebase), and an unrelated pre-existing stub in user.controller.ts.
Verified-email requirements (reward withdrawal, employer routes) were a judgment call since no such policy existed before — flagged for reviewers to weigh in on.
All of this has since been build-and-test verified locally (you ran npm run build and npx vitest run clean after we fixed the two follow-up bugs — the 500-vs-401 JWT issue and the stale employer.routes.test.ts), so the PR is in a mergeable state.