PRD: Authentication & account verification
| Module | User Management (CORE-01) | PRD ID | PRD-AUTH-001 |
| Status | Shipped | Owner | Identity squad |
| Date | 2026-05-25 | Version | v1.0 |
| Packages | @nx/identity · apps/client | URD | AUTH |
TL;DR
Lets every user prove ownership of their account and recover access on their own. People sign up and sign in with a username, email, or phone plus a password, verify their email and phone via one-time codes, and reset a forgotten password end-to-end - no support ticket required. The outcome: identifiers the rest of the platform can trust, and a self-service recovery path that keeps owners and staff out of lockout.
1. Context & Problem
User Management is the platform foundation - every other module trusts the identity and scope it issues. Sign-up and password-credential sign-in exist, but accounts cannot prove ownership of an email or phone, and a user who forgets their password has no self-service recovery path. Without verification, an email or phone identifier is just an unproven string; without a reset flow, a forgotten password means a hard lockout that only manual intervention can clear.
This increment closes those gaps on top of the existing sign-up / sign-in / password-hashing primitives: it adds OTP-driven email and phone verification, a forgot-password reset flow, identifier linking, and the client screens that consume them - delivering the verification and recovery requirements the AUTH feature lists as Must.
2. Goals & Non-Goals
Goals
- Sign-up that creates account, profile, identifiers, and default settings in one all-or-nothing operation, with the Owner role auto-assigned.
- Sign-in with any verified identifier (username / email / phone) + password, returning a scoped session token (roles, organization, merchant scope).
- Change password with the current password verified first; passwords always stored hashed.
- Verify email via OTP - request code → submit code → identifier marked verified.
- Verify phone via OTP using the same request/submit flow.
- Reset a forgotten password end-to-end - request reset → verify code → set a new hashed password.
- Link a verified email or phone to an already-authenticated account.
- Mail configurations and HTML templates seeded from the database so verification / reset mails are configurable, not hardcoded.
- Client screens for identifier verification and password reset.
Non-Goals
- OAuth / OAuth2 third-party login flow - scheme defined, login flow not built (URD-AUTH-014).
- Two-factor enforcement - credential scheme exists, enforcement not built (URD-AUTH-013).
- Session management and remote revocation - session tokens are stateless.
- User invitation via email or phone.
- Login-history / audit logging.
3. Success Metrics
| Metric | Target / signal |
|---|---|
| Verification completion | Share of email/phone identifiers that reach verified state via OTP trends up |
| Self-service recovery | Forgotten passwords resolved via the reset flow, not manual support |
| Sign-in integrity | Zero successful sign-ins on unverified or deactivated/blocked identifiers |
| Credential safety | 100% of stored passwords hashed; zero plain-text storage |
| OTP reliability | OTP delivery success rate; expired/max-attempt errors handled gracefully |
4. Personas & Use Cases
| Persona | Goal in this feature |
|---|---|
| Owner | Sign up, secure the account, verify email/phone, recover a lost password |
| Employee / Cashier | Sign in with own credentials, change or reset their password |
| Customer | Verify an identifier and recover access to their account |
Core scenarios: sign up (account + profile + identifiers + settings, Owner role) → verify email/phone via OTP → sign in with a verified identifier for a scoped token → change password, or - if forgotten - request a reset, verify the code, and set a new one → optionally link an additional verified email or phone.
5. User Stories
- As a new user, I want to sign up with a username and password in one step, so my account, profile, identifiers, and default settings are created together.
- As a user, I want to verify my email via a one-time code, so the platform trusts that identifier and I can sign in with it.
- As a user, I want to verify my phone via the same OTP flow, so my phone becomes a usable, trusted identifier.
- As a user, I want to sign in with any verified identifier plus my password, so I receive a session token carrying my roles and scope.
- As a user, I want to change my password after confirming my current one, so I can rotate credentials safely.
- As a user who forgot my password, I want to request a reset, verify a code, and set a new password, so I can recover access without support.
- As an authenticated user, I want to link a verified email or phone to my account, so I have more ways to sign in and recover.
6. Functional Requirements
| # | Requirement | URD ref |
|---|---|---|
| FR-1 | Sign up with username + password; account, profile, identifiers, and default settings created in one all-or-nothing operation, Owner role assigned | URD-AUTH-001..003 |
| FR-2 | Sign in with any verified identifier (username / email / phone) + password; returns a session token carrying roles and organization / merchant scope | URD-AUTH-004..005 |
| FR-3 | Passwords are securely hashed before storage and never stored in plain text | URD-AUTH-006 |
| FR-4 | Change password with the current password verified first | URD-AUTH-007 |
| FR-5 | Verify email via OTP: request code → submit code → identifier marked verified | URD-AUTH-008 |
| FR-6 | Verify phone via OTP using the same request/submit flow | URD-AUTH-009 |
| FR-7 | Reset a forgotten password: request reset → verify code → set a new hashed password | URD-AUTH-010 |
| FR-8 | Link a verified email or phone to an already-authenticated account | URD-AUTH-011 |
| FR-9 | Verified identifiers participate in the global uniqueness check (one verified email/phone per user) | URD-AUTH-008..009 |
| FR-10 | Mail configurations and HTML templates are seeded from the database, so verification / reset mails are configurable | URD-AUTH-008..010 |
Full requirement text and acceptance criteria live in the User Management URD. This PRD references them rather than restating them.
7. Non-Functional Requirements
| Area | Requirement |
|---|---|
| Data integrity | Sign-up is atomic - if any step fails, nothing is created (account, profile, identifiers, settings) |
| Credential safety | Passwords hashed before storage; reset sets a hashed password; reset response never carries a session token |
| Tenancy & authz | Session token carries roles and organization / merchant scope; all operations except sign-in / sign-up require authentication |
| OTP robustness | OTP codes expire and cap retry attempts; expired-code and max-attempt cases return explicit errors |
| Statelessness | Session tokens are stateless - permission changes take effect on next sign-in |
| Configurability | Mail content driven by DB-seeded configurations and HTML templates, not hardcoded strings |
| i18n | User-facing labels, statuses, and mail content are bilingual ({ en, vi }) |
8. UX & Flows
Key screens (in apps/client): a verify page that consumes the OTP request/submit endpoints, and a forgot-password / reset-password page that drives the reset flow.
9. Data & Domain
| Entity | Role |
|---|---|
User | The authenticated identity that owns credentials, identifiers, profile, and settings |
Credential | The hashed password secret used to authenticate |
Identifier | A login value (username / email / phone), globally unique within its type, carrying a verified flag |
| OTP session | Short-lived state backing verification and reset (request → submit, with expiry and attempt caps) |
| Mail configuration + template | DB-seeded mail config and HTML templates for verification / reset mails |
Conceptual only - full schema and invariants in the identity domain model.
10. Dependencies & Assumptions
Depends on
- User Account (URD-USR) - users, identifiers, and the
verifiedlifecycle this feature drives. - Roles & Scoping (URD-ROLE) - sign-up assigns the Owner role; sign-in resolves roles and scope into the token.
- User Configuration (URD-CFG) - default settings created during the atomic sign-up.
- Mail delivery for OTP and reset codes;
apps/clientfor the verify and reset screens.
Assumptions
- An identifier (email/phone) is reachable so the OTP can be delivered.
- Mail configurations and templates are seeded before verification / reset mails are sent.
- Username is required and automatically considered verified; email and phone start unverified.
11. Risks & Open Questions
| Risk / question | Mitigation / status |
|---|---|
| Partial sign-up could leave orphaned account fragments | Sign-up is all-or-nothing; any failure rolls everything back |
| OTP brute-force / replay | Codes expire and cap attempts; expired/max-attempt errors are explicit |
| Reset response leaking a session token | Session token removed from the reset-password response |
| Email verification on sign-up currently disabled in code | Known gap - fresh sign-up may need a separate verify flow before email sign-in; see developer docs |
| No 2FA enforcement / OAuth login | Out of scope this increment; credential scheme exists, enforcement/login flow planned |
12. Release Plan & Launch Criteria
| Aspect | Plan |
|---|---|
| Phase | P1 (foundation) - see URD feature catalog |
| Rollout | All users; no feature flag |
| Migration | Seed mail configurations and HTML templates; add optional verified fields to user schemas |
| Launch criteria | Email + phone OTP verify end-to-end; forgot-password reset end-to-end; sign-in rejects unverified / deactivated identifiers; passwords stored hashed |
| Monitoring | OTP delivery + verification success rate, reset-flow completion, sign-in failure reasons (unverified / bad password / blocked) |
13. FAQ
Can I sign in with an unverified email or phone? No - sign-in requires a verified identifier. Username is automatically verified; email and phone must pass OTP first.
How does the OTP flow work? Request a code for your email or phone (a session token is returned), then submit the code; on success the identifier is marked verified. Codes expire and limit attempts.
What happens if I forget my password? Request a reset, verify the emailed/texted code, then set a new password - it is hashed before storage, and the reset response never returns a session token.
Where do the verification and reset mails come from? Mail configurations and HTML templates are seeded from the database, so content is configurable rather than hardcoded.
Is OAuth or two-factor supported? Not in this increment - both schemes are defined but their login flow / enforcement is planned, not built.
References
- URD: User Management - Authentication - AUTH requirements + acceptance
- Builds on: User Account · Roles & Scoping · User Configuration
- Related PRD: Employee Management · User Account & Configuration
- Module: User Management - overview + traceability
- Developer: @nx/identity · domain model