Реализация управления активными сессиями на разных устройствах
Экран управления сессиями — стандарт безопасности для мобильных приложений с чувствительными данными. Пользователь видит, с каких устройств выполнен вход, и может принудительно завершить любую сессию. Telegram, Google, банковские приложения — все реализуют эту функцию.
Что показывать пользователю
Для каждой активной сессии нужен минимальный контекст для идентификации устройства:
- Название устройства и платформа ("iPhone 15 Pro, iOS 17.4")
- Примерное местоположение последнего входа (по IP через GeoIP2 / ipapi)
- Дата и время последней активности
- Текущее устройство помечается отдельно
data class DeviceSession(
val sessionId: String,
val deviceName: String,
val deviceType: DeviceType, // IOS, ANDROID, WEB
val location: String?, // "Москва, Россия"
val lastActiveAt: Instant,
val createdAt: Instant,
val isCurrentDevice: Boolean
)
Определить текущее устройство: при запросе списка сессий backend сравнивает device_id из JWT с device_id каждой сессии. Текущее устройство нельзя завершить с этого же экрана — только через явный logout.
UI экрана сессий
@Composable
fun ActiveSessionsScreen(
sessions: List<DeviceSession>,
onRevokeSession: (String) -> Unit,
onRevokeAll: () -> Unit
) {
LazyColumn {
item {
Text(
"Активные сессии",
style = MaterialTheme.typography.headlineSmall
)
}
items(sessions, key = { it.sessionId }) { session ->
SessionCard(
session = session,
onRevoke = if (session.isCurrentDevice) null
else {{ onRevokeSession(session.sessionId) }}
)
}
if (sessions.count { !it.isCurrentDevice } > 1) {
item {
TextButton(onClick = onRevokeAll) {
Text("Завершить все остальные сессии")
}
}
}
}
}
Отзыв сессии
Завершение сессии: backend инвалидирует refresh_token (удаляет из таблицы или помечает как revoked). Устройство с отозванной сессией при следующем запросе API или рефреше токена получает 401. Мобильный обрабатывает 401 — перенаправляет на экран логина и очищает локальные данные.
Для немедленного эффекта (пользователь опасается компрометации) нужен более агрессивный механизм: при отзыве сессии backend отправляет silent push на это устройство с командой немедленного logout. На Android — FCM data message с action: "force_logout". На iOS — APNs background notification с content-available: 1.
// Firebase Function: при отзыве сессии
async function revokeDeviceSession(sessionId: string, targetDeviceToken: string) {
await db.collection('sessions').doc(sessionId).update({ revoked: true });
await admin.messaging().send({
token: targetDeviceToken,
data: {
action: 'force_logout',
reason: 'session_revoked'
},
android: { priority: 'high' }
});
}
Уведомления о новых входах
Безопасная практика: при логине с нового устройства отправлять push-уведомление на другие активные устройства пользователя: "Выполнен вход на устройстве Samsung Galaxy S24 из Москвы. Это были вы?" С кнопкой "Нет, завершить эту сессию".
Это стандарт безопасности у банков и финтех-приложений. Реализация: после создания новой сессии backend итерируется по остальным fcm_token пользователя и отправляет уведомление.
Экран управления сессиями + уведомления о новых входах + принудительный logout с push — 2-3 недели. Стоимость рассчитывается индивидуально.







