12 KiB
title, tags
| title | tags | |||
|---|---|---|---|---|
| Authentication API |
|
Authentication API
All endpoints are mounted under /api/auth/* in backend/src/app.ts. The routes file is backend/src/services/auth/authRoutes.ts and the WebAuthn sub-routes are in passkeyRoutes.ts. Controller logic lives in authController.ts and authService.ts.
Two distinct identities are involved: a User (models/User.ts) and a TempVerification document that holds pending registration data until the email code is confirmed. Tokens are signed JWTs (access + refresh) created in authService. See Authentication Flow for the high-level lifecycle diagram.
Registration
POST /api/auth/register
Description: Start a new registration. Creates a TempVerification document and emails an 8-digit verification code. The actual User is only created once the code is verified. Auth required: No Request body:
{
email: string;
firstName?: string; // default "کاربر"
lastName?: string; // default "جدید"
role?: "buyer" | "seller"; // default "buyer"
password?: string; // accepted now or at verify-email-code time
referralCode?: string; // optional, links to referrer for [[Points API]]
}
Response 200:
{ "success": true, "message": "Verification code sent ...", "data": { "email": "x@y.z" } }
Errors: 400 validation, 409 USER_EXISTS if email already registered.
Side effects:
- Upserts a
TempVerificationrow. - Sends
emailService.sendVerificationCodeEmail. - No socket emission yet (user does not exist).
Source:
authController.register
POST /api/auth/verify-email-code
Description: Confirms the registration code. Creates the User, deletes the TempVerification, processes any referral, issues JWT tokens, and starts the session in Redis.
Auth required: No
Request body:
{
email: string;
code: string; // 8 digits
password?: string; // required if not provided at register
}
Response 200:
{
"success": true,
"message": "Email verified",
"data": {
"user": { "_id": "...", "email": "...", "role": "buyer", ... },
"tokens": { "accessToken": "...", "refreshToken": "..." }
}
}
Errors: 400 invalid/expired code, 404 no pending verification, 409 email already taken.
Side effects:
- Creates User, deletes
TempVerification. - Calls Points API referral hook when
referralCodewas supplied (emitsreferral-signuptouser-<referrerId>). - Stores refresh token in
user.refreshTokens. - Welcome email via
emailService.sendWelcomeEmail.
GET /api/auth/verify-email/:token
Description: Legacy URL-based verification (link in email). Marks isEmailVerified=true.
Auth required: No
Response 200: { "success": true, "message": "Email verified successfully" }
Errors: 400 invalid/expired token.
POST /api/auth/resend-verification
Description: Re-issues the 8-digit code for a pending or unverified user.
Auth required: No
Request body: { email: string }
Response 200: { "success": true, "message": "Verification code resent" }
Errors: 400 invalid email, 404 no user/temp record, 429 rate-limited (Redis).
POST /api/auth/force-verify-user
Description: Development-only helper to mark a user verified without going through email. Auth required: No (intended for dev only — gate with env in prod)
Login & sessions
POST /api/auth/login
Description: Email/password login. Validates credentials with bcrypt, signs JWT pair, stores refresh token on user, returns sanitized user object. Auth required: No Request body:
{
email: string;
password: string;
}
Response 200:
{
"success": true,
"message": "Login successful",
"data": {
"user": { "_id": "...", "email": "...", "role": "buyer", "firstName": "..." },
"tokens": { "accessToken": "...", "refreshToken": "..." }
}
}
Errors:
400validation401invalid credentials403email not verified423account locked (after repeated failures, tracked in Redis viarateLimitService) Side effects:- Updates
user.lastLoginAt. - Pushes refresh token onto
user.refreshTokens. - Redis session start via
sessionService.
POST /api/auth/telegram
Description: First-class Telegram authentication. Accepts Telegram Mini App initData or a Telegram Login Widget payload, verifies the Telegram signature server-side, and signs the user into Amanat without requiring email or password.
Auth required: No
Request body:
// Mini App
{ initData: string; role?: "buyer" | "seller" }
// Login Widget
{ loginWidget: { id: string; first_name?: string; username?: string; auth_date: string; hash: string }; role?: "buyer" | "seller" }
Response 200/201:
{
"success": true,
"data": {
"user": { "_id": "...", "authProvider": "telegram", "telegramVerified": true },
"tokens": { "accessToken": "...", "refreshToken": "..." },
"isNewUser": true,
"telegram": { "userId": "10001", "username": "alice", "source": "miniapp" }
}
}
Errors: 400 missing payload, 401 invalid/stale signature, 403 blocked Telegram account or inactive Amanat account, 409 TELEGRAM_REPLAY reused Mini App initData, 429 rate-limited.
Side effects:
- Creates a Telegram-only User when no active
TelegramLinkexists. The user has no email,authProvider: "telegram", andtelegramVerified: true. - Upserts
TelegramLinkfor the Telegram ID and updates last-seen metadata. - Stores the refresh token on the user document.
- Does not expose phone numbers; Telegram phone data is not requested or persisted.
POST /api/auth/refresh-token
Description: Exchanges a refresh token for a new access token. Rotates the refresh token.
Auth required: No (refresh token in body)
Request body: { refreshToken: string }
Response 200: { "success": true, "data": { "tokens": { "accessToken": "...", "refreshToken": "..." } } }
Errors: 401 token expired / not present in user record, 403 user disabled.
POST /api/auth/logout
Description: Removes the current refresh token from the user record and clears the Redis session.
Auth required: Bearer JWT
Response 200: { "success": true, "message": "Logged out" }
Side effects: redisService session removed.
Google OAuth
POST /api/auth/google/signup
Description: Verifies a Google ID token, creates a new User (no password), optionally links a referral, returns JWT tokens. Auth required: No Request body:
{
googleToken: string;
role?: "buyer" | "seller";
referralCode?: string;
}
Response 200: Same shape as verify-email-code response.
Errors: 400 invalid Google token, 409 email already registered (suggest sign-in instead).
POST /api/auth/google/signin
Description: Verifies a Google ID token and signs in an existing user. Will not create a new account.
Auth required: No
Request body: { googleToken: string }
Response 200: { success, data: { user, tokens } }
Errors: 400 invalid token, 404 no user with that Google email.
Passkey / WebAuthn
Routes are nested under /api/auth/ via passkeyRoutes. Service: passkeyService.ts.
POST /api/auth/passkey/authenticate/challenge
Description: Generates a sign-in challenge that the browser/authenticator will sign. No userId required — the assertion will identify the user.
Auth required: No
Response 200: { "success": true, "challenge": { /* PublicKeyCredentialRequestOptions */ } }
POST /api/auth/passkey/authenticate
Description: Verifies the WebAuthn assertion and, on success, returns a JWT pair.
Auth required: No
Request body: { challenge, assertion } (assertion is the browser's navigator.credentials.get() output).
Response 200: { "success": true, "userId": "...", "user": { ... }, "tokens": { ... } }
Errors: 400 missing fields, 404 Passkey not found, 500 verification error.
POST /api/auth/passkey/register/challenge
Description: Generates a registration challenge for the authenticated user.
Auth required: Bearer JWT
Response 200: { "success": true, "challenge": { /* PublicKeyCredentialCreationOptions */ } }
POST /api/auth/passkey/register
Description: Verifies a new passkey registration and stores the credential on the user.
Auth required: Bearer JWT
Request body: { challenge, credential }
Response 200: { "success": true, "message": "Passkey registered successfully" }
GET /api/auth/passkey/list
Description: Returns the calling user's registered passkeys (id, label, created date).
Auth required: Bearer JWT
Response 200: { "success": true, "passkeys": [...] }
DELETE /api/auth/passkey/:passkeyId
Description: Removes a passkey by id.
Auth required: Bearer JWT
Response 200: { "success": true, "message": "Passkey removed successfully" }
Password management
POST /api/auth/request-password-reset
Description: Generates a reset token, stores it on the user, and emails a reset link plus a numeric code.
Auth required: No
Request body: { email: string }
Response 200: { "success": true, "message": "Password reset email sent" } (always returns success to avoid email enumeration).
Side effects: emailService.sendPasswordResetEmail; rate-limited per IP via Redis.
POST /api/auth/reset-password
Description: Sets a new password using a token from the reset email. Wipes refresh tokens. Auth required: No Request body:
{
token: string;
password: string; // 6+ chars, mixed case + digit
}
Response 200: { "success": true, "message": "Password updated" }
Errors: 400 invalid/expired token or weak password.
POST /api/auth/reset-password-with-code
Description: Alternative reset flow using a numeric code instead of a tokenised URL.
Auth required: No
Request body: { email, code, password }
Response 200: { "success": true }
POST /api/auth/change-password
Description: Authenticated password change. Auth required: Bearer JWT Request body:
{
currentPassword: string;
newPassword: string; // 6+ chars, mixed case + digit
}
Response 200: { "success": true, "message": "Password updated" }
Errors: 400 validation, 401 wrong current password.
Side effects: Clears user.refreshTokens (forces re-login on other devices).
Current user / profile
GET /api/auth/profile
Description: Returns the full sanitized User document for the caller.
Auth required: Bearer JWT
Response 200: { "success": true, "data": { /* User */ } }
PUT /api/auth/profile (and POST /api/auth/update-profile)
Description: Updates the caller's profile (first/last name, phone, bio, website, language/currency preferences).
Auth required: Bearer JWT
Request body: Partial profile, see updateProfileValidation:
{
firstName?: string; // 2-50
lastName?: string; // 2-50
profile?: {
phone?: string; // E.164-ish
bio?: string; // <=500
website?: string; // URL
};
preferences?: {
language?: "en" | "fa" | "ar";
currency?: "USD" | "EUR" | "IRR" | "AED";
};
}
Response 200: Updated user.
Errors: 400 validation.
Account deletion
DELETE /api/auth/account
Description: Permanently deletes the caller's account after re-authenticating with password.
Auth required: Bearer JWT
Request body: { password: string }
Response 200: { "success": true, "message": "Account deleted" }
Errors: 401 bad password.
Side effects: Removes User document, clears Redis session, cascades configured by dataCleanupService.
Error codes summary
| HTTP | App code | Meaning |
|---|---|---|
| 400 | Validation Error |
express-validator rejected the body |
| 401 | — | Bad credentials / missing token |
| 403 | — | Email not verified or insufficient role |
| 409 | USER_EXISTS |
Email already in use |
| 423 | — | Account temporarily locked after failed logins |
See Error Codes for the global error shape.