שולחים קוד אימות ב-SMS ובודקים אותו בכמה שורות קוד, בלי לבנות בעצמכם את יצירת הקוד, התוקף, מגבלת הניסיונות וזמן ההמתנה.
שליחת קוד אימות היא קריאה אחת ל-API (ההסבר המלא כאן). מה שמסביב לה הוא העבודה: ליצור קוד אקראי, לשמור אותו בצורה בטוחה, לבדוק שהוא עדיין בתוקף, לחסום ניחושים, ולא לשלוח עשרה מסרונים למי שלוחץ עשר פעמים על "שלח שוב". החבילות של מסר 10 עושות את כל זה, ונכתבו כך שיתנהגו אותו דבר בשלוש השפות.
Node.js, מ-npm:
npm install @meser10/otp
Python, ישירות מ-GitHub:
pip install "git+https://github.com/meser10/meser10-otp#subdirectory=python"
PHP, עם Composer:
composer config repositories.meser10-otp vcs https://github.com/meser10/meser10-otp composer require meser10/otp:^1.0
מה צריך מחשבון מסר 10: יתרת SMS, מפתח API (הגדרות משתמש, מפתח API) ושם שולח מאושר, עד 11 אותיות באנגלית או ספרות, או מספר נייד שאומת בחשבון. בנוסף צריך סוד אקראי של 16 תווים לפחות, זהה בכל השרתים, שאיתו נחתמים הקודים השמורים.
const { Meser10Otp, Meser10Error } = require('@meser10/otp');
const otp = new Meser10Otp({
apiKey: process.env.MESER10_API_KEY,
sender: 'MyShop',
secret: process.env.OTP_SECRET,
message: 'Your MyShop code is {code}',
});
async function main(userInput) {
await otp.sendCode('054-123-4567');
try {
await otp.verifyCode('054-123-4567', userInput); // true
} catch (e) {
if (e instanceof Meser10Error) console.log(e.code); // wrong_code, locked...
}
}ב-Next.js, route אחד שמטפל גם בשליחה וגם בבדיקה:
// app/api/otp/route.js
import { Meser10Otp, Meser10Error } from '@meser10/otp';
import { redisStore } from '@/lib/otp-store';
const otp = new Meser10Otp({ apiKey: process.env.MESER10_API_KEY, sender: 'MyShop', secret: process.env.OTP_SECRET, store: redisStore });
export async function POST(req) {
const { phone, code } = await req.json();
try {
if (code) return Response.json({ ok: await otp.verifyCode(phone, code) });
await otp.sendCode(phone);
return Response.json({ sent: true });
} catch (e) {
if (e instanceof Meser10Error) return Response.json({ error: e.code }, { status: 400 });
throw e;
}
}import os
from meser10_otp import Meser10Otp, Meser10Error
otp = Meser10Otp(os.environ["MESER10_API_KEY"], sender="MyShop", secret=os.environ["OTP_SECRET"])
otp.send_code("054-123-4567")
try:
otp.verify_code("054-123-4567", user_input) # True
except Meser10Error as e:
print(e.code)use Meser10\Otp\Otp;
use Meser10\Otp\RedisStore;
use Meser10\Otp\Meser10Error;
$redis = new Redis();
$redis->connect('127.0.0.1');
$otp = new Otp([
'apiKey' => getenv('MESER10_API_KEY'),
'sender' => 'MyShop',
'secret' => getenv('OTP_SECRET'),
'store' => new RedisStore($redis),
]);
$otp->sendCode('054-123-4567');
try {
$otp->verifyCode('054-123-4567', $_POST['code'] ?? ''); // true
} catch (Meser10Error $e) {
echo $e->errorCode;
}בשרת אחד שרץ ברציפות אפשר להשתמש בברירת המחדל של Node ו-Python, ששומרת בזיכרון. בכל מצב אחר, כמה שרתים, כמה workers או serverless, מעבירים store שמשותף לכולם. ה-store צריך ארבע פעולות: get, set, delete ו-incr אטומי. ה-incr הוא מה ששומר על מגבלת הניסיונות ועל זמן ההמתנה גם כשמגיעות כמה בקשות באותו רגע. ב-README של כל חבילה יש store מוכן על Redis.
| הגדרה | ברירת מחדל | מה היא קובעת |
|---|---|---|
| codeLength | 6 | אורך הקוד, 4 עד 8 ספרות |
| ttlSeconds | 300 | כמה זמן הקוד בתוקף |
| maxAttempts | 5 | ניסיונות שגויים עד שהקוד ננעל |
| resendSeconds | 60 | זמן המתנה לפני קוד חדש לאותו מספר |
| message | Your verification code is {code} | נוסח ההודעה. {code} מוחלף בקוד |
ב-Python שמות ההגדרות באותיות קטנות עם קו תחתון, למשל code_length ו-ttl_seconds.
כל שגיאה מגיעה עם קוד קבוע, כך שאפשר להציג לגולש הודעה מתאימה:
| קוד | מה קרה |
|---|---|
| invalid_phone | המספר אינו נייד ישראלי. לא נשלח כלום |
| too_soon | ביקשו קוד חדש לפני שזמן ההמתנה נגמר |
| api_error | מסר 10 דחה את השליחה, למשל שם שולח שלא אושר. הסיבה בהודעת השגיאה |
| network | לא הייתה תקשורת עם מסר 10 |
| no_code | אין קוד פעיל למספר הזה: לא נשלח קוד, הקוד כבר נוצל, או שתוקפו פג |
| expired | תוקף הקוד פג (בדרך כלל מגיע כ-no_code, כי הקוד כבר נמחק) |
| wrong_code | הקוד שגוי. ההודעה מציינת כמה ניסיונות נשארו |
| locked | יותר מדי ניסיונות שגויים. צריך לבקש קוד חדש |
| bad_response | תשובה לא צפויה מהשרת של מסר 10 |
| config | הגדרה שגויה, למשל secret קצר מ-16 תווים, חסר store ב-PHP, או ערך מחוץ לטווח |
כדאי להוסיף מעל החבילה גם מגבלה לפי כתובת IP ולפי חשבון: כל קוד הוא מסרון בתשלום.
הקוד של שלוש החבילות נמצא ב-GitHub, ברישיון MIT, עם הבדיקות האוטומטיות:
https://github.com/meser10/meser10-otp
עוד באותו נושא: OTP: קוד אימות חד-פעמי ב-SMS, תוסף OTP לוורדפרס וה-API למפתחים.