Skip to content

Identity & Authentication (SRS-AUTH)

Purpose

Owns everything about who a user is: registration, login, verification, credentials, sessions and devices, profile data, the RBAC model, and account lifecycle (suspension, deletion, data export). It does not own vendor business verification (that is Vendor Onboarding) or per-module permissions logic beyond exposing roles/permissions for other modules to enforce.

One identity system serves all three user types — Customer, Vendor (staff of a vendor business), Admin — as a shared engine. A single person MAY hold both a customer account and a vendor role under the same email.

Actors

Actor Description
Guest Unauthenticated visitor (can browse marketplace, cannot transact).
Customer Registered end user planning events.
Vendor user A person attached to a vendor business (Owner role at MVP; more roles at Scale).
Admin Yam3at staff with an admin role (Super Admin, Operations, Support, Finance, Content Moderator).
System Scheduled jobs (token cleanup, deletion grace expiry).

User stories

  • As a customer, I want to sign up with my phone number and an OTP, so that I can start planning without remembering another password.
  • As a customer, I want to log in with Google or Apple, so that registration takes seconds.
  • As a vendor owner, I want a separate vendor registration path, so that my business account is distinct from my personal customer account.
  • As a user, I want to see and revoke my active sessions and devices, so that I can secure my account if my phone is lost.
  • As a user, I want to reset a forgotten password via email or SMS, so that I am never locked out.
  • As an admin, I want to suspend an account and force-logout all its sessions, so that abuse stops immediately.
  • As a user, I want to export my data and delete my account, so that I control my personal information.
  • As a security-conscious user, I want to enable 2FA, so that a stolen password alone cannot compromise my account. (Scale)

Functional requirements

Registration

ID Requirement Phase
SRS-AUTH-001 The system MUST support customer registration with email + password (fields: first_name, last_name, email, password, optional phone, preferred_locale ar/en, mandatory acceptance of Terms & Privacy with version recorded). MVP
SRS-AUTH-002 The system MUST support customer registration/login with phone OTP only (passwordless): user enters a Kuwait mobile number (+965, 8 digits, starting 5/6/9), receives a 6-digit SMS OTP, and on first successful verification an account is created with phone_verified_at set. MVP
SRS-AUTH-003 The system MUST support Google Sign-In and Sign in with Apple (OIDC). On first social login, an account is created with the provider's verified email; email_verified_at is set immediately when the provider asserts a verified email. Apple private-relay emails MUST be accepted. MVP
SRS-AUTH-004 If a social login's email matches an existing email/password account, the system MUST link the provider to that account only after the user re-authenticates with their password (no silent account takeover). MVP
SRS-AUTH-005 Vendor registration MUST start with the same identity registration (any method above) and then branch into the vendor business application (SRS-VOS-001). Identity creation and business onboarding are separate steps with separate states. MVP
SRS-AUTH-006 Admin accounts MUST NOT be self-registered. They are created by a Super Admin only, require email verification, and password login (2FA mandatory for admins at Scale). MVP
SRS-AUTH-007 Registration MUST be idempotent per identifier: attempting to register an existing email/phone returns a generic "check your inbox/SMS" style response without disclosing whether the account exists (anti-enumeration). MVP

Verification

ID Requirement Phase
SRS-AUTH-010 Email verification: on email registration or email change, send a signed verification link valid 24 h, single-use. Unverified emails may log in but cannot transact (create RFQs, book, pay, publish listings). MVP
SRS-AUTH-011 Phone verification: 6-digit numeric OTP via SMS, valid 5 min, max 5 attempts per code, resend cooldown 60 s, max 5 sends per phone per hour and 10 per day. Codes are stored hashed. MVP
SRS-AUTH-012 A verified phone number is mandatory before a customer can submit an RFQ or booking, and before a vendor application can be submitted. MVP
SRS-AUTH-013 Changing email or phone requires verifying the new identifier before it replaces the old one; the old identifier receives a notification with a revert link valid 72 h. MVP

Login & credentials

ID Requirement Phase
SRS-AUTH-020 Password login with email; generic error "invalid credentials" on failure (no field-level disclosure). MVP
SRS-AUTH-021 Brute-force protection: after 5 failed attempts per account or per IP within 15 min, apply exponential backoff and CAPTCHA; after 10, lock login for 15 min and notify the account email. MVP
SRS-AUTH-022 Forgot password: email link (signed token, 60 min validity, single-use) or, if the account has a verified phone, SMS OTP path. Successful reset revokes all sessions except the one completing the reset, and sends a notification. MVP
SRS-AUTH-023 Password change (while logged in) requires the current password and revokes all other sessions. MVP
SRS-AUTH-024 Passwords hashed with Argon2id. Compromised-password screening against a k-anonymity breach corpus on set/change. MVP
SRS-AUTH-025 Mobile apps MUST offer biometric unlock (Face ID / fingerprint) as a device-local convenience wrapper around the refresh token — biometrics never replace server-side authentication. MVP
SRS-AUTH-026 2FA (TOTP app + SMS fallback): opt-in for customers/vendors, enforced for admins. Recovery codes (10, single-use) issued at enrollment. Scale

Sessions & devices

ID Requirement Phase
SRS-AUTH-030 Token model: short-lived access token (15 min) + rotating refresh token (60 days absolute, revocable). Web uses httpOnly secure cookies; mobile uses secure storage. Refresh token reuse detection revokes the whole token family. MVP
SRS-AUTH-031 Users MUST be able to list active sessions/devices (device name, platform, app version, IP-derived city, last active) and revoke any single session or all others. MVP
SRS-AUTH-032 Admin suspension of an account MUST revoke all its sessions and push tokens within 60 s. MVP
SRS-AUTH-033 Each device registers its FCM push token bound to the session; revoking the session detaches the token. MVP

Profile management

ID Requirement Phase
SRS-AUTH-040 Customer profile: first_name, last_name, email, phone, avatar (image ≤ 5 MB, JPEG/PNG/WebP, stored at 512×512), preferred_locale (ar/en), country (default KW), notification_preferences per channel per category. Civil ID is not collected from customers. MVP
SRS-AUTH-041 Locale preference controls UI language, notification language, and digit formatting; changeable at any time with immediate effect. MVP
SRS-AUTH-042 Vendor users see a profile switcher when they hold both a customer profile and one or more vendor memberships; the active context is explicit in the UI and in the JWT (active_context). MVP

Roles & permissions (RBAC)

ID Requirement Phase
SRS-AUTH-050 RBAC model: usersrolespermissions. Permissions are fine-grained strings (rfq.create, listing.publish, vendor.approve, refund.issue, …). Roles are named permission bundles. Enforcement is server-side on every endpoint; UI hiding is cosmetic only. MVP
SRS-AUTH-051 Built-in roles at MVP: customer, vendor_owner, admin_super, admin_operations, admin_support, admin_finance, admin_moderator. Vendor team roles (vendor_manager, vendor_staff) are Scale (SRS-VOS-060). MVP
SRS-AUTH-052 Vendor permissions are scoped to a vendor_id (membership table), and at Scale additionally to branch_id. A vendor user can never act on another vendor's data. MVP
SRS-AUTH-053 All role/permission grants and revocations are audit-logged with actor and reason. Super Admin role changes require a second Super Admin's confirmation. MVP

Account lifecycle, deletion & export

ID Requirement Phase
SRS-AUTH-060 Account statuses: pending_verification, active, suspended, deletion_requested, anonymized (see state machine). MVP
SRS-AUTH-061 Self-service account deletion: re-authentication required; a 30-day grace period starts during which login cancels the deletion. After grace expiry, personal data is anonymized (name → "Deleted user", email/phone/avatar purged) while transactional records (bookings, payments, invoices, reviews content) are retained for legal/financial integrity, unlinked from identity. MVP
SRS-AUTH-062 Deletion of a vendor_owner account is blocked while the vendor business has active bookings or an active paid subscription; the user is told exactly which blockers exist. MVP
SRS-AUTH-063 Data export: on request, generate a machine-readable archive (JSON + attached files) of profile, events, RFQs, quotes, bookings, invoices, messages, and reviews. Delivered via signed download link (72 h validity) within ≤ 72 h of request; rate-limited to 1 export per 7 days. MVP
SRS-AUTH-064 Suspension (admin action, reason mandatory) blocks login and API access but preserves all data; the user sees a dedicated "account suspended — contact support" screen. MVP

Business rules

  1. One email and one phone number map to at most one identity each. Cross-type reuse (customer + vendor) happens through roles on one identity, not duplicate accounts.
  2. Unverified accounts can browse and save favorites, but every money- or communication-adjacent action requires the relevant verification (SRS-AUTH-010/012).
  3. Social-only accounts (no password) set a password via the forgot-password flow if they want email+password login.
  4. OTP SMS sender ID is "Yam3at"; message templates exist in AR and EN and follow the account's preferred_locale (default AR for +965).
  5. All auth events (register, login success/failure, logout, password change/reset, verification, session revoke, 2FA change, suspension, deletion request) are audit-logged.

Validation rules

Field Rule
email RFC 5322 syntactically valid, ≤ 254 chars, lowercased for uniqueness, disposable-domain blocklist.
password 8–128 chars, must not appear in breach corpus, no composition rules beyond length (NIST 800-63B), Unicode allowed.
phone E.164. Kuwait: ^\+965[569]\d{7}$. Other GCC country codes accepted but marked unsupported-market until expansion.
first_name, last_name 1–50 chars each, Arabic and Latin letters, spaces, hyphen, apostrophe.
OTP code Exactly 6 digits; constant-time comparison of hash.
avatar JPEG/PNG/WebP, ≤ 5 MB, min 200×200 px; EXIF stripped.
preferred_locale ar or en.
Terms acceptance Boolean must be true; terms_version recorded.

Permissions matrix

Action Guest Customer Vendor user Admin (Support) Admin (Super)
Register / login
Manage own profile
List/revoke own sessions
View any user's profile (PII) ✔ (read)
Suspend / reinstate account
Create admin users, assign roles
Request own deletion / export
Execute deletion on behalf of user ✔ (verified request)

Status / state machine

stateDiagram-v2
    [*] --> PendingVerification: register
    PendingVerification --> Active: verify email or phone
    Active --> Suspended: admin suspends
    Suspended --> Active: admin reinstates
    Active --> DeletionRequested: user requests deletion
    DeletionRequested --> Active: user logs in / cancels (within 30 days)
    DeletionRequested --> Anonymized: grace period expires (system job)
    Suspended --> Anonymized: admin-executed verified erasure
    Anonymized --> [*]

Edge cases

  • Apple private relay + later Google login: emails differ → two identities. Resolution: manual account-merge via support (Scale: self-service merge). Never auto-merge on name similarity.
  • Phone number recycled by telecom operator: new owner OTP-registers a number attached to an old account → if the old account has been inactive ≥ 12 months, prompt "is this a new account?" flow that detaches the number after email-based challenge to the old account; otherwise block and route to support.
  • OTP SMS not delivered: after 2 failed sends, offer voice-call OTP fallback (Scale) and email-based path if the account has a verified email.
  • Clock skew on mobile: token expiry validated server-side only; client uses server time from response headers.
  • User deletes account with an upcoming confirmed booking: blocked with an explicit list of blocking bookings; must cancel per policy first.
  • Concurrent password reset and login: completing a reset invalidates all outstanding reset tokens and other sessions atomically.
  • Vendor owner is the vendor's only user and requests deletion: see SRS-AUTH-062; support-assisted business transfer is the escape hatch.

Empty / loading / error states

Screen Empty Loading Error
Login n/a Button spinner, inputs disabled, no double-submit Generic invalid-credentials message; lockout message shows remaining minutes
OTP entry 6 empty boxes, auto-focus first Auto-verify on 6th digit with inline spinner "Code incorrect or expired" + resend countdown; attempts remaining after 3rd failure
Sessions list "This is your only active session" Skeleton rows ×3 Retry banner; revoke failures roll back optimistic UI
Profile Never empty (registration data) Skeleton form Field-level validation errors in AR/EN; save is atomic
Data export "No export requested yet" + CTA "Preparing your archive…" progress state Failure notification with retry; support link

Acceptance criteria

  • A user can register and reach a transact-ready state via each of: email+password, phone OTP, Google, Apple — in both AR (RTL) and EN.
  • OTP limits verified: 5 attempts/code, 60 s resend cooldown, 5/h and 10/day send caps; exceeding caps returns a friendly bilingual error.
  • Social login onto an existing email requires password re-auth before linking.
  • Password reset revokes other sessions and notifies the user.
  • Session list shows every active device; "log out all other devices" works within 60 s across web and mobile.
  • Suspended account: all tokens dead ≤ 60 s, push tokens detached, suspension screen shown.
  • Deletion: 30-day grace honored; post-expiry the profile is anonymized while bookings/invoices remain queryable by admins; export archive contains all listed data classes.
  • Every table above's audit events appear in the audit log with actor, IP, and diff.
  • Anti-enumeration verified: register/forgot-password responses identical for existing vs. non-existing identifiers.

Data entities touched

users, user_auth_providers, user_sessions, user_devices (push tokens), otp_codes, password_resets, roles, permissions, role_user (with vendor_id scope), account_deletion_requests, data_export_requests, audit_logs.

POST   /api/v1/auth/register
POST   /api/v1/auth/login
POST   /api/v1/auth/otp/send
POST   /api/v1/auth/otp/verify
POST   /api/v1/auth/social/{provider}/callback      (google | apple)
POST   /api/v1/auth/refresh
POST   /api/v1/auth/logout
POST   /api/v1/auth/password/forgot
POST   /api/v1/auth/password/reset
PUT    /api/v1/auth/password
POST   /api/v1/auth/email/verify
POST   /api/v1/auth/phone/verify
GET    /api/v1/me
PATCH  /api/v1/me
GET    /api/v1/me/sessions
DELETE /api/v1/me/sessions/{sessionId}
DELETE /api/v1/me/sessions                          (all others)
POST   /api/v1/me/deletion-request
DELETE /api/v1/me/deletion-request
POST   /api/v1/me/export
GET    /api/v1/admin/users
PATCH  /api/v1/admin/users/{userId}/status
POST   /api/v1/admin/users/{userId}/roles