Generate • Send • Verify — A secure OTP service built on Cloudflare Workers
Features • Quick Start • API • Configuration • Security
- Overview
- Features
- Quick Start
- Database Setup
- Configuration
- API Reference
- Testing
- Security
- Monitoring
A production-ready Cloudflare Worker that handles mobile OTP (One-Time Password) verification with enterprise-grade security and reliability. Perfect for telco porting, user verification, and secure authentication flows.
graph LR
A[Client] -->|Basic Auth| B[Worker]
B --> C[D1 Database]
B --> D[SMS API]
E[Cron Job] -->|Every 5min| B
B -->|Cleanup| C
|
|
- Cloudflare account with Workers enabled
- D1 database created
- SMS provider credentials (SMS API)
# Clone the worker code
git clone https://github.com/Nafis28/simpleOTPService
# Deploy to Cloudflare
wrangler deploy2️⃣ Configure (see Configuration)
# Request an OTP
curl -X POST "https://your-worker.workers.dev/request" \
-u "username:password" \
-H "Content-Type: application/json" \
-d '{"Number":"+61412345678","LSP":"Telstra","OR":"ORDER1234"}'- Navigate to: Cloudflare Dashboard → Workers & Pages → D1
- Create database: Name it
mobileotp - Run this schema:
-- 🏗️ Create OTP storage table
CREATE TABLE IF NOT EXISTS otps (
number TEXT PRIMARY KEY, -- Mobile in E.164 format
code TEXT NOT NULL, -- 7-digit OTP
lsp TEXT, -- Service provider
order_ref TEXT, -- Order reference
failed_attempts INTEGER NOT NULL DEFAULT 0,
created_at INTEGER NOT NULL, -- Unix timestamp
status TEXT DEFAULT 'pending' -- Status: pending|success|failed
);
-- 🚀 Add performance index
CREATE INDEX IF NOT EXISTS idx_otps_created_at
ON otps(created_at);| Status | Description | Retention |
|---|---|---|
🟡 pending |
OTP sent, awaiting verification | 10 minutes |
🟢 success |
OTP verified successfully | Permanent* |
🔴 failed |
Max attempts exceeded | Permanent* |
*Consider adding cleanup for old success/failed records
Click to expand setup instructions
- Go to Worker → Settings → Bindings
- Click "+ Add Binding"
- Select D1 Database
- Configure:
- Variable name:
DB - Database:
mobileotp
- Variable name:
| Secret | Description | Example |
|---|---|---|
BASIC_USER |
Basic auth username | otp_service |
BASIC_PASS |
Basic auth password | SecureP@ss123! |
SMS_TOKEN |
SMS API bearer token | abc123xyz789 |
SMS_FROM |
Sender ID/number | VERIFY or +61400000000 |
⚠️ Important: Add these via Settings → Variables → Secrets (not plain variables!)
Schedule: "*/5 * * * *" # Every 5 minutes
Purpose: Clean expired pending OTPsSetup Instructions
- Go to Worker → Triggers → Cron Triggers
- Add trigger with expression:
*/5 * * * * - Save and verify it's active
All requests require Basic Authentication:
Authorization: Basic base64(username:password)Request Body
{
"Number": "+61412345678", // E.164 format required
"LSP": "Telstra", // Losing service provider
"OR": "ORDER1234" // Order reference
}Response Examples
✅ Success (200)
{
"status": "sent"
}❌ Error (400/500)
{
"error": "Invalid number format"
}Request Body
{
"Number": "+61412345678",
"code": "1234567"
}Response Examples
✅ Correct Code (200)
{
"status": "success"
}{
"error": "Incorrect code. Try again.",
"attempts_left": 1
}❌ Wrong Code - Final Attempt (400)
{
"error": "Incorrect code. Attempts exhausted; request a new code."
}⏰ Expired OTP (400)
{
"error": "OTP expired"
}Request OTP
curl -X POST "https://your-worker.workers.dev/request" \
-u "BASIC_USER:BASIC_PASS" \
-H "Content-Type: application/json" \
-d '{
"Number": "+61412345678",
"LSP": "Telstra",
"OR": "ORDER1234"
}'Verify OTP
curl -X POST "https://your-worker.workers.dev/otp" \
-u "BASIC_USER:BASIC_PASS" \
-H "Content-Type: application/json" \
-d '{
"Number": "+61412345678",
"code": "1234567"
}'- Method:
POST - URL:
https://your-worker.workers.dev/[endpoint] - Auth Tab: Type = Basic Auth
- Body: Raw JSON (see examples above)
// Local testing with Wrangler
wrangler dev --local --persist
// View D1 data
wrangler d1 execute mobileotp --command "SELECT * FROM otps"| Feature | Implementation | Benefit |
|---|---|---|
| 🔑 Authentication | Basic Auth on all endpoints | Prevents unauthorized access |
| 🎲 OTP Generation | 7 digits, no leading zeros | Secure & user-friendly |
| ⏱️ Time Limits | 10-minute expiry | Reduces attack window |
| 🚫 Rate Limiting | Max 2 attempts per OTP | Prevents brute force |
| 📝 Audit Trail | All attempts logged | Compliance & debugging |
| 🔒 Secrets Management | Environment variables | No hardcoded credentials |
- Use HTTPS only (automatic with Workers)
- Rotate
BASIC_PASSregularly - Monitor failed attempts
- Use E.164 format for all numbers
- Consider adding IP-based rate limiting
- Implement webhook for suspicious activity
-- 📊 Daily OTP statistics
SELECT
DATE(created_at, 'unixepoch') as date,
COUNT(*) as total_otps,
SUM(CASE WHEN status = 'success' THEN 1 ELSE 0 END) as successful,
SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed
FROM otps
GROUP BY date
ORDER BY date DESC
LIMIT 7;- High failure rate (>20%)
- Unusual traffic patterns
- Cron job failures
- SMS API errors
This project is licensed under the MIT License.