Token Lifecycle
Ithbat IAM يصدر 3 أنواع من الـ Tokens عند نجاح المصادقة: Access Token و Refresh Token و ID Token. فهم آلية عمل هذه الـ Tokens وانتهاء صلاحيتها وتدويرها أمر أساسي لبناء integration آمن وموثوق.
أنواع الـ Tokens
Access Token
| الخاصية | القيمة |
|---|---|
| الصيغة | JWT (RS256) |
| الصلاحية الافتراضية | ساعة واحدة (OIDC_ACCESS_TOKEN_LIFESPAN) |
| الاستخدام | يُرسل كـ Authorization: Bearer <token> مع كل API Request |
الـ Access Token هو JWT موقّع. يحتوي على identity claims المستخدم ويتحقق منه API server باستخدام public key المستفيد. يجب على الـ backend لديك التحقق من JWT signature في كل Request — لا تستدعي Ithbat IAM في كل Request للتحقق من الـ Tokens.
Standard claims:
{
"sub": "usr_01J8X...",
"iss": "https://api.ithbat.io",
"aud": "https://api.ithbat.io",
"exp": 1740400800,
"iat": 1740397200,
"jti": "tok_01J8X..."
}
Custom claims (Ithbat extensions):
{
"tenant_id": "ten_01J8X...",
"email": "[email protected]",
"display_name": "Sara Al-Rashidi",
"roles": ["admin"],
"permissions": ["user:read", "user:write"],
"region": "ksa"
}
Refresh Token
| الخاصية | القيمة |
|---|---|
| الصيغة | Opaque (مُجزّأ، ليس JWT) |
| الصلاحية الافتراضية | 30 يومًا (OIDC_REFRESH_TOKEN_LIFESPAN = 720h) |
| التدوير | أحادي الاستخدام — Refresh Token جديد يُصدر مع كل عملية refresh |
الـ Refresh Token يُخزَّن على السيرفر كـ SHA-256 hash. لا يمكن فك ترميزه لكشف معلومات المستخدم. إذا Refresh Token مستهلَك أُعيد استخدامه، السيرفر يلغي عائلة الـ Tokens كاملة (rotation with reuse detection).
ID Token
| الخاصية | القيمة |
|---|---|
| الصيغة | JWT (RS256, OIDC-compliant) |
| الاستخدام | Client-side identity assertions، ليس للـ API authorization |
الـ ID Token يتبع مواصفات OIDC ويحتوي معلومات profile المستخدم. استخدمه لعرض معلومات المستخدم في واجهتك. لا تستخدمه كـ authorization credential لـ API calls.
Token Refresh Flow
عند انتهاء صلاحية الـ Access Token، استخدم الـ Refresh Token للحصول على زوج جديد دون أن تطلب من المستخدم تسجيل الدخول مجدداً.
POST /api/v1/auth/refresh
الـ Request
{
"refreshToken": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4..."
}
الـ Response — 200 OK
{
"accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "bmV3UmVmcmVzaFRva2Vu...",
"idToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"tokenType": "Bearer",
"expiresIn": 3600
}
الـ Refresh Token القديم يُلغى فورًا بعد refresh ناجح. خزّن refreshToken الجديد من الـ Response. إعادة استخدام القديم تفعّل reuse detection وتلغي كل Sessions المستخدم.
sequenceDiagram
participant Client
participant API as Ithbat IAM
Client->>API: POST /api/v1/auth/refresh { refreshToken }
API->>API: Validate refresh token, check expiry
API->>API: Generate new access token + new refresh token
API->>API: Revoke old refresh token
API-->>Client: { accessToken, refreshToken, idToken, expiresIn }
Client->>Client: Store new tokens, discard old
Token Validation
تحقق من Access Tokens في الـ backend لديك باستخدام JWKS Endpoint الخاص بالمستفيد.
JWKS Endpoint:
GET https://api.ithbat.io/.well-known/jwks.json
OIDC Discovery:
GET https://api.ithbat.io/.well-known/openid-configuration
أغلب JWT libraries تدعم JWKS key rotation تلقائيًا. هيّئها لجلب المفاتيح من JWKS Endpoint بدلاً من تثبيت public key في الكود.
خزّن JWKS Response مؤقتًا وأعد جلبه عند مواجهة kid في Token غير موجود في الـ cache لديك. هذا يتجنب network call في كل Request مع التعامل مع key rotation.
إدارة الـ Sessions
كل عملية تسجيل دخول تنشئ Session على السيرفر تُتتبع في Redis. الـ Sessions تخزّن metadata (IP address, device type, location) للرؤية الأمنية وتتيح الإلغاء المستهدف.
عرض الـ Sessions
GET /api/v1/auth/sessions
Header مطلوب: Authorization: Bearer <accessToken>
مرّر X-Session-ID لتحديد الـ Session الحالية (تُعلَّم بـ isCurrent: true في الـ Response).
الـ Response — 200 OK
[
{
"id": "sess_01J8X...",
"deviceType": "desktop",
"deviceName": "Chrome on macOS",
"ipAddress": "41.64.12.100",
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...",
"location": {
"country": "SA",
"city": "Riyadh"
},
"createdAt": "2026-02-20T08:30:00Z",
"lastActivity": "2026-02-24T09:45:00Z",
"isCurrent": true
},
{
"id": "sess_01J9Y...",
"deviceType": "mobile",
"deviceName": "Safari on iPhone",
"ipAddress": "197.34.11.55",
"userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0)...",
"location": {
"country": "EG",
"city": "Cairo"
},
"createdAt": "2026-02-18T14:00:00Z",
"lastActivity": "2026-02-23T20:10:00Z",
"isCurrent": false
}
]
إلغاء Session محددة
DELETE /api/v1/auth/sessions/{id}
Header مطلوب: Authorization: Bearer <accessToken>
الـ Response — 200 OK
{
"message": "Session revoked successfully"
}
إلغاء كل الـ Sessions الثانية
يلغي كل الـ Sessions إلا الحالية. مفيد لـ "تسجيل الخروج من كل الأجهزة الثانية".
DELETE /api/v1/auth/sessions
Header مطلوب: Authorization: Bearer <accessToken>
الـ Response — 200 OK
{
"message": "All other sessions revoked successfully"
}
التحديث التلقائي (SDK)
الـ Ithbat SDK يتولى Token refresh تلقائيًا. إذا Request فشل بـ 401 Unauthorized بسبب Access Token منتهي، الـ SDK يعترض الـ Response، يحدّث الـ Token، ويعيد محاولة الـ Request الأصلي بشفافية.
import { IthbatSDK } from '@ithbatiam/sdk';
const ithbat = new IthbatSDK({
tenantId: 'ten_01J8X...',
baseUrl: 'https://api.ithbat.io',
// SDK manages token storage and refresh automatically
storage: 'localStorage', // or 'memory' for server-side apps
});
// This request will transparently refresh the token if needed
const user = await ithbat.users.getMe();
التحديث اليدوي (بدون SDK)
async function refreshTokens(refreshToken) {
const resp = await fetch('https://api.ithbat.io/api/v1/auth/refresh', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ refreshToken }),
});
if (!resp.ok) {
// Refresh token is expired or invalid — redirect to login
throw new Error('Session expired');
}
const tokens = await resp.json();
// Store tokens.accessToken and tokens.refreshToken
return tokens;
}
أفضل ممارسات تخزين الـ Tokens
| خيار التخزين | خطر XSS | خطر CSRF | التوصية |
|---|---|---|---|
localStorage | عالي (JavaScript يوصله) | لا يوجد | تجنّبه للتطبيقات الحساسة |
sessionStorage | عالي (JavaScript يوصله) | لا يوجد | مقبول لـ session-scoped SPAs |
HttpOnly cookie | لا يوجد | متوسط (خفّفه بـ SameSite) | الخيار المفضّل لتطبيقات الويب |
| In-memory (variable) | لا يوجد (يُمسح عند refresh) | لا يوجد | الأفضل لـ SPAs ذات Sessions قصيرة |
لتطبيقات الويب، خزّن الـ Access Token في الذاكرة والـ Refresh Token في HttpOnly cookie مع Secure و SameSite=Strict. الـ Access Token قصير الصلاحية (ساعة واحدة) ويمكنك الحصول عليه من جديد من refresh cookie عند تحديث الصفحة.
لا تخزّن Tokens أبدًا في localStorage لتطبيقات تتعامل مع بيانات حساسة. ثغرات XSS يمكنها تسريب جميع الـ Tokens من localStorage بصمت.
أفضل ممارسات الأمان
Refresh Token Rotation
Ithbat IAM يستخدم strict refresh token rotation. كل refresh يلغي الـ Token السابق ويصدر واحد جديد. إذا النظام اكتشف إن الـ Token القديم اُستخدم مرة ثانية بعد الـ rotation، كل Sessions المستخدم تُلغى فورًا.
إلغاء الـ Tokens عند تغيير كلمة المرور
إذا المستخدم غيّر كلمة مروره مع revoke_other_sessions: true، كل Refresh Tokens للمستخدم تُلغى. الـ Access Tokens النشطة تظل صالحة حتى تنتهي صلاحيتها الطبيعية (حتى ساعة). إذا تحتاج إلغاء فوري، قلّل قيمة OIDC_ACCESS_TOKEN_LIFESPAN.
إلغاء الـ Tokens عند تسجيل الخروج
POST /api/v1/auth/logout تلغي كل Refresh Tokens وتحذف كل الـ Sessions على السيرفر للمستخدم. الـ Access Tokens النشطة ما تنلغي استباقيًا — تظل صالحة حتى تنتهي. صمّم نظامك بحيث Access Token expiry يكون قصير (15-60 دقيقة) إذا هذا مهم لك.
أمثلة الكود
cURL — تحديث الـ Tokens
curl -X POST https://api.ithbat.io/api/v1/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refreshToken": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4..."}'
cURL — عرض الـ Sessions
curl https://api.ithbat.io/api/v1/auth/sessions \
-H "Authorization: Bearer eyJhbGci..." \
-H "X-Session-ID: sess_01J8X..."
cURL — إلغاء كل الـ Sessions الثانية
curl -X DELETE https://api.ithbat.io/api/v1/auth/sessions \
-H "Authorization: Bearer eyJhbGci..." \
-H "X-Session-ID: sess_01J8X..."
JavaScript — فك ترميز Access Token Claims
function decodeJwtPayload(token) {
const [, payloadB64] = token.split('.');
const json = atob(payloadB64.replace(/-/g, '+').replace(/_/g, '/'));
return JSON.parse(json);
}
const claims = decodeJwtPayload(accessToken);
console.log(claims.sub); // usr_01J8X...
console.log(claims.tenant_id); // ten_01J8X...
console.log(claims.roles); // ['admin']
console.log(claims.permissions); // ['user:read', 'user:write']
console.log(new Date(claims.exp * 1000)); // expiry date
معالجة الأخطاء
| HTTP Status | Error Code | السبب |
|---|---|---|
400 | VALIDATION_ERROR | حقل refreshToken ناقص |
401 | UNAUTHORIZED | Refresh Token ليس موجود (مستخدَم أو مُلغى) |
401 | UNAUTHORIZED | Refresh Token منتهي الصلاحية (30 يوم انقضت) |
403 | FORBIDDEN | حساب المستخدم موقوف أو معطّل |
الخطوات التالية
- البريد وكلمة المرور — كيف تُصدر الـ Tokens أول مرة
- MFA — كيف MFA تأثر على مسار تسجيل الدخول وإصدار الـ Tokens
- WebAuthn / Passkeys — Passwordless login يرجع نفس Token structure
- Social Login — Social Login يرجع نفس Token structure بالضبط