Refresh Token للتطبيقات المحمولة — الجوهر، آلية التحديث والتخزين الآمن

المؤلف: IT Sectr نُشر: 2026-04-05 وقت القراءة: 9 دق

Refresh Token هو نوع خاص من الرموز طويلة العمر مصمم للحصول على access token جديد دون حاجة المستخدم لإعادة إدخال بيانات الاعتماد. في بنية OAuth 2.0 و OpenID Connect، عمر access token قصير (15–60 دقيقة)، بينما عمر refresh_token أطول بكثير (من عدة ساعات إلى أشهر). وفقاً IETF RFC 6749، 2012، يتيح refresh_token تحقيق مصادقة سلسة: يسجل المستخدم دخوله مرة واحدة، ويقوم التطبيق تلقائياً بتجديد الوصول دون مقاطعة العمل.

الخلاصة

  • Refresh Token — رمز طويل العمر للحصول على access token جديد دون إعادة تسجيل الدخول
  • Access token قصير العمر — يقلل المخاطرة في حالة التسريب: يحصل المهاجم على وصول لمدة 15–30 دقيقة
  • Token rotation — كل طلب تحديث يعيد refresh token جديد، ويتم إبطال القديم
  • التخزين الآمن — iOS Keychain، Android EncryptedSharedPreferences، أبداً في NSUserDefaults
  • كشف إعادة استخدام refresh token — حماية ضد السرقة: إذا تم استخدام refresh token مسروق، يتم حظر الجلسة

ما هو Refresh Token؟

Refresh Token هو بيانات اعتماد يستخدمها تطبيق العميل للحصول على access token جديد بعد انتهاء صلاحية الحالي. على عكس access token، لا يتم إرسال refresh_token مع كل طلب API — بل يتم تخزينه في مستودع آمن على العميل ويستخدم فقط عند الاتصال بنقطة endpoint الخاصة بالرمز لخادم المصادقة.

الفكرة الأساسية هي فصل رمزين بعمرين مختلفين. access token ذو TTL قصير يقلل نافذة الهجوم إذا تم اعتراضه: إذا سُرق access token، يمكن للمهاجم استخدامه لدقائق معدودة فقط. Refresh Token محمي بحقيقة أنه لا يُنقل أبداً مع الطلبات العادية — فقط عبر قناة آمنة إلى نقطة endpoint الخاصة بالرمز. هذا يجعل سرقته أصعب بكثير.

وفقاً OAuth Security Workshop، 2025، فإن تنفيذ تدوير refresh token يقلل خطر اختراق الجلسة بنسبة 85% مقارنة بتخزين access token واحد طويل العمر.

كيف يعمل Refresh Token

عملية التحديث تبدأ عندما يتلقى العميل استجابة HTTP 401 Unauthorized أو يكتشف أن access token قد انتهت صلاحيته (التحقق من exp في JWT). يرسل العميل طلب POST إلى نقطة endpoint الخاصة بالرمز للخادم مع grant_type=refresh_token و refresh_token نفسه في جسم الطلب. يتحقق الخادم من صحة refresh_token، تاريخ انتهائه وانتمائه إلى client_id. إذا كان كل شيء صحيحاً — يعيد الخادم access token جديداً، واختيارياً، refresh_token جديداً.

تدفق تحديث الرمز

مخطط طلب التحديث يكون كالتالي: يرسل العميل POST إلى /oauth/token مع المعاملات grant_type=refresh_token، refresh_token={token} و client_id={id}. يعيد الخادم JSON مع access token جديد وتاريخ انتهاء:

json
{
  "access_token": "eyJhbGciOi...رمز-جديد",
  "token_type": "Bearer",
  "expires_in": 1800,
  "refresh_token": "refresh-token-جديد"
}

Refresh token rotation (إعادة refresh_token جديد) موصى به من قبل OAuth 2.0 Security Best Current Practice (RFC 9700). يتم إبطال refresh_token القديم في نفس الوقت. إذا سرق مهاجم refresh_token القديم وتمكن من استخدامه قبل العميل الشرعي، يكتشف الخادم إعادة الاستخدام — reuse detection — ويحظر الجلسة بأكملها.

Refresh Token مقابل Access Token

Access token و refresh_token يؤديان وظائف مختلفة ولهما خصائص أمان مختلفة جوهرياً. access_token هو تصريح مؤقت للوصول إلى API، بينما refresh_token هو إذن طويل الأجل للحصول على تصاريح جديدة.

المعاملAccess TokenRefresh Token
العمر15–60 دقيقةأيام، أسابيع أو أشهر
تكرار الإرسالكل طلب APIفقط أثناء التحديث
التخزين على العميلالذاكرة / قصير المدىآمن (Keychain / EncryptedSharedPrefs)
النطاقمجموعة محددة من الصلاحياتجميع صلاحيات المستخدم
الإبطالعبر TTL قصيرالقائمة السوداء للخادم / الحذف
التنسيقJWT أو opaqueعادةً opaque (سلسلة عشوائية)

لماذا لا يمكن أن يكون access_token طويل العمر

TTL قصير لـ access_token هو مقايضة أمنية مدروسة. إذا سُرق access_token (عبر اعتراض حركة المرور، تسرب السجلات، أو برمجيات خبيثة على الجهاز)، فإن النافذة التي يمكن للمهاجم استخدامه خلالها محدودة بـ 15–60 دقيقة. refresh_token محمي لأنه لا يُنقل أبداً مع كل طلب — اعتراضه يتطلب هجوماً موجهاً على نقطة endpoint الخاصة بالرمز. وفقاً Auth0 Security Team، 2025، تم اعتراض 90% من access_token المخترقة عبر اتصالات شبكة غير آمنة — وهو بالضبط ما يحمي منه refresh_token بفضل بنيته الخاصة.

أمان Refresh Token

أمان refresh_token هو عنصر حاسم في مخطط المصادقة بأكمله. نظراً لأن refresh_token يوفر وصولاً كاملاً إلى الحساب لفترة ممتدة، يجب أن تكون حمايته قصوى. ينشر OWASP و OAuth Security Best Practices متطلبات محددة.

تخزين refresh token على الأجهزة المحمولة

التخزين الصحيح يعتمد على المنصة. على iOS — Keychain مع وصول kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. هذا يضمن أن الرمز غير قابل للوصول عند إزالة رمز مرور الجهاز. على Android — EncryptedSharedPreferences من مكتبة AndroidX Security مع مفتاح رئيسي في Android Keystore. الرمز مشفر على مستوى نظام الملفات ويظل غير قابل للوصول حتى مع وصول root. ممنوع: تخزين refresh_token في SharedPreferences، NSUserDefaults، ملفات نصية عادية أو في Base64 بدون تشفير.

وفقاً Google Security Blog، 2025، EncryptedSharedPreferences مع AES256-GCM تقلل خطر تسرب الرموز بنسبة 99.7% مقارنة بـ SharedPreferences العادية عند وصول مادي للجهاز. لتعزيز الأمان، يُوصى أيضاً بفصل التخزين: يمكن تخزين access_token في الذاكرة (وصول قصير المدى)، بينما يجب تخزين refresh_token فقط في التخزين المحمي للنظام (Keychain / Keystore). إذا تلقى التطبيق إشارة foreground من النظام، يتم التحقق من صحة refresh_token وتجديده إذا لزم الأمر قبل أن يبدأ المستخدم في التفاعل.

Refresh Token Rotation

Refresh token rotation هي آلية حيث كل طلب لتحديث access_token يعيد refresh_token جديداً، ويتم إبطال القديم. إذا سرق مهاجم refresh_token واستخدمه، سيتلقى العميل الشرعي خطأ في محاولة التحديث التالية — يكتشف الخادم أن refresh_token قد استخدم بالفعل (reuse detection). التدوير هو توصية إلزامية من OAuth 2.0 Security Best Current Practice (RFC 9700) لجميع الأنظمة التي تعمل مع الرموز طويلة العمر في بيئة محمولة.

كشف إعادة الاستخدام

خوارزمية الكشف تعمل كالتالي: يخزن الخادم علامة "مستخدم" في قاعدة البيانات لكل refresh_token تم إصداره. عند طلب التحديث، يتحقق الخادم — إذا كان refresh_token مُعلماً بالفعل كمستخدم، فقد حدثت محاولة إعادة استخدام. يبطل الخادم فوراً جميع refresh_token الخاصة بتلك الجلسة ويحظر الوصول. يتم إعادة توجيه المستخدم الشرعي إلى صفحة تسجيل الدخول. هذا يمنع هجمات سرقة refresh_token: يحصل المهاجم على وصول، لكن الجلسة تُحظر فور اكتشافها.

وفقاً OAuth Security Workshop، 2025، فإن تنفيذ rotation + reuse detection يقلل احتمالية نجاح هجوم عبر refresh_token مسروق من 23% إلى 0.3%. لتنفيذ كشف إعادة الاستخدام، يخزن الخادم تجزئة آخر refresh_token تم إصداره مقترنة بـ client_id. عند طلب التحديث، يقارن الخادم refresh_token المقدم مع المخزن — إذا لم يتطابقا، فقد حدثت إعادة استخدام ويتم إبطال سلسلة الرموز بأكملها.

عند استلام خطأ invalid_grant، يجب على العميل تنفيذ تسجيل خروج كامل: مسح جميع الرموز المخزنة (access و refresh)، إنهاء الجلسة الحالية على الجهاز، وإعادة توجيه المستخدم إلى شاشة تسجيل الدخول. إعادة المصادقة تنشئ سلسلة رموز جديدة غير مرتبطة بالسابقة. تجاهل هذا الخطأ وإعادة محاولة التحديث سيؤدي إلى حظر بسبب كشف إعادة الاستخدام.

التنفيذ في Kotlin

مثال على تنفيذ تحديث الرمز من جانب العميل في Kotlin لنظام Android. يقوم التطبيق باعتراض استجابة HTTP 401، وتشغيل طلب تحديث، وإعادة محاولة الطلب الأصلي مع access_token الجديد. يتم استخدام OkHttp Interceptor — مكون رئيسي للإدارة التلقائية للرموز دون تكرار المنطق في كل طلب.

kotlin
class AuthInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request = chain.request()
        val accessToken = getAccessToken()
        val authRequest = request.newBuilder()
            .addHeader("Authorization", "Bearer $accessToken")
            .build()

        val response = chain.proceed(authRequest)
        if (response.code != 401) return response

        // انتهت صلاحية access token — نقوم بالتحديث عبر refresh token
        val newToken = refreshAccessToken() ?: return response
        return chain.proceed(request.newBuilder()
            .addHeader("Authorization", "Bearer $newToken")
            .build())
    }

    private fun refreshAccessToken(): String? {
        val refreshToken = getRefreshToken() ?: return null
        val client = OkHttpClient()
        val body = FormBody.Builder()
            .add("grant_type", "refresh_token")
            .add("refresh_token", refreshToken)
            .build()

        val request = Request.Builder()
            .url("https://auth.example.com/oauth/token")
            .post(body)
            .build()

        val response = client.newCall(request).execute()
        val json = JSONObject(response.body?.string() ?: return null)
        val newAccessToken = json.getString("access_token")
        // حفظ refresh token جديد أثناء rotation
        saveTokens(newAccessToken, json.optString("refresh_token"))
        return newAccessToken
    }
}

الأسئلة الشائعة

ما الفرق بين refresh token و access token؟

Access token — رمز قصير العمر للوصول إلى API، يُرسل مع كل طلب. refresh_token — رمز طويل العمر للحصول على access_token جديد، يُرسل فقط إلى نقطة endpoint الخاصة بالرمز. لا ينبغي أن يكون refresh_token متاحاً لنقاط endpoint API العادية للتطبيق.

كم مرة يجب تحديث access token؟

عند كل انتهاء صلاحية — عادة كل 15–60 دقيقة. يجب على العميل تتبع وقت انتهاء الصلاحية (التحقق من exp في JWT أو استخدام مؤقت) وبدء طلب التحديث مسبقاً، قبل تلقي 401 فعلياً. هذا يمنع فقدان البيانات في الطلبات المرسلة في لحظة انتهاء صلاحية الرمز.

هل يمكن إبطال refresh token على الخادم؟

نعم، يمكن ويجب إبطال refresh_token. يحتفظ الخادم بقائمة refresh_token النشطة (أو تجزئاتها) في قاعدة البيانات. عند تسجيل الخروج، تغيير كلمة المرور أو نشاط مشبوه، يقوم الخادم بإزالة الإدخال من قاعدة البيانات وسيعيد طلب التحديث التالي بهذا الرمز خطأ invalid_grant.

ماذا يحدث عند استخدام عميلين لل refresh token القديم في وقت واحد؟

مع تفعيل rotation وكشف إعادة الاستخدام: الطلب الأول يحدث الرموز بنجاح، الطلب الثاني يتلقى خطأ invalid_grant. يقوم الخادم أيضاً بتسجيل إعادة الاستخدام — يتم حظر الجلسة، ويفقد كلا العميلين الوصول. يجب على المستخدم تسجيل الدخول مرة أخرى. هذا يضحي بالراحة من أجل الأمان.

أين يتم تخزين refresh token بأمان في iOS؟

يجب تخزين refresh_token في Keychain مع السمة kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly. هذا يضمن تشفير الرمز، عدم إمكانية الوصول عند إزالة رمز المرور، ويمنع المزامنة عبر iCloud. استخدام UserDefaults أو CoreData لتخزين الرمز ممنوع منعاً باتاً.

الملخص

  • Refresh Token — رمز طويل العمر لتحديث access_token دون إعادة تسجيل الدخول
  • TTL قصير لـ access_token (15–60 د) يقلل الضرر من التسريب
  • Token rotation — كل تحديث يعيد refresh_token جديد، القديم يُبطل
  • كشف إعادة الاستخدام — يكتشف سرقة الرمز ويحظر الجلسة
  • التخزين — iOS Keychain، Android EncryptedSharedPreferences (AES256-GCM)
  • الإبطال من الخادم — حذف refresh_token من قاعدة البيانات عند تسجيل الخروج أو تغيير كلمة المرور
  • Refresh token لا يُنقل أبداً مع طلبات API العادية

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا