Skip to content

API — Authentication ​

Base path: /api/v1/auth. Auth is OTP-first with an optional password login. New users are created as Light Users on OTP verification and become Active Users on profile completion.

Endpoints ​

MethodPathNotes
POST/auth/send-otpSend OTP. Rate limited 5/min per phone.
POST/auth/verify-otpCreate-or-find User; issue JWT access + refresh pair.
POST/auth/firebase-verifyFirebase phone-auth fallback.
PATCH/auth/complete-profile{ name, state, district, role, password? } → profileComplete=true, assigns membership number.
POST/auth/set-passwordPost-completion password set/change.
POST/auth/reset-passwordOTP-gated password reset.
POST/auth/login-passwordPassword login (only if a passwordHash is set). Runs phone → device → ip limiters.
POST/auth/refresh{ refreshToken, deviceId } → new pair (rotates the refresh token).
POST/auth/logoutInvalidate the device's refresh token.
GET/auth/meCurrent user context.

User states ​

StateprofileCompleteCan doCannot do
Light UserfalseBrowse all leads, view products/profiles, dashboardPost leads, offer/bid, manage products, inquiries
Active UsertrueEverything—

JWT payload ​

ts
interface JWTPayload {
  userId: string
  profileComplete: boolean
  activeRole: string | null   // "farmer" | "trader" | null
  deviceId: string
}
  • Access token: 15 minutes.
  • Refresh token: 30 days, device-bound, rotated on every refresh (single-use with a grace window — see below).
  • Password: bcrypt, work factor 12, stored in User.passwordHash, never logged or returned.

Refresh & rotation ​

Each refresh issues a new refresh token and keeps the previous one valid for REFRESH_TOKEN_GRACE_MS (default 60s), so a client that rotated but lost the response (flaky network) can retry without being logged out. A successful fresh login clears the grace field.

Client behaviour (web axios interceptor):

  • Transient refresh failure (5xx / network / timeout) → keep the session, fail only that one request.
  • Terminal refresh failure (401 on /auth/refresh) → log out.
  • Cross-tab refresh is serialised with the Web Locks API so peers reuse the freshly-stored token instead of re-rotating.

Deactivated users ​

The authenticate middleware rejects a user whose kycStatus === DEACTIVATED with 401.

QA fast-path (non-production only) ​

When ENABLE_TEST_ENDPOINTS=true + TEST_ENDPOINT_SECRET are set, /api/v1/test/* lets automation read the current OTP, expire OTP/JWT, mint an instant session, and reset a fixture phone (guarded to TEST_OTP_PHONES only). Numbers in TEST_OTP_PHONES get the fixed OTP 123456, no SMS, and are rate-limit-exempt. Both locks must be unset in production.

Internal technical documentation — Cropto