Flex Auth Service
Phone-based authentication service supporting OTP and password login. Built with NestJS, deployed on AWS Lambda.
Flex Auth Service (zv-flex-auth-service)
Phone-based authentication supporting OTP (WhatsApp/SMS) and password login. The primary auth service for mobile apps (Forsa).
Service Info
| Property | Value |
|---|---|
| Framework | NestJS (TypeScript) |
| Database | PostgreSQL (zv_phone_auth) |
| API Prefix (dev) | api/v2/auth |
| API Prefix (local) | api/v1/auth |
Base URLs
| Environment | URL |
|---|---|
| Test (dev) | https://test.zonevast.com/auth/api/v2/auth/ |
| Local | http://localhost:8015/api/v1/auth/ |
| Lambda | zv-flex-auth-service-dev |
Concept
Flex Auth uses phone numbers as the primary identity. There are two paths:
- New user -- Register first, then you can login
- Existing user -- Login directly with password or OTP
Every user is scoped to a project (projectId). Before using any endpoint, you need a project.
Don't have a project ID? See Getting Started - Create a Project.
Check What Your Project Supports
Each project configures which login methods are enabled:
curl https://test.zonevast.com/auth/api/v2/auth/config/<project-id>/public
This returns the enabled methods (phone, otp, etc.) and OTP settings.
Step 1: Registration
Every new user starts here. Registration verifies the phone number via OTP, then creates the account.
1.1 Send OTP to Verify Phone (register-init)
This is the first call for any new user. It sends an OTP to the phone number via WhatsApp or SMS.
curl -X POST https://test.zonevast.com/auth/api/v2/auth/register-init \
-H "Content-Type: application/json" \
-d '{
"phone": "+966500000000",
"projectId": <project-id>
}'
Response:
{
"message": "OTP sent successfully",
"cooldownSeconds": 60
}
The OTP is sent via WhatsApp (primary) or SMS (fallback). Seed test numbers
+9647500000001and+9647500000002always use OTP123456.
1.2 Verify OTP and Create Account (register-verify)
After the user receives the OTP, call this to complete registration and create the account.
curl -X POST https://test.zonevast.com/auth/api/v2/auth/register-verify \
-H "Content-Type: application/json" \
-d '{
"phone": "+966500000000",
"otp": "123456",
"firstName": "John",
"lastName": "Doe"
}'
Response:
{
"user": {
"id": "uuid",
"phone": "+966500000000",
"role": "customer",
"status": "active",
"phoneVerified": true
},
"tokens": {
"accessToken": "eyJhbGciOiJIUzI1NiJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiJ9..."
}
}
You now have access and refresh tokens. Use the
accessTokenin theAuthorization: Bearerheader for all authenticated requests.
Registration Flow Summary
User opens app
→ POST /register-init { phone, projectId }
→ OTP sent to phone via WhatsApp/SMS
→ User enters OTP
→ POST /register-verify { phone, otp, firstName, lastName }
→ Account created
→ Returns tokens (auto-logged-in)
→ Use accessToken for all subsequent API calls
Step 2: Login (Existing Users)
For users who already registered. Two methods:
Login with Password
curl -X POST https://test.zonevast.com/auth/api/v2/auth/login \
-H "Content-Type: application/json" \
-d '{
"phone": "+966500000000",
"password": "your-password",
"projectId": <project-id>
}'
After 5 failed attempts: 15-minute lockout.
Login with OTP (Passwordless)
Step 1: Send OTP
curl -X POST https://test.zonevast.com/auth/api/v2/auth/send-otp \
-H "Content-Type: application/json" \
-d '{
"phone": "+966500000000",
"projectId": <project-id>
}'
OTP cooldown tiers: 1st: immediate, 2nd: 1min, 3rd: 5min, 4th+: 24h.
Step 2: Login with OTP
curl -X POST https://test.zonevast.com/auth/api/v2/auth/login-otp \
-H "Content-Type: application/json" \
-d '{
"phone": "+966500000000",
"otp": "123456",
"projectId": <project-id>
}'
If user is not registered, this returns an error. Redirect to registration flow.
Password Reset
# Step 1: Initiate reset
curl -X POST https://test.zonevast.com/auth/api/v2/auth/password/reset/init \
-H "Content-Type: application/json" \
-d '{"phone": "+966500000000", "projectId": <project-id>}'
# Step 2: Confirm reset
curl -X POST https://test.zonevast.com/auth/api/v2/auth/password/reset/confirm \
-H "Content-Type: application/json" \
-d '{"phone": "+966500000000", "otp": "123456", "newPassword": "new-pass-123"}'
Token Refresh
Access tokens expire after 60 minutes. Use the refresh token to get a new one:
curl -X POST https://test.zonevast.com/auth/api/v2/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refresh_token": "YOUR_REFRESH_TOKEN"}'
| Token | Expiry |
|---|---|
| Access Token | 60 minutes |
| Refresh Token | 7 days |
Authenticated Endpoints
All require Authorization: Bearer {token} and X-Project-ID: <project-id> headers.
Profile
# Get profile
curl https://test.zonevast.com/auth/api/v2/auth/profile \
-H "Authorization: Bearer TOKEN" \
-H "X-Project-ID: <project-id>"
# Update profile
curl -X PUT https://test.zonevast.com/auth/api/v2/auth/profile \
-H "Authorization: Bearer TOKEN" \
-H "X-Project-ID: <project-id>" \
-H "Content-Type: application/json" \
-d '{"firstName": "John", "lastName": "Doe", "email": "john@example.com"}'
Change Password
curl -X PUT https://test.zonevast.com/auth/api/v2/auth/password \
-H "Authorization: Bearer TOKEN" \
-H "X-Project-ID: <project-id>" \
-H "Content-Type: application/json" \
-d '{"currentPassword": "old-pass", "newPassword": "new-pass"}'
User Settings
# Get settings
curl https://test.zonevast.com/auth/api/v2/auth/settings \
-H "Authorization: Bearer TOKEN" \
-H "X-Project-ID: <project-id>"
# Update settings
curl -X PUT https://test.zonevast.com/auth/api/v2/auth/settings \
-H "Authorization: Bearer TOKEN" \
-H "X-Project-ID: <project-id>" \
-H "Content-Type: application/json" \
-d '{"notifications": true, "language": "ar"}'
User Model
| Field | Type | Notes |
|---|---|---|
id | UUID | Primary key |
phone | string | Required, unique per project |
email | string? | Optional, unique |
role | enum | customer, driver, staff, admin, pending |
status | enum | active, inactive, suspended, deleted |
firstName | string? | Optional |
lastName | string? | Optional |
avatar | string? | URL to profile image |
language | string | Default: 'en' |
phoneVerified | boolean | Set true after OTP verification |
projectId | number | Project isolation |
TypeScript Integration
const FLEX_AUTH_BASE = 'https://test.zonevast.com/auth/api/v2/auth';
// Full registration + auto-login flow
async function registerAndLogin(phone: string, projectId: number) {
// 1. Send OTP to verify phone
await fetch(`${FLEX_AUTH_BASE}/register-init`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, projectId }),
});
// 2. After user enters OTP, verify and create account
const res = await fetch(`${FLEX_AUTH_BASE}/register-verify`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, otp: 'user-entered-otp', projectId }),
});
const { tokens } = await res.json();
return tokens; // User is now registered AND logged in
}
// Login existing user with OTP
async function loginWithOtp(phone: string, projectId: number) {
// 1. Send OTP
await fetch(`${FLEX_AUTH_BASE}/send-otp`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, projectId }),
});
// 2. Login (after user enters OTP)
const res = await fetch(`${FLEX_AUTH_BASE}/login-otp`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ phone, otp: 'user-entered-otp', projectId }),
});
const { tokens } = await res.json();
return tokens;
}
Deployment
cd /home/yousef/Documents/workspace/zonevast/services/zv-flex-auth-service/
npm run deploy:dev # Deploy to test.zonevast.com
npm run deploy:prod # Deploy to production