Remaining docs updated to match code (the docs that the first pass had not covered):
- Flows: Chat, Referral, Rating, Registration, Google OAuth, Negotiation, Payout,
Trezor Safekeeping — corrected endpoints, socket events, status enums, auth gaps
- API Reference: User API, Trezor API — admin route prefix/verb/status corrections,
added undocumented endpoints (ton-proof challenge, profile email verify,
GET /trezor/account, POST /trezor/verify-operation)
- Data Models: Chat, Notification, Payment, PointTransaction, User — corrected
enums (PaymentProvider, escrowState, PointTransaction.type, User.status),
90-day notification TTL, soft-delete semantics, wallet fields
Trezor "zero frontend" finding (audit C31/C32) corrected as STALE:
- Verified current code HAS a full frontend Trezor implementation (admin/trezor
page, TrezorSettingsView, trezorConnector via @trezor/connect-web,
TrezorSignDialog, actions/trezor.ts building the {message,signature} object)
- Fixed Trezor Safekeeping Flow doc (removed false "no frontend" warnings)
- Reclassified ISSUE-012 as invalid/superseded with explanation
Issue set reconciled to a single canonical numbering (ISSUE-001..054):
- Adopted the comprehensive 51-issue set (long-slug, fully indexed)
- Removed 35 superseded short-slug duplicates from the first pass
- Removed a duplicate ISSUE-046 file
- Added 3 issues the 51-set lacked: ISSUE-052 (completed-not-counted-in-stats),
ISSUE-053 (axios 401-only interceptor), ISSUE-054 (rate limiter counts all attempts)
- Regenerated Issues Index: 53 open (14 critical, 39 major) + 1 invalid
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
14 KiB
title, tags, related_models, related_apis
| title | tags | related_models | related_apis | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Registration Flow |
|
|
|
Registration Flow
Last updated: 2026-05-29 — aligned with code (see Doc vs Code Audit Report)
End-to-end specification for email + password registration with role selection (buyer/seller), six-digit email verification, optional referral code attribution, and terms acceptance.
Actors
- Prospective User – submits the sign-up form.
- Frontend –
frontend/src/auth/view/jwt/jwt-sign-up-view.tsx, callingsignUp()and laterverifyEmailWithCode()fromfrontend/src/auth/context/jwt/action.ts. - Backend –
AuthController.registerandAuthController.verifyEmailWithCodeinbackend/src/services/auth/authController.ts. - MongoDB –
TempVerificationcollection (temporary), thenUsercollection (final). - Email service –
backend/src/services/email/emailService.ts(SMTP/transactional provider) —sendVerificationCodeEmail(). - Socket.IO – emits
referral-signupto the referrer if a referral code is supplied.
Preconditions
- The email is not already a verified
User. If aTempVerificationalready exists, its code and metadata are regenerated and resent rather than throwing a conflict. - Outbound SMTP credentials are configured (
EMAIL_*env vars consumed byemailService.ts). - If a
referralCodeis supplied, it does not need to exist for sign-up to succeed — invalid codes are silently ignored at verification time.
State machine: TempVerification → User
stateDiagram-v2
[*] --> NotStarted
NotStarted --> TempCreated: POST /api/auth/register\nemail + role [+ ref]
TempCreated --> TempCreated: POST /api/auth/resend-verification\n(new code, 15-min TTL)
TempCreated --> TempExpired: 15 minutes elapse\nor verification fails
TempExpired --> TempCreated: User clicks "Resend"
TempCreated --> UserActive: POST /api/auth/verify-email-code\n(code + password)
UserActive --> [*]
note right of TempCreated
TempVerification document holds:
email, firstName, lastName, role,
referralCode, code, codeExpires
end note
note right of UserActive
User created with isEmailVerified=true,
status="active"; tokens issued immediately.
end note
Step-by-step narrative
Phase 1 — Submit registration
- User visits
/auth/jwt/sign-up(optionally with?ref=ABCD1234from the short-URL referral redirect implemented atbackend/src/app.ts:274-278). - User selects role (buyer or seller), enters email, password (held in client state only), accepts the Terms checkbox, and clicks "Create account".
[!bug] ⚠️ KNOWN BUG / quirk — the sign-up form does not collect the real password
jwt-sign-up-view.tsxonSubmitcallssignUp({ ..., password: '' })with a hard-coded empty string (jwt-sign-up-view.tsx:191, with the inline comment// You might need to add password field to form). So the actual password is not collected on the sign-up form at all — it is collected at the email-verification step (/verify-email-code). TheTempVerification.passwordfield is effectively unused (it is set to''and never read as a real credential). The credential that ends up on theUseris the one entered at verification.
- HTTP request:
POST /api/auth/registerwith{ email, password: '', firstName?, lastName?, role, referralCode? }. The frontend passespassword: ''(empty string) — see the quirk above. The controller persists this empty string intoTempVerification.password, which is never used as a real credential. - Validation middleware
registerValidation(authValidation.ts) checks email format, password complexity, and role enum. - Duplicate check (
authController.ts:55-64):User.findOne({ email })— if found, returns409 USER_EXISTS. - Idempotent temp record:
TempVerification.findOne({ email })— if present, the existing temp is updated in place (new name, role, referralCode, fresh 6-digit code, expiry pushed to now + 15 min). - Verification code:
authService.generateVerificationCode()(authService.ts:226-228) returns a uniformly random 6-digit string. - Persistence: A new
TempVerificationis saved with{ email, password: '', firstName: defaults to "کاربر", lastName: defaults to "جدید", role, referralCode, emailVerificationCode, emailVerificationCodeExpires }. - Email dispatch:
emailService.sendVerificationCodeEmail(email, firstName, code)is called. The email contains the 6-digit code, branding, and a 15-minute expiry notice. Failure to send is logged but the response still succeeds with201(the user can resend). - Response:
{ email, message: "Verification code sent to email" }with HTTP201for first-time,200for resend. - Frontend transitions to the OTP screen
/auth/jwt/verify?email=...(frontend/src/auth/view/jwt/jwt-verify-view.tsx).
Phase 2 — Verify code and finalise
- User enters the 6-digit code and confirms the password. The password may be re-entered here for safety.
- HTTP request:
POST /api/auth/verify-email-codewith{ email, code, password }. - Format guard:
authService.isValidVerificationCode(code)enforces/^\d{6}$/(authService.ts:236-238). - Lookup:
TempVerification.findOne({ email, emailVerificationCode: code, emailVerificationCodeExpires: { $gt: now } })— if any field mismatches or the code is older than 15 minutes, returns400. - Hash password:
bcrypt.hash(password, 12)viaauthService.hashPassword(). - Create
User(authController.ts:400-435):email,password: hashedPassword,firstName,lastName,role,isEmailVerified: true,status: "active". - Apply referral (
authController.ts:691-713):tempVerification.referralCode(stored on theTempVerificationdocument at registration and applied here at verification) is looked up viaUser.findOne({ referralCode }). If a referrer is found:user.referredBy = referrer._idreferrer.referralStats.totalReferrals += 1- Emit
referral-signuponuser-${referrer._id}Socket.IO room (authController.ts:704; the equivalent Google/other path emits atauthController.ts:1132) — see Referral Flow for the points-awarding side effect that happens later on the first purchase. - ⚠️ No self-referral guard: the code only checks
if (referrer)— it never comparesreferrer._idto the newly created user. A user who somehow signs up with their ownreferralCodewould be attributed as their own referrer.
- Persist user, then delete the TempVerification document (
findByIdAndDelete). - Token issuance: identical to Authentication Flow — generate access + refresh, push the refresh into
user.refreshTokens[]. - Response:
{ user, tokens: { accessToken, refreshToken } }. Frontend writes both intolocalStorage(action.ts:228-235) and routes the user into the appropriate dashboard (/dashboard/buyeror/dashboard/seller).
Sequence diagram
sequenceDiagram
autonumber
actor U as User
participant FE as Frontend
participant BE as Backend
participant DB as MongoDB
participant MAIL as Email Service
participant IO as Socket.IO
U->>FE: Fill sign-up form (email, role, ref?, password)
FE->>BE: POST /api/auth/register
BE->>DB: User.findOne({ email })
DB-->>BE: null
BE->>DB: TempVerification.findOne({ email })
DB-->>BE: null
BE->>BE: code = generateVerificationCode()
BE->>DB: TempVerification.create({...code, expires=+15m})
BE->>MAIL: sendVerificationCodeEmail(email, firstName, code)
MAIL-->>U: Email with 6-digit code
BE-->>FE: 201 { email, message }
FE-->>U: Redirect /auth/jwt/verify
U->>FE: Enter code + (re)password
FE->>BE: POST /api/auth/verify-email-code { email, code, password }
BE->>DB: TempVerification.findOne({ email, code, expires>now })
DB-->>BE: tempVerification doc
BE->>BE: hashPassword(password)
BE->>DB: User.create({...isEmailVerified:true, status:active})
opt referral present
BE->>DB: User.findOne({ referralCode })
DB-->>BE: referrer
BE->>DB: referrer.referralStats.totalReferrals += 1
BE->>IO: emit user-{refId} 'referral-signup'
end
BE->>DB: TempVerification.findByIdAndDelete(...)
BE->>BE: generate tokens
BE->>BE: push refresh
BE-->>FE: 200 { user, tokens }
FE->>FE: localStorage.setItem(accessToken, refreshToken)
FE-->>U: Redirect /dashboard/{role}
API calls
| Method | Endpoint | Source |
|---|---|---|
POST |
/api/auth/register |
authRoutes.ts:21 → authController.register |
POST |
/api/auth/verify-email-code |
authRoutes.ts:34 → authController.verifyEmailWithCode |
POST |
/api/auth/resend-verification |
authRoutes.ts:36-40 → authController.resendVerificationEmail |
GET |
/r/:code |
app.ts:274-278 — short-URL redirect that injects ?ref= into the sign-up page |
POST |
/api/auth/force-verify-user |
Dev-only — authController.forceVerifyUser (rejects outside NODE_ENV=development) |
Database writes
tempverificationscollection: insert on first POST (carryingemail,password: '',firstName,lastName,role,referralCode, code + expiry), in-place update on duplicate POST, delete on successful verification.userscollection: full insert on successful verification (authController.ts:680-688). The first refresh token is appended in the same save.userscollection (referrer):referralStats.totalReferralsincremented (authController.ts:699).
Socket events emitted
referral-signup→user-${referrerId}room when a referred user verifies. Payload:Source:{ userId, userName, userEmail, timestamp, totalReferrals }authController.ts:704-710(and:1132on the parallel path).
Side effects
- Email: one transactional message per
/registerand per/resend-verification. Content is generated byemailService.sendVerificationCodeEmail. Plain-text fallback included. - Sentry: errors during
User.createor email dispatch are captured server-side. - Logs: the controller
console.logs the generated code in all environments (authController.ts:88,:117,:518). Useful in dev; in prod the same log line ends up in CloudWatch/Sentry breadcrumbs. (Tracked as a hardening item.)
[!warning] Verification code is logged server-side The generated 6-digit code is
console.log-ed by the controller even in production. Anyone with log access can take over an unverified account. Move behindif (NODE_ENV !== 'production').
Error / edge cases
- Email already registered (verified) →
409 USER_EXISTS. - Email already in temp (unverified) →
200, code regenerated, email re-sent. User-friendly; no error. - Code mismatch / expired (>15 min) →
400 Invalid or expired verification code. The TempVerification is not deleted, so the user can request a new code via "Resend". - Code format wrong (non-digits or wrong length) →
400fromisValidVerificationCodeguard before DB lookup. - Email delivery failure → response still
201/200; the user can hit "Resend" or check spam. - Referral code that does not match any user → silently ignored; the user is still created with
referredBy: undefined. - Self-referral → not guarded. The referral attribution (
authController.ts:691-713) only checks that a referrer exists, never that it differs from the signing-up user. - Race condition: two parallel registrations for the same email → MongoDB unique index on
User.emailensures only one user document; the loser of the race seesE11000and returns409 USER_EXISTS. - Race condition: verify request arrives twice with the same code → second request finds no TempVerification and returns
400. The createdUseris the canonical record. - Role tampering → role is validated by
registerValidationenum (buyer | seller). Admin role is created only via the bootstrap seed (initializeAdminUserinapp.ts:377), never via this flow.
Defaults & quirks
firstName/lastNameare not required by the frontend in many sign-up variants; the controller defaults them to Persian placeholders"کاربر"/"جدید"(authController.ts:52-53). They can be edited later under/dashboard/account/profile.- The TempVerification TTL is enforced by the
emailVerificationCodeExpirescheck, not by a Mongo TTL index — expired docs remain in the collection until overwritten or manually purged.
Linked flows
- Authentication Flow — the next time the user signs in (includes the Telegram first-class auth section).
- Referral Flow — full points-awarding mechanics triggered here.
- Google OAuth Flow — alternative path that bypasses
TempVerification(Google identities are pre-verified). - Password Reset Flow — if the user forgets the password they set during verification.
[!tip] Telegram — zero-step registration Users who open the Amanat Telegram Mini App do not go through this flow at all.
POST /api/auth/telegramverifies the Telegram-signedinitDataand auto-provisions a newUser(no email,authProvider: "telegram") in a single round-trip. TheTempVerification+ email code cycle only applies to email-based sign-ups. See Authentication Flow#Telegram first-class auth flow.
Source files
- Backend:
backend/src/services/auth/authController.ts:33-158(register),:364-469(verify),:498-539(resend) - Backend:
backend/src/services/auth/authValidation.ts(validation rules) - Backend:
backend/src/models/TempVerification.ts(temp schema) - Backend:
backend/src/services/email/emailService.ts(sendVerificationCodeEmail) - Backend:
backend/src/app.ts:274-278(short referral redirect) - Frontend:
frontend/src/auth/view/jwt/jwt-sign-up-view.tsx - Frontend:
frontend/src/auth/view/jwt/jwt-verify-view.tsx - Frontend:
frontend/src/auth/context/jwt/action.ts:121-256