به محتوای اصلی

احراز هویت کاربران با JWT بدون کدنویسی؛ ثبت‌نام، ورود، دسترسی

JWT چطور کار می‌کند، چرا نوشتن دستی احراز هویت پرریسک‌ترین کار پروژه است، و چطور ثبت‌نام و ورود و دسترسی سطح رکورد را بدون نوشتن کد بسازید.

عاطفه امیری۴ دقیقه مطالعه
در این مطلب

احراز هویت کاربران آن بخشی از هر پروژه است که همه دست‌کم می‌گیرندش و همه هم رویش وقت می‌سوزانند: ثبت‌نام، ورود، هش رمز، بازیابی، توکن، انقضا، و بعد سطح دسترسی. این مقاله می‌گوید JWT دقیقاً چطور کار می‌کند، چرا نوشتن دستی این لایه پرریسک‌ترین کاری است که در یک پروژه‌ی کوچک انجام می‌دهید، و چطور می‌شود ثبت‌نام و ورود و دسترسی «فقط رکوردهای خودم» را بدون نوشتن کد سمت سرور داشت.

JWT چیست و چرا این‌قدر رایج شد؟

JWT (JSON Web Token) یک رشته‌ی امضاشده است که هویت کاربر را با خودش حمل می‌کند. سه بخش دارد که با نقطه جدا می‌شوند:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIwMTkyZjMuLi4iLCJleHAiOjE3NTQ0MDAwMDB9.k3vX...
^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^
header               payload                                          signature

بخش اول می‌گوید با چه الگوریتمی امضا شده، بخش دوم شناسه‌ی کاربر و زمان انقضا را حمل می‌کند، و بخش سوم امضاست.

نکته‌ی مهمی که مدام اشتباه فهمیده می‌شود: بخش payload رمزنگاری نشده، فقط کدگذاری شده است. هرکسی می‌تواند بازش کند و بخواند. چیزی که امضا تضمین می‌کند این است که کسی نمی‌تواند محتوایش را تغییر دهد. پس هیچ‌وقت داده‌ی محرمانه داخل توکن نگذارید.

مزیت اصلی JWT بدون‌حالت بودن است: سرور لازم نیست نشست را به خاطر بسپارد. توکن را می‌گیرد، امضا را بررسی می‌کند و می‌فهمد طرف کیست. همین باعث می‌شود برای اپ موبایل و API طبیعی‌تر از نشست مبتنی بر کوکی باشد.

کلید API و توکن کاربر: دو لایه‌ی متفاوت

رایج‌ترین اشتباه امنیتی در پروژه‌های کوچک، یکی گرفتن این دو است:

کلید APIتوکن کاربر (JWT)
هویت چه کسی؟برنامه‌ی شماکاربر نهایی اپ
کجا نگه‌داری می‌شود؟سمت سرور یا محیط امنروی دستگاه همان کاربر
دسترسیکامل به پروژهمحدود به همان کاربر
در کد فرانت‌اند؟هرگزبله، طبیعی است
چه زمانی باطل می‌شود؟وقتی شما بچرخانیدشبا انقضا یا خروج کاربر

اپلیکیشنی که فقط کلید API دارد و آن را داخل باندل موبایل یا کد جاوااسکریپت گذاشته، عملاً کلید کل داده را در دسترس همه گذاشته است. اگر اپ شما کاربر دارد، به هر دو لایه نیاز دارید. (کلیدها و احراز هویت)

چرا احراز هویت را خودتان ننویسید؟

نه به این دلیل که سخت است — به این دلیل که فهرست کارهایش تمام نمی‌شود و هر مورد جاافتاده یک آسیب‌پذیری واقعی است:

  • هش رمز با الگوریتم درست و پارامترهای امروزی (نه MD5، نه SHA بدون salt)
  • مقاومت در برابر brute-force: محدودیت نرخ روی ورود، و قفل موقت
  • جریان بازیابی رمز که خودش تبدیل به راه نفوذ نشود
  • انقضا و تمدید توکن، و باطل کردن در زمان خروج
  • جلوگیری از افشای اینکه «این شماره ثبت‌نام کرده یا نه» در پیام خطا
  • و بعد از همه‌ی این‌ها: کنترل دسترسی، که کار واقعی از اینجا شروع می‌شود

هر کدام از این‌ها به‌تنهایی ساده است. مسئله این است که همه‌شان باید درست باشند، و اشتباه در هرکدام بی‌سروصدا تا روزی که دیر است پنهان می‌ماند.

ثبت‌نام و ورود بدون نوشتن کد

در فیکارو هر پروژه از همان ابتدا موجودیت user و اندپوینت‌های آماده دارد. لازم نیست چیزی بنویسید:

POST /v1/auth/signup
{ "phone": "09121234567", "password": "..." }

POST /v1/auth/login
{ "phone": "09121234567", "password": "..." }
→ { "token": "eyJ..." }

توکن دریافتی را در همان هدر Authorization می‌فرستید و از آن لحظه، درخواست از طرف آن کاربر انجام می‌شود:

const { token } = await (
  await fetch("https://api.fikaro.ir/my-app/v1/auth/login", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ phone, password }),
  })
).json();

// از این به بعد، هر درخواست از طرف همین کاربر است
const orders = await fetch("https://api.fikaro.ir/my-app/v1/order", {
  headers: { Authorization: `Bearer ${token}` },
});

شماره‌ی موبایل در قالب E.164 ذخیره می‌شود و ارقام فارسی ورودی خودکار نرمال می‌شوند — یعنی کاربری که با کیبورد فارسی شماره‌اش را می‌نویسد، به دیوار نمی‌خورد.

دسترسی «فقط رکوردهای خودم»

اینجا جایی است که بیشتر پیاده‌سازی‌های دستی ناقص می‌مانند. اینکه کاربر وارد شده باشد کافی نیست؛ باید مشخص باشد چه چیزی را می‌بیند.

برای هر موجودیت می‌توانید یک فیلد مالکیت تعیین کنید. وقتی دسترسی روی scope=own باشد، مقدار آن فیلد با شناسه‌ی کاربرِ واردشده مقایسه می‌شود و کاربر فقط رکوردهای خودش را می‌گیرد — بدون اینکه کلاینت لازم باشد فیلتری بفرستد:

# همان اندپوینت، دو کاربر، دو نتیجه‌ی متفاوت
curl https://api.fikaro.ir/my-app/v1/order \
  -H "Authorization: Bearer eyJ..."

فیلتر کردن در سمت کلاینت، کنترل دسترسی نیست. اگر لیست کامل از سرور بیرون بیاید و کلاینت رکوردهای دیگران را «نشان ندهد»، داده همچنان افشا شده است — یک درخواست ساده با curl کافی است.

کلیدهای Production دسترسی کامل دارند و محدودسازی per-user فقط با توکن کاربران معنا پیدا می‌کند؛ این تفکیک عمدی است.

توکن را کجا نگه داریم؟

  • اپ موبایل: حافظه‌ی امن سیستم‌عامل — Keychain در iOS و EncryptedSharedPreferences یا Keystore در اندروید. نه در فایل ساده، نه در SharedPreferences معمولی.
  • وب: کوکی httpOnly امن‌ترین گزینه است چون جاوااسکریپت به آن دسترسی ندارد. اگر ناچار به localStorage هستید، بدانید که هر آسیب‌پذیری XSS یعنی توکن رفته.
  • هیچ‌جا: توکن را در URL، لاگ، یا Query String نگذارید. این‌ها همه‌جا ذخیره می‌شوند.

پنج اشتباه رایج

  1. گذاشتن کلید Production در اپ موبایل. باندل اپ قابل باز کردن است؛ کلید داخلش عمومی است.
  2. اعتماد به بررسی سمت کلاینت. هر قاعده‌ای که سرور اعمالش نکند، وجود ندارد.
  3. توکن بدون انقضا. توکنی که هیچ‌وقت باطل نمی‌شود، تا ابد معتبر است — از جمله برای کسی که دزدیدتش.
  4. پیام خطای پرحرف در ورود. «رمز اشتباه است» به مهاجم می‌گوید این شماره ثبت‌نام کرده. «شماره یا رمز نادرست است» درست‌تر است.
  5. نداشتن محدودیت نرخ روی ورود. بدون آن، حمله‌ی حدس رمز فقط مسئله‌ی زمان است.

گام بعدی

اگر تازه شروع کرده‌اید، شروع سریع شما را از ثبت‌نام تا اولین درخواست می‌برد. برای دیدن اینکه بقیه‌ی بک‌اند چطور کنار همین لایه می‌نشیند، API آماده و برای اتصال از اپ، بک‌اند فلاتر و ری‌اکت‌نیتیو را ببینید.

سوالات متداول

JWT امن است؟
خود مکانیزم استاندارد و امن است، به شرط رعایت سه نکته: توکن انقضا داشته باشد، در محل امن نگه‌داری شود، و داده محرمانه داخل payload نرود چون payload فقط کدگذاری شده و هرکسی می‌تواند بخواندش. امضا از تغییر محتوا جلوگیری می‌کند، نه از خواندنش.
فرق JWT با Session چیست؟
در Session، سرور وضعیت هر کاربر را نگه می‌دارد و کلاینت فقط یک شناسه دارد. در JWT، خود توکن اطلاعات را حمل می‌کند و سرور چیزی به خاطر نمی‌سپارد. برای API و اپ موبایل، JWT ساده‌تر و مقیاس‌پذیرتر است؛ برای وب‌اپ کلاسیک، Session با کوکی httpOnly هنوز انتخاب خوبی است.
می‌شود ثبت‌نام با ایمیل به‌جای موبایل داشت؟
مدل داده کاربر قابل تنظیم است و فیلدهای دلخواه می‌گیرد. برای کاربر ایرانی، شماره موبایل معمولاً شناسه عملی‌تری است چون تأییدش با پیامک ساده‌تر و نرخ ورود موفقش بالاتر است.
کنترل دسترسی نقش‌محور هم بدون کد ممکن است؟
بله. برای هر موجودیت مشخص می‌کنید هر نقش چه کاری می‌تواند بکند، و با تعیین فیلد مالکیت و دسترسی scope=own، محدودسازی تا سطح «هر کاربر فقط رکوردهای خودش» بدون نوشتن کد انجام می‌شود.

مطالب مرتبط