SaaS OTP Management System – Case Study

{ // deep_execution_view
const authorName = "Ankit Agrawal";
//
const publishDate = "June 3, 2026";

Business Requirements

Your SaaS platform needs OTP verification for:

  • User Registration
  • Login Authentication
  • Password Reset
  • Email Change
  • Mobile Number Change
  • Transaction Approval
  • 2FA Authentication
  • Sensitive Account Settings Changes

Requirements:

  • Multiple OTP providers
    1. Twilio
    2. MSG91
    3. AWS SNS
    4. SMTP Email
    5. WhatsApp API
  • Enable/Disable providers
  • Provider priority and failover
  • OTP expiration
  • Retry limits
  • Rate limiting
  • Prevent OTP reuse
  • Detailed logs
  • Audit trail
  • Multi-tenant support (if SaaS)

High Level Architecture

Provider Manager decides:


Database Structure

otp_providers

Manage all platforms from admin panel.

FieldType
idbigint
namevarchar
codevarchar
typesms/email/whatsapp
is_activetinyint
priorityint
configjson
created_attimestamp

Example


{
  "api_key":"xxx",
  "sender_id":"CODEEX"
}

otp_templates

Different OTP use cases.

otp_templates

FieldType
idbigint
namevarchar
purposevarchar
expiry_secondsint
max_attemptsint
max_resendint
is_activetinyint

Example

 	Login OTP
 	Expiry: 300 sec
 	Attempts: 5
 	Resend: 3

otp_requests

Main OTP table.

FieldType
idbigint
user_idbigint
tenant_idbigint
otp_hashvarchar
purposevarchar
recipientvarchar
provider_idbigint
expires_attimestamp
verified_attimestamp
statuspending / verified / expired / blocked
resend_countint
attempt_countint
created_attimestamp

Important:

Never store OTP directly.

Store hash:

Hash::make($otp)

or

hash('sha256',$otp)

otp_logs

Track everything.

FieldType
idbigint
otp_request_idbigint
actionvarchar
responsejson
ip_addressvarchar
created_attimestamp

Examples:

  • OTP_GENERATED
  • OTP_SENT
  • OTP_RESENT
  • OTP_VERIFIED
  • OTP_EXPIRED
  • OTP_BLOCKED

otp_rate_limits

Protection against abuse.

FieldType
idbigint
recipientvarchar
purposevarchar
total_requestsint
last_request_attimestamp

OTP Generation Flow

Before creating new OTP:

UPDATE otp_requests
SET status='expired'
WHERE recipient='9999999999'
AND purpose='login'
AND status='pending';

This ensures:

  • Previous OTP becomes unusable

Only latest OTP works.


OTP Verification Logic

User enters OTP


Pseudo:

if(now() > expires_at)
{
    return "OTP Expired";
}

if(attempt_count >= max_attempts)
{
    status = 'blocked';
}

if(hash(inputOtp) != storedHash)
{
    attempt_count++;
}

if(match)
{
    status = 'verified';
    verified_at = now();
}

Prevent OTP Reuse

After successful verification:

status = verified
verified_at = now()

Future verification query:

SELECT *
FROM otp_requests
WHERE status='pending'

Verified OTP never matches again.

Additionally:

UNIQUE INDEX
(
recipient,
purpose,
status
)

Only one active OTP.


Resend Logic

Example:

Max Resend = 3

Track:

resend_count

Logic:

if(resend_count >= 3)
{
    throw "Resend limit exceeded";
}

When resending:

Option 1 (Recommended)

Generate new OTP.

Old OTP Expired

New OTP Generated

More secure.


Attempt Limit Logic

Example:

5 Wrong Attempts

After that:

OTP Blocked

Update:

status='blocked'

User must request new OTP.


Rate Limiting

Prevent OTP bombing.

  • Per Mobile – 5 OTP / 15 min
  • Per IP – 10 OTP / hour
  • Per User – 20 OTP / day

Store counters in:

  • otp_rate_limits
  • or Redis

Recommended:

Redis

for speed.


Provider Failover Logic

Priority table:

ProviderPriority
Twilio1
MSG912
AWS SNS3

Algorithm:

foreach($providers as $provider)
{
   try {
      sendOtp();
      break;
   }
   catch(Exception $e){
      continue;
   }
}

Logs:

  • Twilio Failed
  • MSG91 Success

Admin Panel Features

OTP Providers

✓ Twilio Active

✓ MSG91 Active

✗ AWS SNS Disabled

Change without deployment.

OTP Settings

Login OTP

Expiry: 5 Min

Attempts: 5

Resend: 3


Analytics Dashboard

Show:

  • Total OTP Sent
  • Success Rate
  • Failed OTP
  • Provider Wise Delivery
  • Resend %
  • Blocked Attempts

Total OTP Sent

Success Rate

Failed OTP

Provider Wise Delivery

Resend %

Blocked Attempts

Recommended Status Flow


Enterprise-Level Enhancements

Device Fingerprinting

Store:

  • Device ID
  • Browser
  • OS
  • Location

Detect suspicious requests.

OTP Purpose Isolation

Separate OTPs:

  • LOGIN
  • PASSWORD_RESET
  • PAYMENT
  • EMAIL_CHANGE

A login OTP should never verify a password reset request.

Queue-Based Delivery

Never send directly.

Using:

  • Laravel Queue
  • Redis
  • RabbitMQ
  • SQS

Recommended Final Design

Tables:

  • otp_providers
  • otp_templates
  • otp_requests
  • otp_logs
  • otp_rate_limits

Security Rules:

  • Hash OTP before storing
  • Only latest OTP active
  • Previous OTP automatically invalid
  • Expiry configurable
  • Retry configurable
  • Resend configurable
  • Provider failover
  • Admin manageable
  • Full audit logs
  • Redis rate limiting
  • Queue-based delivery
  • Multi-tenant ready
}

Add this website to your home screen?