المصادقة عبر JWT في البيئات الإنتاجية: رموز الوصول، تدوير الرموز، والإلغاء الفوري
تصميم نظام مصادقة آمن باستخدام JWT: هيكل التوكن، التخزين الآمن في كوكيز HTTP-only ضد ثغرات XSS، وتدوير الرموز مع كشف الاختراق.
🔐 JWT Authentication: Access Tokens, Refresh Tokens & Rotation
<img src="https://cdn.simpleicons.org/jsonwebtokens/000000" alt="JWT" width="110"/>JWT مش معناه إن الـSystem Secure تلقائيًا
الـSecurity الحقيقي في طريقة تصميم الـauthentication lifecycle بالكامل.
</div>📚 Table of Contents
- الفكرة في دقيقة
- JWT يعني إيه؟
- JWT Structure
- Access Token
- Refresh Token
- Access Token vs Refresh Token
- Authentication Flow
- Access Token Expiration
- 401 ثم Refresh
- أين نخزن الـTokens؟
- HttpOnly Cookies
- XSS vs CSRF
- Refresh Token Rotation
- Refresh Token Reuse Detection
- هل Refresh Token لازم يكون JWT؟
- هل نخزن Refresh Tokens في Database؟
- Logout وRevocation
- Sessions وMultiple Devices
- JWT Statelessness مش مطلقة
- Security Checklist
- Production Architecture
- Common Misconceptions
- الخلاصة
- Further Reading
⚡ الفكرة في دقيقة
ناس كتير أول ما تسمع:
JWT Authentication
تفكر:
"خلاص عندي Token يبقى الدنيا كلها Secure."
لكن أول ما الـAccess Token ينتهي تبدأ المشاكل:
Access Token expired
↓
401 Unauthorized
↓
Frontend confused
↓
Logout?
Refresh?
Retry?
أو تلاقي:
JWT expiration = 30 days 😬
أو:
Refresh Token stored in localStorage 😬
أو:
Refresh Token never rotated 😬
الحقيقة:
JWT مجرد format/token mechanism.
مش Security Architecture كاملة.
الـAuthentication system المحترم محتاج يفكر في:
Token lifetime
+
Storage
+
HTTPS
+
Rotation
+
Revocation
+
XSS
+
CSRF
+
Session management
+
Replay detection
🪪 JWT يعني إيه؟
JWT اختصار لـ:
JSON Web Token
وهو token format شائع لنقل claims بين الأطراف، ويمكن توقيعه للتحقق من سلامة البيانات ومصدرها وفق الخوارزمية المستخدمة.
مثال claims ممكن يحتوي:
{
"sub": "123",
"role": "admin",
"exp": 1787000000
}
ممكن تلاقي claims مثل:
- User ID
- Subject
- Roles
- Permissions
- Issuer
- Audience
- Expiration time
- Issued-at time
لكن مهم جدًا:
JWT signed ≠ JWT encrypted.
لو JWT مجرد signed JWS، محتواه ليس سرًا. الـClient يستطيع عادةً قراءة الـpayload.
لذلك:
JWT
↓
Integrity / Authenticity
مش:
JWT
↓
Confidentiality
لو عندك بيانات سرية جدًا، لا تضعها في JWT لمجرد أنها "مش ظاهرة في UI".
🧩 JWT Structure
الـJWT التقليدي يتكون من 3 أجزاء:
HEADER.PAYLOAD.SIGNATURE
مثال شكلي:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjMiLCJleHAiOjE3ODcwMDAwMDB9
.
signature...
Header
مثلاً:
{
"alg": "HS256",
"typ": "JWT"
}
Payload
مثلاً:
{
"sub": "123",
"role": "admin",
"exp": 1787000000
}
Signature
تقريبًا:
sign(
base64url(header)
+
"."
+
base64url(payload)
)
والسيرفر يتحقق من الـsignature والـclaims قبل الاعتماد على الـtoken.
🎟️ Access Token
الـAccess Token هو credential يستخدمه الـClient للوصول إلى protected resources.
غالبًا يكون:
Authorization: Bearer <access-token>
مثال:
GET /api/profile
Authorization: Bearer eyJ...
السيرفر:
Request
↓
Extract Access Token
↓
Validate
↓
Check claims / permissions
↓
Allow / Reject
ليه Short-Lived؟
لو الـAccess Token اتسرق:
Attacker
↓
Stolen Access Token
↓
API
يقدر يستخدمه حتى انتهاء صلاحيته أو إبطاله حسب تصميم النظام.
عشان نقلل نافذة الخطر، Access Tokens غالبًا تكون قصيرة العمر.
مثال:
5 min
15 min
30 min
1 hour
لكن مفيش رقم سحري مناسب لكل System.
اختيار الـTTL يعتمد على:
- Risk
- UX
- Client type
- Sensitivity
- Revocation requirements
- Threat model
♻️ Refresh Token
الـRefresh Token يستخدم للحصول على Access Token جديد بدون إجبار المستخدم على تسجيل الدخول مرة أخرى.
Flow:
Login
↓
Access Token + Refresh Token
↓
Use Access Token
↓
Access Token expires
↓
Refresh
↓
New Access Token
والنقطة المهمة:
الـRefresh Token ليس مطلوبًا أن يكون JWT.
ممكن يكون:
Opaque random token
وده شائع جدًا عندما تريد أن يكون server-side session state قادرًا على:
- Revocation
- Rotation
- Reuse detection
- Session management
⚖️ Access Token vs Refresh Token
| Header | Access Token | Refresh Token |
|---|---|---|
| الاستخدام | API authorization | إصدار Access Token جديد |
| Lifetime | قصير | أطول |
| يُرسل مع كل API request؟ | غالبًا نعم | لا |
| قيمة السرقة | عالية | أعلى عادةً |
| يحتاج حماية قوية؟ | نعم | نعم، وبشكل أشد |
| Rotation | ممكن | مهم جدًا عند استخدام rotation |
| Revocation | ممكن | مهم جدًا |
| Storage | يعتمد على architecture | غالبًا HttpOnly Secure Cookie في browser apps |
Refresh Token هو credential طويل العمر، وليس "Access Token أكبر".
🔄 Authentication Flow
الصورة الكبيرة:
sequenceDiagram
participant U as User
participant F as Frontend
participant A as Auth API
participant DB as Database
U->>F: Login
F->>A: POST /login
A->>DB: Verify credentials
DB-->>A: User valid
A-->>F: Access Token + Refresh Token
F->>A: API request + Access Token
A-->>F: Protected data
Note over F,A: Access Token expires
F->>A: POST /refresh
A->>DB: Validate refresh session
DB-->>A: Valid
A-->>F: New Access Token + rotated Refresh Token
Note over F,A: User continues without login
⏳ Access Token Expiration
لنفترض:
Access Token
TTL = 15 minutes
بعدها:
Client
↓
API
↓
401 Unauthorized
الـFrontend ممكن يعمل:
401
↓
POST /refresh
↓
New Access Token
↓
Retry original request
لكن لازم تمنع infinite loops:
Request
↓
401
↓
Refresh
↓
401
↓
Refresh
↓
401
↓
💥
اعمل حد واضح:
One refresh attempt
ولو فشل:
Clear auth state
→ Redirect to login
🔁 401 ثم Refresh
Flow شائع:
sequenceDiagram
participant C as Client
participant API as API
participant Auth as Auth / Refresh Endpoint
C->>API: Request + Access Token
API-->>C: 401 Unauthorized
C->>Auth: POST /refresh
Auth-->>C: New Access Token
C->>API: Retry original request
API-->>C: 200 OK
لكن:
مش كل 401 معناها "اعمل refresh".
ممكن تكون:
- Invalid token
- Missing token
- Wrong audience
- Wrong issuer
- Revoked credential
- Expired token
لذلك الـClient logic والـAPI contract لازم يكونوا واضحين.
🍪 أين نخزن الـTokens؟
دي من أهم النقاط الأمنية.
الاختيار مش:
localStorage = good
cookie = bad
الموضوع مرتبط بالـthreat model والـarchitecture.
لكن بالنسبة للـbrowser applications، تخزين credentials طويلة العمر في Web Storage يعرّضها للوصول من JavaScript في نفس origin؛ OWASP تحذر تحديدًا من تخزين authentication tokens / JWTs / refresh tokens في localStorage أو sessionStorage. citeturn0search8
🚨 localStorage
مثال:
localStorage.setItem("refreshToken", token);
المشكلة:
XSS
↓
JavaScript executes
↓
localStorage.getItem(...)
↓
Token stolen
أي malicious JavaScript يعمل في نفس origin يمكنه قراءة Web Storage.
لذلك لا تعتبر:
localStorage
مكانًا آمنًا تلقائيًا للـcredentials.
🔐 HttpOnly Cookies
الـRefresh Token في browser architecture كثيرًا ما يوضع في cookie مثل:
Set-Cookie: __Host-refresh=...
; Path=/
; Secure
; HttpOnly
; SameSite=Lax
HttpOnly
JavaScript لا يستطيع قراءة الـcookie عبر document.cookie.
لكن:
HttpOnly لا يمنع الـbrowser من إرسال الـcookie مع requests.
وهذا هو سبب أن CSRF يجب التفكير فيه عندما تعتمد على cookies للمصادقة. MDN توضح أن HttpOnly يمنع JavaScript من قراءة cookie، بينما يظل المتصفح قادرًا على إرسالها في الطلبات. citeturn0search0turn0search4
🛡️ Cookie Attributes
| Attribute | الهدف |
|---|---|
HttpOnly | منع JavaScript من قراءة cookie |
Secure | إرسالها عبر HTTPS فقط |
SameSite | تقليل cross-site cookie sending |
Path | تحديد نطاق المسارات |
Domain | تحديد النطاق عند الحاجة |
Max-Age | مدة الحياة |
مثال:
Set-Cookie: __Host-refresh=abc123;
Path=/;
Secure;
HttpOnly;
SameSite=Strict;
Max-Age=2592000
__Host- له شروط إضافية: Secure، وPath=/، وعدم وجود Domain. citeturn0search0turn0search4
⚔️ XSS vs CSRF
لازم تفرق بينهم.
XSS
المشكلة:
Attacker
↓
Malicious JavaScript
↓
Runs inside your origin
لو الـtoken موجود في:
localStorage
الـscript ممكن يقرأه.
لكن:
HttpOnly Cookie
يمنع JavaScript من قراءة قيمة الـcookie مباشرة.
لكن HttpOnly لا يعني أن XSS انتهى.
الـXSS ما زال قادرًا في بعض السيناريوهات على تنفيذ requests من داخل application context، لذلك منع XSS نفسه يظل مهمًا.
CSRF
المشكلة مختلفة:
Malicious Site
↓
Browser
↓
Target Site
↓
Cookie automatically attached
لأن المتصفح هو الذي يدير cookies.
لذلك:
HttpOnly
لا يحل CSRF.
🧱 CSRF Protection
الخيارات تختلف حسب الـarchitecture، لكن ممكن تستخدم:
SameSite=StrictSameSite=Lax- CSRF tokens
- Origin / Fetch Metadata checks
- Non-simple requests + CORS policy
- BFF architecture
SameSite يوفر defense-in-depth ضد CSRF، لكنه ليس دفاعًا كاملًا في كل الحالات. MDN توصي بتحديد SameSite صراحةً، وتوضح الفرق بين Strict وLax وNone. citeturn0search3turn0search5
مهم
لو عندك:
SameSite=None
لازم:
Secure
لأن SameSite=None يتطلب secure context/HTTPS. citeturn0search0turn0search3
🔄 Refresh Token Rotation
دي من أهم الأفكار في الـModern Authentication.
بدل:
Refresh Token A
↓
Refresh
↓
Refresh Token A
↓
Refresh
↓
Refresh Token A
نعمل:
Refresh Token A
↓
Refresh
↓
Refresh Token B
↓
Refresh
↓
Refresh Token C
يعني:
Old Token
↓
Consumed
↓
New Token
ليه؟
لأن لو Refresh Token قديم اتسرق، نريد أن يكون عندنا طريقة لاكتشاف استخدامه مرة أخرى، بدل ما يفضل credential صالحًا بلا نهاية.
🕵️ Refresh Token Reuse Detection
دي النقطة الأقوى في rotation.
لنفترض:
User owns:
Refresh Token A
المستخدم يعمل refresh:
A → B
الـA أصبح قديمًا.
المهاجم عنده نسخة من A:
Attacker
↓
Uses A again
السيرفر يكتشف:
A was already rotated
وده ممكن يكون مؤشر على:
Refresh Token Reuse
ساعتها الـserver ممكن حسب الـdesign:
Revoke token family
↓
Revoke session
↓
Force re-authentication
↓
Security event / alert
وده أقوى بكثير من مجرد تغيير token كل مرة بدون tracking.
🧬 Token Family
ممكن تمثل الـrotation كسلسلة:
Token A
│
└──→ Token B
│
└──→ Token C
│
└──→ Token D
لو ظهر:
Old Token B
بعد ما وصلنا إلى:
D
السيرفر يعرف إن فيه reuse.
وده يتطلب server-side state أو آلية موثوقة لتتبع الـtoken/session family.
🪪 هل Refresh Token لازم يكون JWT؟
لا.
ممكن يكون:
JWT
أو:
Opaque Random Token
والـopaque token أحيانًا يكون أبسط لإدارة:
- Revocation
- Rotation
- Session state
- Reuse detection
مثال:
Refresh Token
↓
random 256-bit value
↓
stored / hashed server-side
وبالتالي الـClient لا يحتاج أن يعرف أي claims داخله.
🗄️ هل نخزن Refresh Tokens في Database؟
ممكن، وده مفيد جدًا.
رغم إن الـJWT مشهور بفكرة:
Stateless
إلا إن authentication system قد يحتاج state.
مثلاً:
refresh_sessions
-----------------------------
id
user_id
token_hash
family_id
expires_at
revoked_at
created_at
last_used_at
device_info
الفكرة ليست تخزين الـraw token نفسه بالضرورة.
ممكن تخزن:
hash(refresh_token)
وتتحقق من الـincoming token بعد hashing/lookup وفق تصميمك.
ليه نخزن state؟
عشان تقدر تعمل:
- Revoke
- Logout
- Logout all devices
- Device management
- Session listing
- Rotation
- Reuse detection
- Compromise response
🚪 Logout وRevocation
لو Access Token JWT قصير العمر، بمجرد إصداره قد يكون valid حتى expiry ما لم يكن عندك revocation mechanism.
لذلك logout في architecture تعتمد على Refresh Tokens غالبًا يحتاج على الأقل:
Client
↓
POST /logout
↓
Revoke refresh session
↓
Delete refresh cookie
لكن:
Revoking Refresh Token لا يعني تلقائيًا أن Access Token الحالي أصبح invalid.
لو تحتاج immediate invalidation للـAccess Token، تحتاج strategy إضافية مثل:
- Short TTL
- Token denylist
- Session version
- Introspection
- Server-side authorization state
وكل واحدة لها cost.
📱 Sessions وMultiple Devices
لو المستخدم داخل من:
Chrome
Android
Laptop
Tablet
ممكن يكون عنده:
Session A
Session B
Session C
Session D
كل Session ممكن يكون لها:
session_id
refresh token family
device
created_at
last_used_at
expires_at
revoked_at
فتقدر تعمل:
Logout this device
أو:
Logout all devices
بدون قتل كل authentication state بشكل عشوائي.
🧠 JWT Statelessness مش مطلقة
ناس كتير تقول:
"JWT = Stateless = مفيش Database."
مش بالضرورة.
ممكن الـAccess Token validation يكون stateless:
Request
↓
Verify signature
↓
Validate claims
↓
Authorize
لكن الـsystem كله قد يظل stateful بسبب:
Refresh Sessions
Revocation
User Permissions
Account Status
Device Sessions
Security Events
يعني:
JWT Access Token
↓
Potentially stateless validation
لا يعني:
Entire Authentication System
↓
Stateless
🏗️ Production Architecture
Architecture شائعة للـbrowser application:
flowchart TD
U[User] --> F[Frontend]
F --> A[Auth API]
A --> DB[(User / Session DB)]
A -->|Short-lived| AT[Access Token]
A -->|Long-lived credential| RT[HttpOnly Secure Cookie]
F --> API[Protected API]
AT --> API
API --> S[Services]
S --> DB
F -->|Access token expired| REF[POST /refresh]
REF --> A
A -->|Rotate| RT
A -->|New access token| F
🔁 Complete Login → Refresh Flow
sequenceDiagram
participant User
participant Frontend
participant Auth as Auth Server
participant DB as Session DB
participant API as Resource API
User->>Frontend: Login
Frontend->>Auth: POST /login
Auth->>DB: Verify user
DB-->>Auth: Valid
Auth-->>Frontend: Access Token
Auth-->>Frontend: Set-Cookie Refresh Token
Frontend->>API: Request + Bearer Access Token
API-->>Frontend: 200 OK
Note over Frontend,API: Access Token expires
Frontend->>API: Request + expired token
API-->>Frontend: 401
Frontend->>Auth: POST /refresh + Cookie
Auth->>DB: Validate session / token family
DB-->>Auth: Valid
Auth->>DB: Rotate old refresh token
Auth-->>Frontend: New Access Token
Auth-->>Frontend: Set-Cookie New Refresh Token
Frontend->>API: Retry request
API-->>Frontend: 200 OK
🚨 Reuse Attack Flow
sequenceDiagram
participant User
participant Browser
participant Attacker
participant Auth as Auth Server
participant DB as Session DB
User->>Auth: Refresh with Token A
Auth->>DB: Consume A
Auth-->>Browser: Token B
Note over Attacker: Attacker stole old Token A
Attacker->>Auth: Refresh with old Token A
Auth->>DB: Check token family
DB-->>Auth: A already rotated
Auth->>DB: Revoke token family
Auth-->>Attacker: Reject
🔐 Security Checklist
Token Design
- Access Token short-lived
- Refresh Token longer-lived but protected
- Do not put sensitive secrets inside JWT payload
- Validate signature
- Validate
exp - Validate
iss - Validate
audwhen applicable - Restrict accepted algorithms
- Do not blindly trust JWT claims
Browser Storage
- لا تخزن Refresh Tokens في
localStorage - تجنب تخزين authentication credentials في Web Storage
- استخدم
HttpOnlyعند ملاءمة الـarchitecture - استخدم
Secure - حدد
SameSite - قلل cookie lifetime
- استخدم restrictive
Path/Domain - فكر في
__Host-prefix
OWASP توصي بعدم تخزين JWTs أو refresh tokens أو session credentials في localStorage/`sessionStorage بسبب تعرضها لأي JavaScript يعمل على نفس الـorigin. citeturn0search8
Refresh Tokens
- Rotation
- Reuse detection
- Token family tracking
- Server-side revocation
- Expiration
- Logout support
- Logout all devices
- Device/session management
Browser Security
- HTTPS everywhere
- XSS prevention
- CSP where appropriate
- CSRF protection when using cookies
- Explicit CORS policy
- Never use GET for state-changing actions
- Validate
Origin/ Fetch Metadata where appropriate
MDN توضح أن SameSite مجرد defense-in-depth، وأن التطبيقات التي تعتمد على cookies للمصادقة تحتاج تصميم CSRF protection مناسبًا. citeturn0search5turn0search9
❌ Common Misconceptions
"JWT = Secure"
لا.
JWT مجرد token format.
Security تعتمد على:
JWT
+
Validation
+
Storage
+
Expiration
+
Transport Security
+
Rotation
+
Revocation
+
XSS/CSRF defenses
"Refresh Token مجرد Access Token طويل"
لا.
الـRefresh Token له وظيفة مختلفة:
Access Token
→ access protected resources
Refresh Token
→ obtain new access credentials
"Refresh Token لازم يكون JWT"
لا.
Opaque random tokens ممكن تكون اختيارًا ممتازًا.
"HttpOnly يمنع XSS"
لا.
هو يمنع JavaScript من قراءة قيمة الـcookie مباشرة.
لكن XSS نفسه يظل vulnerability خطيرة.
"HttpOnly يمنع CSRF"
لا.
الـBrowser ما زال ممكن يرسل cookie تلقائيًا.
لذلك CSRF protection تظل مهمة عندما تكون authentication مبنية على cookies.
"SameSite حل كامل للـCSRF"
لا.
هو defense-in-depth، والاختيار بين Strict وLax وNone يعتمد على الـapplication architecture. citeturn0search5turn0search3
"JWT Stateless يعني مفيش Database"
لا.
ممكن Access Token validation تكون stateless بينما Refresh Sessions وRevocation تكون stateful.
"Logout يبطل JWT فورًا"
مش بالضرورة.
لو Access Token stateless ولم ينته بعد، revoking refresh session لا يلغي الـAccess Token تلقائيًا.
"كل 401 لازم يعمل Refresh"
لا.
لازم تفرق بين:
Expired
Invalid
Revoked
Missing
Wrong audience
Wrong issuer
والـClient لازم يمنع infinite refresh loops.
🧭 اختيار Storage حسب Architecture
| Architecture | Access Token | Refresh / Session Credential |
|---|---|---|
| Traditional server-rendered web | Server session | Secure HttpOnly cookie |
| SPA + BFF | BFF-managed session | HttpOnly Secure cookie |
| SPA direct API | Short-lived token strategy | Carefully designed refresh flow |
| Mobile native app | OS secure storage | OS secure storage / provider-specific strategy |
| Service-to-service | Client credentials / signed tokens | Depends on protocol |
مفيش storage strategy واحدة تصلح لكل client type.
🎯 الخلاصة
لو فهمت الـflow ده:
LOGIN
↓
Access Token + Refresh Token
↓
API Requests
↓
Access Token expires
↓
401
↓
/refresh
↓
Validate Refresh Session
↓
Rotate Refresh Token
↓
Issue New Access Token
↓
Continue
فأنت فهمت جزء كبير من الـmodern authentication lifecycle.
لكن الجزء الأهم هو:
JWT
≠
Security
الأمان الحقيقي يأتي من:
Short-lived Access Tokens
+
Protected Refresh Tokens
+
Secure Storage
+
HTTPS
+
Rotation
+
Reuse Detection
+
Revocation
+
XSS Protection
+
CSRF Protection
+
Session Management
وأهم سؤال مش:
"هل أستخدم JWT؟"
لكن:
"إزاي أصمم authentication lifecycle آمن وقابل للإدارة لو token اتسرق، session اتلغت، device اتغير، أو attack حصل؟"
وده الفرق بين:
I know how to generate a JWT
و:
I understand authentication architecture.
📚 Further Reading
OWASP
MDN
JWT
OAuth / Browser Apps
<div align="center">
🔐 A token is not a security architecture.
Design the whole authentication lifecycle.
</div>منشورات مقترحة
مشاريع ذات صلة
منصة عقارية متكاملة تدعم دورة حياة الإعلانات، وإشعارات الواتساب المباشرة، والبحث الجغرافي بالخريطة في القاهرة والجيزة.