Реалізація сессійного управління в мобільному приложенні
Сесія в мобільному приложенні — поняття ширше, ніж токен авторизації. Це комплекс станів: актуальність облікових даних, активність користувача, поведінка при зміні пристрою, реакція на eventos безпеки (зміна пароля на сервері, відозив сесії адміністратором).
Що включає повноцінне session management
Більшість проектів зупиняються на "є токен — користувач авторизований". Насправді потрібно враховувати:
- Timeout за неактивністю. Приложення блокується після N хвилин без взаємодії. Для фінтех — 3–5 хвилин, для корпоративних — 15–30 хвилин, для потребительських — зазвичай не потрібно.
- Примусовий logout при зміні пароля. Бекенд відозиває всі активні refresh tokens при зміні пароля. Приложення повинно коректно обробити 401 на refresh як SessionExpired, а не як мережевої помилки.
- Паралельні сесії. Скільки пристроїв може бути залогінено одночасно? Якщо один — сервер відозиває попередню сесію при новому логіні, приложення отримує 401 та повинно пояснити користувачу, що сталося.
- Восстановлення сесії після перезапуску приложення. Cold start — перевіряємо валідність токенів в Keychain/Keystore до показу будь-якого контента.
Timeout за неактивністю: реалізація
iOS: UIApplication.shared.sendAction не підходить для відстеження взаємодій на рівні всього приложення. Правильний спосіб — subclass UIWindow та переопреділити sendEvent(_:):
class ActivityTrackingWindow: UIWindow {
override func sendEvent(_ event: UIEvent) {
super.sendEvent(event)
if event.type == .touches {
SessionManager.shared.resetInactivityTimer()
}
}
}
SessionManager тримає Timer, який по істеченні N хвилин публікує событие sessionInactivityTimeout. На це событие реагують координатори навігації — показують екран блокування (біометрія або PIN).
Android: Handler + Runnable з postDelayed. Скидаємо при кожному MotionEvent у базовому Activity:
abstract class BaseActivity : AppCompatActivity() {
private val inactivityHandler = Handler(Looper.getMainLooper())
private val lockRunnable = Runnable { SessionManager.onInactivity() }
override fun onUserInteraction() {
super.onUserInteraction()
inactivityHandler.removeCallbacks(lockRunnable)
inactivityHandler.postDelayed(lockRunnable, INACTIVITY_TIMEOUT_MS)
}
}
Важливо: таймер паузуємо при уході в background (onPause) та возобновляємо при поверненні (onResume). Коли приложення в background — інший механізм (абсолютне час background start).
Background timeout
Окремо відстежуємо час у background. При onPause/sceneDidEnterBackground зберігаємо Date.now(). При onResume/sceneWillEnterForeground вичисляємо дельту. Якщо більше порога — показуємо екран блокування без анімації (одразу, до того як користувач побачить контент).
// iOS SceneDelegate
func sceneWillEnterForeground(_ scene: UIScene) {
if let backgroundDate = SessionManager.shared.backgroundDate,
Date().timeIntervalSince(backgroundDate) > SessionConfig.backgroundTimeout {
SessionManager.shared.lockSession()
}
}
Мультиустройственность та revocation
Сервер повинен мати endpoint для отримання списку активних сесій та їх відозиву. Мобільне приложення — UI для цього списку: "Sessions" екран з пристроями, датою останньої активності, кнопкою "Завершити цю сесію".
При відозиві сесії з іншого пристрою: наступний API-запит повертає 401. Якщо це 401 на спробі refresh — SessionExpired. Важливо розрізняти "401 тому що access token істік" (робимо refresh) та "401 тому що refresh token відозван" (SessionExpired). Різниця: при істеченні access token refresh повертає 200, при відозванному refresh token — 400/401 з кодом помилки invalid_grant.
Стану сесії
Зручно моделювати як sealed class/enum:
sealed class SessionState {
object Active : SessionState()
object Locked : SessionState() // потрібна біометрія/PIN
object Expired : SessionState() // потрібен повторний логін
object Loading : SessionState() // перевіряємо токени при запуску
}
Глобальний StateFlow<SessionState> у SessionManager — всі частини приложення реагують на зміну стану. Navigation coordinator/AppCoordinator підписується та переключає root view controller/NavHost залежно від стану.
Терміни
Повне session management (timeout за неактивністю, background timeout, екран блокування, мультиустройственність, SessionExpired flow) — 8–12 робочих днів. Якщо потрібна тільки базова частина (timeout + блокування) — 4–6 днів. Серверна частина (зберігання сесій, revocation API) — окрема оцінка з backend-командою.







