Реализация мульти-девайсной авторизации (Multi-Device Login) в мобильном приложении
Multi-device login — возможность пользователя быть залогиненным на нескольких устройствах одновременно. iPhone, iPad, рабочий Android — все показывают актуальные данные. Технически это история про правильное управление токенами и синхронизацию состояния между устройствами.
Архитектура токенов
Каждое устройство получает собственную пару access_token + refresh_token. Backend хранит таблицу сессий:
CREATE TABLE user_sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id),
device_id VARCHAR(255) NOT NULL, -- уникальный идентификатор устройства
device_name VARCHAR(255), -- "iPhone 15 Pro", "Samsung Galaxy S24"
device_type VARCHAR(50), -- ios, android, web
refresh_token_hash VARCHAR(255) NOT NULL,
created_at TIMESTAMPTZ DEFAULT now(),
last_active_at TIMESTAMPTZ DEFAULT now(),
expires_at TIMESTAMPTZ NOT NULL,
UNIQUE(user_id, device_id)
);
device_id на Android — Settings.Secure.ANDROID_ID (не меняется при переустановке, сбрасывается при factory reset). На iOS — identifierForVendor (сбрасывается при удалении всех приложений разработчика). Для более стабильного ID на iOS можно генерировать UUID при первом запуске и хранить в Keychain.
device_name определяется из: Android — Build.MANUFACTURER + " " + Build.MODEL, iOS — UIDevice.current.name.
Ограничение количества устройств
Некоторые приложения ограничивают количество активных сессий (стриминговые сервисы, финансовые приложения). При превышении лимита backend возвращает ошибку MAX_DEVICES_REACHED с списком активных сессий — мобильный предлагает пользователю выбрать, какую сессию закрыть.
sealed class LoginResult {
data class Success(val tokens: AuthTokens) : LoginResult()
data class MaxDevicesReached(val activeSessions: List<DeviceSession>) : LoginResult()
data class Error(val message: String) : LoginResult()
}
@Composable
fun MaxDevicesScreen(sessions: List<DeviceSession>, onRevoke: (String) -> Unit) {
Text("Достигнут лимит устройств. Выйдите с одного из них:")
sessions.forEach { session ->
DeviceSessionCard(
deviceName = session.deviceName,
lastActive = session.lastActiveAt,
onRevoke = { onRevoke(session.id) }
)
}
}
Синхронизация данных между устройствами
Когда пользователь меняет данные на одном устройстве — другие должны узнать об этом. Механизмы:
Push-уведомления с data payload — при изменении профиля на устройстве A, backend отправляет silent push на все другие устройства пользователя. Устройство B просыпается, выполняет синхронизацию в фоне.
WebSocket — если приложение использует реалтайм, события синхронизации идут по тому же каналу.
Pull при foreground — при выходе из фона (onResume / applicationWillEnterForeground) запрашиваем свежие данные. Дёшево реализовать, подходит для нечастых изменений.
Для финансовых приложений — синхронизация баланса и истории транзакций при каждом выходе из фона обязательна. Кэшированный баланс на другом устройстве должен помечаться как stale после определённого времени.
Реализация multi-device login (multiple токены, таблица сессий, список устройств) — 2-3 недели. С синхронизацией данных между устройствами — 3-5 недель. Стоимость рассчитывается индивидуально.







