اتصال Supabase Auth به هر سرویس پیامکی با Send SMS hook
خلاصهٔ کاملتر
Supabase Auth فقط چهار سرویس پیامک داره: MessageBird، Twilio، Vonage و TextLocal. اگه مثلاً تو هند به MSG91 یا تو نیجریه به Termii نیاز داشته باشی، تو داشبورد جایی براش نیست. نویسنده میگه راهحل Send SMS hook هست که حدود ۴۰ خط کد لازم داره. ایده اینه که فقط رسوندن پیامک رو به یه endpoint خودت بسپاری و هر کار امنیتی رو پیش GoTrue (سرویس احراز هویت Supabase) نگه داری.
فرایند OTP دو مرحله داره. تو مرحلهی صدور، GoTrue کد رو میسازه، هش میکنه، ذخیره میکنه، زمان انقضاش رو با sms_otp_exp میذاره و محدودیتها رو با sms_max_frequency و سقف کلی پروژه چک میکنه. تو مرحلهی تأیید هم خودش هش رو مقایسه میکنه، تعداد تلاشها رو میشمره و سشن میسازه. بین این دو فقط جابهجا کردن شش رقم روی شبکهی مخابراتی هست که هیچ نقش امنیتی نداره. نویسنده معتقده دلیل اصلی استفاده از hook بهجای ساختن OTP از صفر همینه، چون پیادهسازیهای دستی معمولاً کد رو بدون هش نگه میدارن یا محدودیت تلاش ندارن.
endpoint تو یه درخواست امضاشده با استاندارد Standard Webhooks میگیره که دو فیلد مهمش user.phone و sms.otp هستن. اگه ارسال موفق بود باید {} با کد 200 برگردونی و اگه نه، یه خطا مثل 502. نویسنده تأکید میکنه برگردوندن 200 وقتی پیامک نرفته یه باگ بدجوره، چون کاربر منتظر پیامی میمونه که هیچوقت نمیاد و هیچ جا هم خطایی ثبت نمیشه.
چهار اشتباه رایج تو چک کردن امضا هست. اول، هش کردن body بعد از parse و دوباره serialize کردنش. امضا روی بایتهای خام حساب شده و خروجی دوبارهساختهشده فرق میکنه، پس همیشه 401 میگیری. دوم، چک نکردن timestamp که جلوی replay (یعنی فرستادن دوبارهی یه درخواست ضبطشده) رو نمیگیره. نویسنده پنج دقیقه رو پیشنهاد میکنه. سوم، اینکه هدر امضا رو یه مقدار تکی فرض کنی، در حالی که یه لیسته تا موقع عوض کردن secret هر دو امضا معتبر باشن. چهارم، مقایسه با === که از طریق زمان پاسخ اطلاعات لو میده و باید جاش از timingSafeEqual استفاده کرد.
const rawBody = await req.text(); // raw bytes, before any parsing
const authentic = verifyStandardWebhook(secret, {
id: req.headers.get("webhook-id") ?? undefined,
timestamp: req.headers.get("webhook-timestamp") ?? undefined,
signature: req.headers.get("webhook-signature") ?? undefined,
}, rawBody);
if (!authentic) return err(401, "invalid signature");این بخش از endpoint نشون میده که اول body خام خونده میشه، امضا روی همون چک میشه و تازه بعدش JSON parse میشه. endpoint هیچ state یا دیتابیسی نداره. کد OTP تا وقتی منقضی نشده یه رمز زندهست و نباید تو هیچ لاگ یا trace ذخیره بشه. برای صدا زدن سرویس پیامک هم باید timeout مشخص بذاری (تو مثال مقاله ۱۰ ثانیه) و body جواب رو هم چک کنی، چون خیلی از این APIها حتی موقع خطا 200 برمیگردونن. نویسنده در آخر یادآوری میکنه که بعد از این کار، endpoint خودت نقطهی شکست کل احراز هویته و هیچ fallbackی وجود نداره.
نکات کلیدی:
- Supabase Auth فقط MessageBird، Twilio، Vonage و TextLocal رو پشتیبانی میکنه.
- با Send SMS hook فقط ارسال پیامک به endpoint تو میرسه و ساخت کد، انقضا و محدودیتها دست GoTrue میمونه.
- امضای Standard Webhooks باید روی body خام چک بشه، نه JSON دوبارهساختهشده.
- بازهی مجاز timestamp حدود ۵ دقیقه پیشنهاد شده و مقایسهی امضا باید با timingSafeEqual باشه.
- هدر امضا یه لیسته تا موقع عوض کردن secret، امضای قدیمی و جدید هر دو قبول بشن.
- اگه پیامک نرفت باید خطا (مثلاً 502) برگردونی، نه 200.
- endpoint خودت بعد از این کار نقطهی شکست کل ورود کاربرهاست و fallback نداره.




