Skip to content

[Backend] Implement Phone Number Verification via SMS OTP #500

@llinsss

Description

@llinsss

⚠️ This is a backend issue — work is done inside the backend/ folder

Description

The User entity (backend/src/modules/users/entities/user.entity.ts) has phone and phoneVerified columns, and the sms module (backend/src/modules/sms/) has a fully wired Twilio integration. However, there is no OTP flow to actually verify a user's phone number. phoneVerified is never set to true anywhere in the codebase.

Current State

  • User.phone and User.phoneVerified columns exist but phoneVerified is never updated
  • SmsService.sendSms() works and is tested
  • No OTP generation, storage, or verification logic exists
  • No endpoints for phone verification exist in UsersController or AuthController

What Needs to Be Built

1. OTP Storage

  • Store OTPs in Redis (via the existing RedisService in backend/src/auth/services/redis.service.ts)
  • Key: phone_otp:{userId}, TTL: 10 minutes
  • Value: hashed OTP (bcrypt or SHA-256)

2. New Endpoints in UsersController

POST /users/phone/send-otp
Body: { phone: string }
  • Validate E.164 phone format
  • Generate 6-digit OTP
  • Store hashed OTP in Redis with 10-min TTL
  • Send via SmsService
  • Rate-limit: max 3 requests per 15 minutes per user
POST /users/phone/verify-otp
Body: { otp: string }
  • Retrieve hashed OTP from Redis
  • Compare with submitted OTP (timing-safe)
  • On match: set user.phoneVerified = true, delete Redis key
  • On mismatch: increment attempt counter; lock after 5 failed attempts

3. DTOs

export class SendPhoneOtpDto {
  @IsPhoneNumber() phone: string;
}
export class VerifyPhoneOtpDto {
  @IsString() @Length(6, 6) otp: string;
}

Acceptance Criteria

  • OTP is sent via SMS and expires after 10 minutes
  • Correct OTP sets phoneVerified = true
  • Incorrect OTP returns 400; account locks after 5 failed attempts
  • Rate limiting prevents OTP spam (max 3 sends per 15 min)
  • OTP is hashed before storage — never stored in plaintext
  • Unit tests cover send, verify, expiry, and lockout scenarios

Files to Create / Modify

  • backend/src/modules/users/users.controller.ts (add endpoints)
  • backend/src/modules/users/users.service.ts (add OTP logic)
  • backend/src/modules/users/dto/phone-otp.dto.ts (new)
  • backend/src/modules/users/users.service.spec.ts (add OTP tests)

Priority

High — phoneVerified is used as a trust signal for SMS notifications and emergency contacts

Estimated Effort

2 days

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions