JWT واقعاً چگونه کار میکند؟ Access Token، Refresh Token و Session
مقدمه؛ JWT دقیقاً چه مشکلی را حل میکند؟
فرض کنید کاربر وارد یک وبسایت میشود و با ایمیل و رمز عبور خود Login میکند.
سرور باید از این به بعد بداند:
این Request متعلق به کدام کاربر است؟
مثلاً کاربر بعد از Login درخواست زیر را ارسال میکند:
GET /api/profileسرور باید بتواند تشخیص دهد که این Request متعلق به User شماره 42 است.
اینجاست که مفهوم Authentication وارد میشود.
یکی از روشهای بسیار رایج برای مدیریت Authentication در APIها استفاده از JWT یا JSON Web Token است.
اما JWT فقط یک «توکن برای Login» نیست.
برای درک درست آن باید چند مفهوم را کنار هم ببینیم:
Authentication
↓
Access Token
↓
Refresh Token
↓
Session
↓
Authorizationدر این مقاله از صفر بررسی میکنیم JWT چیست، داخل آن چه اطلاعاتی وجود دارد، Access Token و Refresh Token چه تفاوتی دارند و Session دقیقاً کجای این معماری قرار میگیرد.
JWT چیست؟
JWT مخفف JSON Web Token است.
JWT یک استاندارد برای انتقال اطلاعات به شکل یک Token قابل انتقال و قابل بررسی است.
ساختار معمول JWT به شکل زیر است:
xxxxx.yyyyy.zzzzzیعنی JWT از سه بخش تشکیل شده است:
Header.Payload.Signatureبرای مثال:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjMiLCJyb2xlIjoidXNlciJ9
.
signatureاین سه قسمت عبارتاند از:
Header
مشخص میکند Token با چه نوع الگوریتمی امضا شده است.
مثلاً:
{
"alg": "HS256",
"typ": "JWT"
}Payload
اطلاعات یا Claimهای مربوط به Token در این قسمت قرار میگیرند.
مثلاً:
{
"sub": "123",
"role": "user",
"iat": 1720000000,
"exp": 1720003600
}Signature
Signature برای بررسی صحت Token استفاده میشود.
به شکل ساده:
Header
+
Payload
+
Secret / Private Key
↓
Signatureسرور هنگام دریافت Token میتواند Signature را بررسی کند تا متوجه شود Token معتبر و دستکارینشده است.
آیا JWT رمزنگاری شده است؟
یکی از رایجترین سوءتفاهمها درباره JWT این است که:
«اطلاعات داخل JWT رمزنگاری شدهاند.»
در حالت معمول، JWT رمزگذاری نشده است؛ بلکه Base64URL-encoded شده و امضا شده است.
یعنی اگر Payload شامل این اطلاعات باشد:
{
"userId": 123,
"role": "admin"
}نباید فرض کنید این اطلاعات مخفی هستند.
بنابراین نباید اطلاعات حساس مانند:
Password
Credit Card Number
Private Data
Secret Keysرا داخل Payload قرار دهید.
Signature از تغییر محتوا جلوگیری میکند، اما بهتنهایی محتوای JWT را مخفی نمیکند.
JWT چگونه Authentication را انجام میدهد؟
بیایید کل فرآیند را مرحلهبهمرحله ببینیم.
مرحله اول؛ Login
کاربر اطلاعات خود را ارسال میکند:
POST /api/loginمثلاً:
{
"email": "user@example.com",
"password": "********"
}Backend اطلاعات کاربر را بررسی میکند.
اگر Login موفق باشد، سرور معمولاً یک Access Token ایجاد میکند.
User
↓
Login
↓
Backend
↓
Validate Credentials
↓
Generate Token
↓
Access TokenAccess Token چیست؟
Access Token توکنی است که Client برای دسترسی به منابع محافظتشده استفاده میکند.
مثلاً:
GET /api/profile
Authorization: Bearer <access-token>Backend Token را دریافت میکند و آن را Validate میکند.
اگر معتبر باشد:
Token Valid
↓
Identify User
↓
Check Authorization
↓
Return ResourceAccess Token معمولاً عمر کوتاهی دارد.
مثلاً:
5 دقیقه
15 دقیقه
30 دقیقهمدت دقیق به معماری و سطح امنیت موردنیاز سیستم بستگی دارد.
چرا کوتاه؟
چون اگر Access Token سرقت شود، مدت قابل استفاده بودن آن محدودتر خواهد بود.
Refresh Token چیست؟
اگر Access Token خیلی زود منقضی شود، کاربر مجبور خواهد بود مرتب Login کند.
اینجاست که Refresh Token وارد میشود.
Refresh Token برای گرفتن یک Access Token جدید استفاده میشود.
معماری ساده:
Access Token
↓
Short-lived
↓
Expires
↓
Refresh Token
↓
New Access Tokenمثلاً:
Access Token
15 دقیقه
Refresh Token
چند روز / هفتهالبته طول عمر واقعی باید بر اساس مدل تهدید و نیاز محصول تعیین شود.
چرا دو Token داشته باشیم؟
ممکن است بپرسید:
چرا یک Token با عمر طولانی نداشته باشیم؟
مثلاً:
JWT
Valid for 30 daysمشکل این است که اگر این Token سرقت شود، مهاجم میتواند برای مدت طولانی از آن استفاده کند.
در مدل Access + Refresh:
Access Token
↓
کوتاهمدت
↓
محدود کردن آسیب در صورت سرقت
Refresh Token
↓
برای دریافت Access Token جدید
↓
نگهداری و کنترل محافظتشدهتراین جداسازی امکان کنترل بیشتری روی چرخه عمر Authentication ایجاد میکند.
Refresh Token دقیقاً چگونه کار میکند؟
فرض کنید کاربر Login کرده است.
Backend پاسخ میدهد:
{
"accessToken": "access-token",
"refreshToken": "refresh-token"
}Access Token برای درخواستهای معمول استفاده میشود.
GET /api/orders
Authorization: Bearer access-tokenبعد از مدتی:
Access Token
↓
ExpiredClient درخواست Refresh میفرستد:
POST /api/auth/refreshBackend Refresh Token را بررسی میکند.
اگر معتبر باشد:
Refresh Token
↓
Validate
↓
Generate New Access Token
↓
Returnکاربر بدون وارد کردن دوباره Password میتواند به کار خود ادامه دهد.
Refresh Token را کجا نگه داریم؟
این قسمت یکی از مهمترین بخشهای طراحی Authentication است.
دو مکان رایج:
localStorage
Cookieاما این دو از نظر امنیتی یکسان نیستند.
برای Tokenهای حساس، بسیاری از معماریهای وب ترجیح میدهند Refresh Token در Cookie با ویژگیهای امنیتی مناسب قرار گیرد، مثلاً:
HttpOnly
Secure
SameSiteHttpOnly
JavaScript سمت Client نمیتواند مستقیماً Cookie را بخواند.
این ویژگی میتواند در برابر برخی سناریوهای سرقت Token از طریق XSS کمککننده باشد.
Secure
Cookie فقط روی HTTPS ارسال میشود.
SameSite
به کنترل ارسال Cookie در درخواستهای Cross-Site کمک میکند و میتواند ریسک برخی حملات CSRF را کاهش دهد.
اما تنظیم صحیح آن به معماری، دامنهها و نحوه ارتباط Frontend و Backend بستگی دارد.
آیا localStorage کاملاً ممنوع است؟
نه.
اما باید ریسک آن را در نظر گرفت.
اگر Access Token در:
localStorageذخیره شود، JavaScript صفحه میتواند آن را بخواند.
در صورت وجود یک XSS موفق، مهاجم ممکن است بتواند Token را استخراج کند.
بنابراین انتخاب محل ذخیره Token باید بر اساس مدل تهدید انجام شود، نه صرفاً یک قانون کلی.
Session چیست؟
Session مفهوم متفاوتی از JWT است.
در Session-based Authentication، سرور اطلاعات Session را نگهداری میکند.
مثلاً:
Browser
↓
Session ID
↓
Backend
↓
Session Storeفرض کنید:
Session ID = abc123سرور در Session Store نگه میدارد:
{
"sessionId": "abc123",
"userId": 42,
"expiresAt": "..."
}Browser فقط Session ID را ارسال میکند.
JWT و Session چه تفاوتی دارند؟
تفاوت اصلی در این است که وضعیت Authentication کجا نگهداری میشود.
در Session:
Client
↓
Session ID
↓
Server
↓
Session Store
↓
User Dataدر JWT:
Client
↓
JWT
↓
Server
↓
Verify Signature
↓
Claimsدر JWT معمولاً اطلاعات لازم برای اعتبارسنجی Token در خود Token قرار دارد و سرور میتواند بدون lookup معمول Session Store آن را Verify کند.
اما این به معنی «کاملاً Stateless بودن کل سیستم» نیست؛ بسیاری از سیستمهای JWT برای Revocation، Refresh Tokenها، Session Management یا سایر نیازها همچنان state سمت سرور دارند.
JWT Stateless است؛ اما این جمله نصف واقعیت است
زیاد میشنویم:
«JWT یعنی Stateless Authentication.»
این جمله در یک سناریوی ساده میتواند درست باشد، اما در سیستمهای واقعی همیشه به این سادگی نیست.
فرض کنید Refresh Tokenها را در Database نگهداری میکنیم:
Refresh Token
↓
Database
↓
User Sessionدر این حالت بخشی از Authentication State در Server وجود دارد.
همچنین اگر بخواهیم Token را Revoke کنیم، ممکن است نیاز به نگهداری اطلاعاتی سمت سرور داشته باشیم.
بنابراین بهتر است بگوییم:
JWT امکان اعتبارسنجی بدون Session Lookup برای Access Token را فراهم میکند، اما کل سیستم Authentication لزوماً Stateless نیست.
JWT چگونه اعتبار Token را بررسی میکند؟
فرض کنید Client این Token را ارسال کرده است:
Authorization: Bearer eyJ...Backend معمولاً مراحل مختلفی را بررسی میکند:
Receive Token
↓
Parse Token
↓
Verify Signature
↓
Check Expiration
↓
Check Issuer / Audience
↓
Read Claims
↓
Authorizationبرای مثال:
{
"sub": "123",
"role": "editor",
"exp": 1800000000
}سرور میتواند بررسی کند:
Token expired?
↓
Role allowed?
↓
User authorized?نکته مهم:
Authentication و Authorization یکی نیستند.
Authentication vs Authorization
Authentication
یعنی:
«تو چه کسی هستی؟»
مثلاً:
User ID = 123Authorization
یعنی:
«چه کاری اجازه داری انجام دهی؟»
مثلاً:
User
→ Read Profile ✓
→ Delete User ✗JWT میتواند Claimهایی مانند Role یا Scope داشته باشد، اما وجود یک Role داخل Token بهتنهایی نباید بدون طراحی درست Authorization به معنی دسترسی قطعی به همه منابع باشد.
Access Token و Refresh Token؛ یک تفاوت مهم
ویژگیAccess TokenRefresh Tokenکاربرددسترسی به APIدریافت Access Token جدیدطول عمرمعمولاً کوتاهمعمولاً طولانیتراستفاده در APIبلهمعمولاً خیرحساسیتبالابسیار بالانیاز به کنترل Revocationبسته به معماریمعمولاً مهمترارسال مداومبلهخیر
یکی از اشتباهات رایج این است که Refresh Token را برای تمام APIها ارسال کنیم.
در معماری معمول:
Access Token
↓
API Requests
Refresh Token
↓
Refresh EndpointRefresh Token Rotation چیست؟
در سیستمهای حساستر میتوان از Refresh Token Rotation استفاده کرد.
ایده ساده است:
Refresh Token A
↓
Refresh
↓
Access Token B
+
Refresh Token Cیعنی Refresh Token قبلی دیگر قابل استفاده نباشد.
اگر مهاجم Token قبلی را داشته باشد و تلاش کند دوباره از آن استفاده کند، Backend میتواند آن را شناسایی کند و Session مربوطه را بررسی یا مسدود کند.
این روش میتواند سطح کنترل و تشخیص سوءاستفاده از Refresh Token را افزایش دهد.
JWT Expiration چیست؟
JWT معمولاً Claimای به نام:
expدارد.
این Claim زمان انقضای Token را مشخص میکند.
مثلاً:
{
"sub": "42",
"exp": 1800000000
}پس Backend میتواند بررسی کند:
Current Time > exp
↓
Token Expiredدر این حالت Access Token باید دوباره با فرآیند Refresh دریافت شود.
چرا نباید Access Token را بیش از حد طولانی کنیم؟
فرض کنید:
Access Token
Valid for 30 daysاگر Token لو برود، مهاجم ممکن است برای مدت طولانی از آن استفاده کند.
اگر:
Access Token
Valid for 10 minutesباشد، پنجره زمانی سوءاستفاده محدودتر است.
اما اینجا یک Trade-off وجود دارد:
Short Lifetime
↓
Security ↑
Refresh Frequency ↑در مقابل:
Long Lifetime
↓
Refresh Frequency ↓
Potential Exposure Window ↑بنابراین طول عمر Token باید متناسب با کاربرد و مدل تهدید انتخاب شود.
JWT در معماری Frontend و Backend
یک معماری رایج میتواند چیزی شبیه این باشد:
┌──────────────┐
│ Frontend │
└──────┬───────┘
│
│ Login
↓
┌──────────────┐
│ Backend │
└──────┬───────┘
│
├── Access Token
│
└── Refresh Tokenسپس:
Frontend
│
│ Authorization: Bearer AccessToken
↓
Backend API
│
↓
Verify Token
│
↓
Return Dataو هنگام Expire شدن:
Access Token Expired
↓
Refresh Endpoint
↓
Refresh Token
↓
New Access Tokenآیا JWT برای همه پروژهها مناسب است؟
خیر.
JWT یک ابزار است، نه یک الزام.
برای بعضی پروژهها Session-based Authentication انتخاب سادهتر و مناسبی است.
برای برخی معماریهای API، SPA، Mobile Application یا سیستمهای توزیعشده، Token-based Authentication میتواند مناسبتر باشد.
تصمیم باید بر اساس مواردی مانند:
Architecture
Security Requirements
Client Types
Scalability
Session Management
Revocation Requirements
Infrastructureگرفته شود.
اشتباهات رایج در استفاده از JWT
۱. قرار دادن اطلاعات حساس داخل Payload
JWT را نباید به عنوان محل امن برای نگهداری Secretها در نظر گرفت.
۲. Access Token با عمر بسیار طولانی
Token طولانیمدت در صورت سرقت، پنجره سوءاستفاده بزرگتری ایجاد میکند.
۳. نگهداری Token حساس بدون بررسی XSS
اگر Token در محلی قرار گیرد که JavaScript بتواند آن را بخواند، یک XSS موفق میتواند خطرناکتر شود.
۴. نداشتن Expiration
Token بدون Expiration میتواند مدت بسیار زیادی معتبر بماند.
۵. اعتماد مستقیم به Payload
اینکه Payload میگوید:
{
"role": "admin"
}به معنی معتبر بودن آن نیست.
Backend باید Signature و سایر شرایط اعتبار Token را بررسی کند.
۶. استفاده از یک Secret ضعیف
اگر الگوریتم و کلید امضای JWT درست مدیریت نشوند، امنیت کل Authentication میتواند تحت تأثیر قرار گیرد.
Secretها باید:
قوی باشند
امن نگهداری شوند
داخل Repository قرار نگیرند
به شکل مناسب Rotate شوند
۷. ارسال Refresh Token برای همه APIها
Refresh Token معمولاً باید فقط در فرآیند Refresh استفاده شود.
JWT در برابر Session؛ کدام بهتر است؟
به جای اینکه بگوییم:
JWT بهتر است.
یا:
Session بهتر است.
بهتر است معماری را مقایسه کنیم.
موضوعJWTSessionنگهداری State سمت سروربرای Access Token میتواند کمتر باشدمعمولاً داردRevocation فوریپیچیدهترمعمولاً سادهترمناسب برای APIهای توزیعشدهمیتواند مناسب باشدنیازمند Session Store مشترک در معماری چندسروریپیچیدگیبسته به معماری میتواند بالا باشدمعمولاً سادهترمدیریت Refreshمعمولاً نیازمند طراحی دقیقبسته به پیادهسازیStateless Access Validationامکانپذیرمعمولاً خیر
بنابراین انتخاب بین JWT و Session بیشتر یک تصمیم معماری است تا یک انتخاب تکنولوژی.
یک معماری متداول و قابل اتکا
برای بسیاری از Web Applicationها میتوان چنین الگویی را در نظر گرفت:
LOGIN
│
↓
┌───────────┐
│ Backend │
└─────┬─────┘
│
┌────────┴────────┐
↓ ↓
Access Token Refresh Token
Short-lived Longer-lived
│ │
│ ↓
│ Secure Cookie
↓
API Requests
│
↓
Verify Token
│
↓
Protected APIو وقتی Access Token منقضی شد:
API Request
↓
401 Unauthorized
↓
Refresh Endpoint
↓
Refresh Token
↓
New Access Token
↓
Retry Requestالبته Client نباید هر 401 را کورکورانه Refresh کند؛ باید شرایط و خطاهای Authentication را از سایر خطاها تفکیک کند تا وارد چرخههای بینهایت نشود.
یک نکته مهم درباره Logout
در یک سیستم JWT ساده، Logout همیشه به معنی «حذف JWT از سرور» نیست؛ چون Access Token ممکن است تا زمان Expiration معتبر باقی بماند.
برای کنترل بهتر Logout میتوان معماریهایی مانند:
Short-lived Access Token
+
Revocable Refresh Token
+
Refresh Token Rotationرا به کار برد.
در این مدل، میتوان Session یا Refresh Token را در Backend باطل کرد و از صدور Access Tokenهای جدید جلوگیری کرد.
JWT را به عنوان یک سیستم کامل ببینید
اشتباه رایج این است که Authentication را فقط این بدانیم:
Login
↓
JWT
↓
Doneدر یک سیستم واقعی، چرخه کامل بیشتر شبیه این است:
Login
↓
Authentication
↓
Issue Tokens
↓
Access API
↓
Access Token Expiration
↓
Refresh
↓
Token Rotation
↓
Logout / Revokeدر کنار آن باید موارد دیگری مانند:
Authorization
Rate Limiting
CSRF Protection
XSS Protection
HTTPS
Secret Management
Session Management
Audit Loggingنیز متناسب با معماری در نظر گرفته شوند.
جمعبندی
JWT در اصل یک روش استاندارد برای انتقال Claimهای امضاشده است؛ اما چیزی که در معماری Authentication اهمیت دارد، نحوه استفاده از آن است.
یک معماری Token-based میتواند به شکل زیر باشد:
Login
↓
Access Token + Refresh Token
↓
Access Token → API
↓
Expire
↓
Refresh Token
↓
New Access TokenAccess Token معمولاً برای دسترسی کوتاهمدت به API استفاده میشود و Refresh Token برای گرفتن Access Token جدید.
در مقابل، Session-based Authentication معمولاً Session را سمت سرور نگهداری میکند و Client یک Session Identifier دریافت میکند.
بنابراین سؤال اصلی این نیست که:
«JWT بهتر است یا Session؟»
بلکه باید پرسید:
«با توجه به معماری پروژه، سطح امنیت، نوع Clientها، نیاز به Revocation و نحوه مدیریت Session، کدام مدل مناسبتر است؟»
اگر JWT انتخاب شود، امنیت آن فقط به خود Token وابسته نیست؛ محل نگهداری Token، طول عمر، Rotation، Revocation، Cookie Configuration، HTTPS و نحوه پیادهسازی Authentication و Authorization همگی بخشی از سیستم امنیتی هستند.
خلاصه مفاهیم
JWT
→ ساختار Token
Access Token
→ دسترسی کوتاهمدت به API
Refresh Token
→ دریافت Access Token جدید
Session
→ نگهداری وضعیت Authentication سمت سرور
Authentication
→ تشخیص هویت کاربر
Authorization
→ تعیین سطح دسترسی کاربرو مهمتر از همه:
JWT خودش یک سیستم Authentication کامل نیست؛ JWT یکی از اجزای یک معماری Authentication است.
