9.9 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | requirements-completed | duration | completed | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 02-authentication-multi-tenancy | 03 | auth, mail |
|
|
|
|
|
|
|
|
|
5min | 2026-06-18 |
Phase 2 Plan 03: Password Reset & Mail Summary
Self-service password reset via email with single-use tokens, admin manual reset, force-password-change interceptor (D-06), and MailModule with MailHog for dev testing
Performance
- Duration: 5 min
- Started: 2026-06-18T11:43:25Z
- Completed: 2026-06-18T11:48:42Z
- Tasks: 2 (1 checkpoint:human-verify + 1 auto)
- Files modified: 20
Accomplishments
- MailModule with SMTP transport configurable via ENV variables, defaulting to MailHog for development
- Complete password reset flow: public request-reset endpoint (always 200, no enumeration), token-based reset with 1-hour expiry and single-use enforcement
- Admin password reset endpoint protected by ADMIN/SUPER_ADMIN roles (D-03)
- Change password for logged-in users with current password verification
- ForcePasswordChangeInterceptor as global APP_INTERCEPTOR blocks all API routes (except change-password, logout, me) when mustChangePassword=true, enforcing D-06 at the API level
- Frontend pages: reset-password request form, token-based reset form with 3-second redirect on success, change-password form with force-change notice
- MailHog added to docker-compose.dev.yml (SMTP on port 1025, web UI on port 8025)
- "Forgot password?" link added to login page
- Complete DE/EN i18n coverage for all new password flows
Task Commits
Each task was committed atomically:
- Task 1: Verify SUS-flagged packages - (checkpoint:human-verify, pre-approved by user)
- Task 2: MailModule, password reset flow, force-change interceptor, frontend pages -
ac617f4(feat)
Files Created/Modified
Created
apps/api/src/mail/mail.module.ts- NestJS MailerModule with SMTP transport from ENVapps/api/src/mail/mail.service.ts- Email sending: password reset (i18n) and welcome emailsapps/api/src/auth/dto/reset-password.dto.ts- RequestResetDto and ResetPasswordDto with validationapps/api/src/auth/dto/change-password.dto.ts- ChangePasswordDto with validationapps/api/src/auth/dto/admin-reset-password.dto.ts- AdminResetPasswordDto with validationapps/api/src/auth/interceptors/force-password-change.interceptor.ts- Global interceptor enforcing D-06apps/web/src/app/(auth)/reset-password/page.tsx- Password reset request formapps/web/src/app/(auth)/reset-password/[token]/page.tsx- Token-based password reset formapps/web/src/app/(portal)/change-password/page.tsx- Change password form (portal route group)
Modified
apps/api/src/auth/auth.service.ts- Added requestPasswordReset, resetPassword, changePassword, adminResetPassword methodsapps/api/src/auth/auth.controller.ts- Added 4 new endpoints: request-reset, reset-password, change-password, admin-reset-password/:userIdapps/api/src/auth/auth.module.ts- Imported MailModuleapps/api/src/app.module.ts- Imported MailModule, registered ForcePasswordChangeInterceptor as APP_INTERCEPTORapps/api/package.json- Added @nestjs-modules/mailer, nodemailer, ldapts, @types/nodemailerapps/web/src/app/(auth)/login/page.tsx- Added "Forgot password?" linkapps/web/src/messages/de.json- Added resetPassword and changePassword i18n keysapps/web/src/messages/en.json- Added resetPassword and changePassword i18n keysdocker-compose.yml- Added TESSERA_SMTP_* and TESSERA_APP_URL env vars to api servicedocker-compose.dev.yml- Added mailhog service and SMTP env vars for api
Decisions Made
- Plain text emails for MVP: No HTML email templates -- plain text emails with reset links are sufficient for MVP. HTML templates can be added later with handlebars adapter.
- Fire-and-forget email sending: MailService catches and logs send failures without throwing. The caller (requestPasswordReset) always returns success, both for error resilience and email enumeration prevention.
- ForcePasswordChangeInterceptor allows /auth/me: In addition to /auth/change-password and /auth/logout, the interceptor allows GET /auth/me so the frontend can detect the mustChangePassword flag and show appropriate UI.
- Admin reset defaults mustChangePassword to true: When an admin resets a user's password, the user is forced to change it on next login. This is configurable via the DTO.
- mustChangePassword included in JWT payload: Added to the login response and JWT token so the frontend middleware can detect it for client-side redirect.
Deviations from Plan
None - plan executed exactly as written.
Threat Model Coverage
| Threat ID | Status | Implementation |
|---|---|---|
| T-02-12 | Mitigated | requestPasswordReset always returns 200; MailService swallows errors |
| T-02-13 | Mitigated | UUID tokens, single-use (usedAt check), 1-hour expiry |
| T-02-14 | Mitigated | ForcePasswordChangeInterceptor as global APP_INTERCEPTOR; allowlist of exempt paths |
| T-02-15 | Mitigated | adminResetPassword endpoint requires ADMIN/SUPER_ADMIN role via @Roles decorator |
| T-02-SC | Mitigated | SUS packages verified by human before installation (checkpoint:human-verify) |
Issues Encountered
None - all packages installed cleanly, type-check passed on first attempt.
Known Stubs
None - all features are fully wired with data sources.
User Setup Required
SMTP configuration for production: The development setup uses MailHog (no config needed). For production, set these environment variables:
TESSERA_SMTP_HOST- SMTP server hostnameTESSERA_SMTP_PORT- SMTP port (587 for STARTTLS, 465 for SSL)TESSERA_SMTP_USER- SMTP authentication usernameTESSERA_SMTP_PASSWORD- SMTP authentication passwordTESSERA_SMTP_FROM- Sender email addressTESSERA_APP_URL- Application URL for reset links
For development, MailHog is automatically available at http://localhost:8025 when using docker-compose.dev.yml.
Next Phase Readiness
- Password reset and force-change flows are complete -- auth lifecycle is now fully implemented
- MailModule is reusable for future notification features
- ldapts package is installed and ready for Plan 02-04 (LDAP sync)
- Ready for Plan 02-05 (RLS + tenant management) or any subsequent plans
Self-Check: PASSED
All 9 created files verified on disk. Task commit (ac617f4) verified in git log. Type-check passes for all packages.
Phase: 02-authentication-multi-tenancy Completed: 2026-06-18