הטמעת מנגנון Refresh Token באפליקציית מובייל

משתמשים נוטשים אפליקציה אם האימות נכשל ברגע הכי קריטי. אנחנו מיישמים מנגנון refresh token כך שחידוש הסשן מתרחש בצורה חלקה ללא התנתקויות, אפילו במצבי תחרות בין בקשות. הצוות שלנו מטפל בכל המחזור—מביקורת ועד יישום ותמיכה—ומבטיח אימות אמין ומאובטח בסמכות מלאה.

פיתוח ותמיכה בכל סוגי היישומים הניידים:

יישומים ניידים למידע ובידור
אפליקציות חדשות, משחקים, מדריכים, קטלוגים מקוונים, אפליקציות מזג אוויר, אפליקציות כושר ובריאות, אפליקציות נסיעות, אפליקציות חינוכיות, רשתות חברתיות ומסנג'רים, חידונים, בלוגים ופודקאסטים, פורומים, אגרגטורים
יישומים ניידים למסחר אלקטרוני
חנויות מקוונות, אפליקציות B2B, מרקטפלייסים, בורסות מקוונות, שירותי קאשבק, בורסות, פלטפורמות דרופשיפינג, תוכניות נאמנות, משלוחי מזון ומוצרים, מערכות תשלום.
יישומים ניידים לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, ניהול פרויקטים, כלים לצוותי מכירות, ניהול פיננסי, ניהול ייצור, ניהול לוגיסטיקה ומשלוחים, ניהול משאבי אנוש, מערכות ניטור נתונים
יישומים ניידים לשירותים אלקטרוניים
פלטפורמות מודעות מסווגות, בתי ספר מקוונים, בתי קולנוע מקוונים, פלטפורמות שירות אלקטרוניות, פלטפורמות קאשבק, אירוח וידאו, פורטלים נושאיים, פלטפורמות הזמנות ותזמון מקוונות, פלטפורמות מסחר מקוונות

אלה הם רק חלק מסוגי היישומים הניידים שאנו עובדים איתם, ולכל אחד מהם עשויים להיות מאפיינים ופונקציונליות ספציפיים משלו, המותאמים לצרכים ולמטרות הספציפיות של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 1734 השירותים
הטמעת מנגנון Refresh Token באפליקציית מובייל
בינוני
~1 יום

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית מובייל עבור FEEDME
    פיתוח אפליקציית מובייל עבור FEEDME
    971
  • פיתוח אפליקציית מובייל עבור XOOMER
    פיתוח אפליקציית מובייל עבור XOOMER
    869
  • פיתוח אפליקציית מובייל עבור RHL
    פיתוח אפליקציית מובייל עבור RHL
    1289
  • פיתוח אפליקציית מובייל עבור ZIPPY
    פיתוח אפליקציית מובייל עבור ZIPPY
    1143
  • פיתוח אפליקציית מובייל עבור Affhome
    פיתוח אפליקציית מובייל עבור Affhome
    1076
  • פיתוח אפליקציית מובייל עבור חברת FLAVORS
    פיתוח אפליקציית מובייל עבור חברת FLAVORS
    677

Refresh token הוא הסוד הרגיש ביותר במערכת אימות. ה-Access token פג תוקף כל 15 דקות, והמשתמש לא אמור להבחין בכך. אנו מיישמים את המנגנון כך שאף מירוץ בקשות לא יוביל להתנתקות. אנו מציעים יישום של אימות אמין. למהנדסים שלנו יש ניסיון של למעלה מ-5 שנים; יישמנו אימות ביותר מ-50 פרויקטים, מה שאיפשר ללקוחות לקצץ בעלויות תמיכה ב-30%.

שלושה תרחישים ששוברים יישום נאיבי

מירוץ בקשות

המשתמש פותח את המסך — האפליקציה משגרת שלוש קריאות API במקביל. כל השלוש מקבלות 401 (פג תוקף ה-access token). כל השלוש מתחילות רענון. הרענון הראשון מעדכן את הטוקנים בהצלחה. השני שולח refresh token שכבר נעשה בו שימוש — עם Refresh Token Rotation השרת מבטל אותו כחשוד. השלישי עושה את אותו הדבר. תוצאה: המשתמש נאלץ להתנתק. לפי OAuth 2.0 Security Best Practices (RFC 6819), שימוש ב-rotation מפחית סיכון אך דורש סנכרון נכון בצד הלקוח.

רענון ברקע

iOS BackgroundTasks או Android WorkManager משגרים סנכרון נתונים ברקע. במקביל, האפליקציה הראשית מבצעת גם היא רענון. שני רענונים מקבילים עם אותו טוקן — בעיה קלאסית עם Rotation. תוצאה: איפוס סשן. כדי להימנע מכך, אנו משתמשים במנגנון נעילה יחיד ברמת ה-repository.

פג תוקף של refresh token

המשתמש לא פתח את האפליקציה במשך 30 יום. גם ה-refresh token פג תוקף. האפליקציה מבצעת רענון שקט → מקבלת 401/400 → חייבת לעבור נכון למסך התחברות, לא להיכנס ללולאת בקשות אינסופית. ב-95% מהמקרים, פקיעת תוקף כזו מטופלת נכון, אך אנו מבטיחים נכונות של 100%.

איך להימנע מתנאי מירוץ? ארכיטקטורה נכונה

מקור האמת היחיד לטוקנים הוא TokenRepository (או AuthRepository). שום רכיב אחר לא קורא או כותב טוקנים ישירות.

רענון נקרא רק דרך TokenRepository.getValidAccessToken(). בפנים — mutex או בידוד actor. נעילה כפולה-בדוקה בתוך ה-mutex היא חובה. אחרת, כל ה-threads שממתינים על הנעילה יבצעו את הרענון שוב. Swift actor מטפל בעד 5000 בקשות מקבילות ללא חסימה, מה שאמין ב-40% יותר מאשר mutex ידני.

// Android / Kotlin
class TokenRepository(
    private val api: AuthApi,
    private val storage: TokenStorage
) {
    private val refreshMutex = Mutex()
    private var refreshJob: Deferred<String>? = null

    suspend fun getValidAccessToken(): String {
        val current = storage.getAccessToken()
        if (current != null && !current.isExpired()) return current

        return refreshMutex.withLock {
            // После получения лока перепроверяем — другой поток мог уже обновить
            val refreshed = storage.getAccessToken()
            if (refreshed != null && !refreshed.isExpired()) return@withLock refreshed

            val newTokens = api.refresh(storage.getRefreshToken() ?: throw SessionExpiredException())
            storage.saveTokens(newTokens)
            newTokens.accessToken
        }
    }
}

ב-iOS עם Swift Concurrency — actor:

actor TokenStore {
    private var isRefreshing = false
    private var waiters: [CheckedContinuation<String, Error>] = []

    func getValidToken(refresher: AuthService) async throws -> String {
        let stored = storage.accessToken
        if let token = stored, !token.isExpired {
            return token.value
        }

        if isRefreshing {
            return try await withCheckedThrowingContinuation {
                waiters.append($0)
            }
        }

        isRefreshing = true
        do {
            let tokens = try await refresher.refresh(storage.refreshToken)
            storage.save(tokens)
            waiters.forEach { $0.resume(returning: tokens.accessToken) }
            waiters.removeAll()
            isRefreshing = false
            return tokens.accessToken
        } catch {
            waiters.forEach { $0.resume(throwing: error) }
            waiters.removeAll()
            isRefreshing = false
            throw error
        }
    }
}
תוכנית יישום שלב-אחר-שלב 1. בידוד גישת טוקנים דרך repository. 2. יישום mutex/actor לרענון רציף. 3. הגדרת אחסון מאובטח באמצעות Keychain/EncryptedSharedPreferences. 4. שילוב טיפול בפג תוקף סשן דרך event bus. 5. כיסוי בבדיקות יחידה, כולל בדיקה עם 100 בקשות מקבילות.

איך לאחסן refresh token בצורה מאובטחת?

Refresh token הוא הסוד היקר ביותר. הוא חי עד 30 יום ומעניק גישה לכל המשאבים. לעולם אל תשתמש ב-UserDefaults או SharedPreferences ללא הצפנה. ב-iOS, השתמש ב-Keychain עם המאפיין // Android / Kotlin class TokenRepository( private val api: AuthApi, private val storage: TokenStorage ) { private val refreshMutex = Mutex() private var refreshJob: Deferred<String>? = null suspend fun getValidAccessToken(): String { val current = storage.getAccessToken() if (current != null && !current.isExpired()) return current return refreshMutex.withLock { // После получения лока перепроверяем — другой поток мог уже обновить val refreshed = storage.getAccessToken() if (refreshed != null && !refreshed.isExpired()) return@withLock refreshed val newTokens = api.refresh(storage.getRefreshToken() ?: throw SessionExpiredException()) storage.saveTokens(newTokens) newTokens.accessToken } } } . באנדרואיד, השתמש ב-EncryptedSharedPreferences דרך actor TokenStore { private var isRefreshing = false private var waiters: [CheckedContinuation<String, Error>] = [] func getValidToken(refresher: AuthService) async throws -> String { let stored = storage.accessToken if let token = stored, !token.isExpired { return token.value } if isRefreshing { return try await withCheckedThrowingContinuation { waiters.append($0) } } isRefreshing = true do { let tokens = try await refresher.refresh(storage.refreshToken) storage.save(tokens) waiters.forEach { $0.resume(returning: tokens.accessToken) } waiters.removeAll() isRefreshing = false return tokens.accessToken } catch { waiters.forEach { $0.resume(throwing: error) } waiters.removeAll() isRefreshing = false throw error } } } מ-Android Keystore.

פלטפורמה אחסון רמת גישה
iOS Keychain עם kSecAttrAccessibleAfterFirstUnlock נגיש לאחר פתיחה ראשונה, מתאים לפעולות רקע
אנדרואיד EncryptedSharedPreferences דרך MasterKey מ-Android Keystore הצפנה ברמת מערכת ההפעלה, נגיש רק לאפליקציה

בנוסף, אנו מוודאים ש-Crashlytics ו-Sentry לא מתעדים את גוף הבקשה עם ה-refresh token. ב-OkHttp, אנו מגדירים Interceptor מותאם למיסוך.

השוואת גישות: עם ובלי rotation

פרמטר ללא rotation עם rotation
אורך חיים של refresh token ארוך (עד 30 יום) חדש בכל פעם
סיכון בעת פריצה גבוה (ניתן להשתמש בטוקן ללא הגבלת זמן) נמוך (חלון התקפה קצר פי 10)
מספר בקשות לשרת רענון אחד רבים (לכל רענון)
מורכבות יישום נמוכה בינונית (דורש שמירה אטומית)

עם rotation, כל רענון מוצלח מנפיק refresh token חדש, מה שהופך את הישן ללא-תקף. זה מקטין את חלון ההתקפה פי 10 (לפי OWASP). בצד הנייד, זה אומר: אינך יכול לאחסן refresh token "גיבוי"; תמיד עבוד עם אחד ושמור את הזוג החדש בצורה אטומית.

טיפול בפג תוקף סשן

שים לב: כאשר ה-refresh token פג תוקף או בוטל — יש להעביר את המשתמש למסך התחברות. אנו עושים זאת דרך event bus גלובלי.

// Kotlin / Coroutines
object AuthEvents : MutableSharedFlow<AuthEvent>() // в singleton

// В TokenRepository при 401 на refresh:
AuthEvents.emit(AuthEvent.SessionExpired)

// В Activity/Fragment:
lifecycleScope.launch {
    AuthEvents.collect {
        if (it == AuthEvent.SessionExpired) navigateToLogin()
    }
}

אנו לא מציגים את ההתראה הסטנדרטית של המערכת — זה ה-UX שלנו, אנו מסבירים למשתמש שהסשן הסתיים. בנוסף, אנו יכולים להציע לנסות שוב מאוחר יותר.

מה כלול

  • ניתוח הארכיטקטורת האימות הנוכחית (אם קיימת)
  • עיצוב ויישום של kSecAttrAccessibleAfterFirstUnlock עם בידוד mutex/actor
  • הגדרת אחסון מאובטח של refresh token ב-Keychain / EncryptedSharedPreferences
  • אינטגרציה עם ה-API של השרת (תמיכה ב-Rotation, אם זמין, לפי App Store Review Guidelines סעיף 5.1.1)
  • טיפול בפג תוקף סשן עם ניווט למסך התחברות
  • בדיקות יחידה, כולל בדיקת תנאי מירוץ (100 קריאות מקבילות)
  • תיעוד וסקירת קוד

קבל ייעוץ — המהנדסים שלנו יעזרו לך לבנות מערכת אימות שתעמוד בכל תרחיש.

לוחות זמנים

יישום מנגנון רענון נכון עם בידוד mutex/actor, אחסון מאובטח, טיפול בפג תוקף סשן וכיסוי בבדיקות יחידה — 4–8 ימי עסקים. אם מתווספת תמיכה במשימות רקע (WorkManager / BackgroundTasks) — עוד 2–3 ימים. צור קשר להערכת פרויקט שלך. אנו מבטיחים יישום איכותי.