How to Send OTP SMS in Morocco with Node.js
Building user verification for applications in Morocco requires handling local carrier formats and ensuring fast message delivery. Moroccan operators—Maroc Telecom (IAM), Orange Morocco, and Inwi—enforce strict E.164 phone formatting and delivery filters.
This guide covers setting up a two-step One-Time Password (OTP) workflow in Node.js using EnvoiSMS.ma.
Prerequisites
- Node.js 18 or higher
- An active EnvoiSMS API key from your EnvoiSMS Dashboard
Install the official SDK package:
npm install envoisms
Step 1: Normalizing Moroccan Phone Numbers
Moroccan mobile numbers use the international prefix +212 followed by a 9-digit subscriber number starting with 6 or 7. Local users often input numbers starting with 06 or 07.
Always normalize input before sending API requests:
function normalizeMoroccanPhone(input: string): string {
const cleaned = input.replace(/\D/g, '');
if (cleaned.startsWith('212')) {
return `+${cleaned}`;
}
if (cleaned.startsWith('0')) {
return `+212${cleaned.slice(1)}`;
}
return `+212${cleaned}`;
}
// Example outputs:
// "0661234567" => "+212661234567"
// "212770123456" => "+212770123456"
Step 2: Requesting an OTP Code
Initialize the client and invoke sendOtp(). The API generates a cryptographically secure OTP, hashes it server-side, and dispatches the SMS via direct local operator links.
import { EnvoiSMSClient } from 'envoisms';
const client = new EnvoiSMSClient(process.env.ENVOISMS_API_KEY!);
async function requestVerification(rawPhoneNumber: string) {
const phone = normalizeMoroccanPhone(rawPhoneNumber);
try {
const response = await client.sendOtp({
to: phone,
brand: 'MyCompany',
code_length: 6,
expiry: 600, // Code expires in 10 minutes
});
// Save response.session_id in the user's session or temp auth state
return {
success: true,
sessionId: response.session_id,
expiresAt: response.expires_at,
};
} catch (error: any) {
console.error('OTP request failed:', error.message);
throw new Error(`Failed to send verification code: ${error.message}`);
}
}
Step 3: Verifying the Code Submitted by the User
When the user enters the numeric code from their phone, send it along with the session_id to checkOtp():
async function verifyCode(sessionId: string, userCode: string) {
try {
const response = await client.checkOtp({
session_id: sessionId,
code: userCode.trim(),
});
if (response.verified) {
// OTP is valid. Clear the temporary session ID and mark user as verified.
return { verified: true };
}
return { verified: false, message: 'Invalid verification code.' };
} catch (error: any) {
if (error.message.includes('MAX_ATTEMPTS')) {
return { verified: false, message: 'Maximum verification attempts exceeded. Please request a new code.' };
}
if (error.message.includes('SESSION_NOT_FOUND')) {
return { verified: false, message: 'Verification session expired. Please request a new code.' };
}
return { verified: false, message: error.message };
}
}
Direct HTTP Request Alternative (Native Fetch)
If you prefer not using an external SDK dependency, implement the API calls with standard fetch:
const API_BASE = 'https://api.envoisms.ma/v1';
const API_KEY = process.env.ENVOISMS_API_KEY!;
// 1. Send OTP
const sendRes = await fetch(`${API_BASE}/verify/send`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${API_KEY}`,
},
body: JSON.stringify({
to: '+212661234567',
brand: 'MyCompany',
code_length: 6,
expiry: 600,
}),
});
const sendData = await sendRes.json();
const sessionId = sendData.session_id;
// 2. Check OTP
const checkRes = await fetch(`${API_BASE}/verify/check`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${API_KEY}`,
},
body: JSON.stringify({
session_id: sessionId,
code: '492018',
}),
});
const checkData = await checkRes.json();
console.log('Verified:', checkData.verified);
Production Security Best Practices
- Rate Limit Requests: Restrict OTP requests per IP and phone number (e.g., maximum 3 requests per 15 minutes) to prevent SMS pumping attacks.
-
Never Expose API Keys: Keep
ENVOISMS_API_KEYstrictly on the backend server. -
Session Bound: Associate the returned
session_idwith the authenticated user's temporary state or JWT token rather than trusting raw client payload parameters.
Resources
-
SDK Package:
npm install envoisms - Documentation: https://envoisms.ma/fr/docs
- Dashboard: https://envoisms.ma/dashboard
Top comments (0)