Вы когда-нибудь открывали приложение и видели кнопку «Войти через Google» или «Войти через Apple»? Это не просто маркетинговый ход. За этим стоит сложная механика OAuth is стандарт открытого доступа, который позволяет приложениям получать ограниченный доступ к данным пользователя без передачи пароля. В мобильной среде это критически важно, потому что пользовательские устройства менее контролируемы, чем серверные. Ошибки здесь ведут к утечкам токенов и краже сессий.
Почему мобильная авторизация отличается от веб-версии
На сайте браузер хранит куки, а сервер доверяет их проверке. На телефоне ситуация другая. Приложение может быть взломано, устройство украдено, а сеть ненадежной. Поэтому мобильная разработка требует особого подхода к безопасности. Главный враг здесь - публичный клиент (public client), который не имеет секрета на стороне сервера для проверки подлинности.
Раньше разработчики пытались использовать стандартный OAuth flow, передавая секрет приложения прямо в коде APK или IPA. Но любой хакер мог вытащить его за пять минут. Сейчас индустрия перешла на более безопасные методы. Ключевым стандартом стала спецификация RFC 8252, которая описывает, как правильно делать OAuth на мобильных устройствах.
Ключевые игроки в экосистеме
Чтобы понять схемы, нужно знать, кто участвует в процессе обмена данными:
- Resource Owner (владелец ресурса) - сам пользователь.
- Client (клиент) - ваше мобильное приложение.
- Authorization Server (сервер авторизации) - сервис, который выдает токены (например, Auth0, Firebase Auth, Keycloak).
- Resource Server (ресурсный сервер) - ваш бэкенд, который хранит данные и проверяет токены.
Важно понимать разницу между Authorization Server и Resource Server. Часто они объединяются в один микросервис, но логически их функции разные. Первый отвечает за выдачу ключей доступа, второй - за проверку этих ключей при каждом запросе API.
Схема 1: Implicit Flow (Имплицитный поток)
Долгое время это был стандарт де-факто для SPA и мобильных приложений. Суть проста: пользователь переходит на страницу логина, подтверждает доступ, и сервер сразу возвращает Access Token в URL ответа.
Implicit Flow передает токен напрямую в клиентское приложение через фрагмент URL, минуя этап обмена кода на сервере.Звучит удобно? Да, но есть ловушка. Токен виден в истории браузера (если переход идет через WebView) и может попасть в логи. Более того, в этой схеме нет Refresh Token. Когда Access Token истекает (обычно через 15-60 минут), пользователю приходится заново логиниться. Для мобильного приложения это плохой опыт.
Сегодня Implicit Flow считается устаревшим для нативных приложений. Его используют разве что в простых WebView, где нет возможности перехватить редиректы. Если вы пишете новое приложение с нуля, лучше пропустить этот вариант.
Схема 2: Authorization Code Flow with PKCE
Это золотой стандарт для мобильных приложений сегодня. PKCE (Proof Key for Code Exchange) решает главную проблему публичных клиентов: как доказать серверу, что именно наше приложение запрашивает токен, если у нас нет секрета?
Механика выглядит так:
- Приложение генерирует случайную строку (code verifier).
- Из нее вычисляется хеш (code challenge).
- Приложение отправляет challenge на сервер авторизации вместе с запросом на код.
- Пользователь логинится, получает authorization code.
- Приложение отправляет код и исходный verifier обратно на сервер.
- Сервер сверяет хеш верифайера с сохраненным челленджем. Если совпало - выдает Access Token и Refresh Token.
Благодаря этому даже если код перехватят по дороге, без оригинального верифайера он бесполезен. Этот метод поддерживается всеми современными IdP (Identity Providers): Okta, Auth0, Microsoft Azure AD, Firebase.
Как хранить токены на устройстве
Получили токены. Куда их деть? Самый частый вопрос среди джуниоров. Ответ зависит от платформы.
| Платформа | Рекомендуемое хранилище | Небезопасное хранилище | Особенность |
|---|---|---|---|
| iOS | Keychain Services | UserDefaults | Keychain шифрует данные аппаратно |
| Android | Keystore / EncryptedSharedPreferences | Standard SharedPreferences | Требует настройки библиотек (Jetpack Security) |
Никогда не храните Access Token в обычном файле или в памяти глобального объекта, если только не используете короткие TTL (time-to-live). Лучше всего работать с короткими Access Tokens (5-15 минут) и длинными Refresh Tokens (30-90 дней).
JWT и проверка на бэкенде
Когда мобильное приложение делает запрос к вашему API, оно кладет Access Token в заголовок Authorization: Bearer <token>. Чаще всего это JWT (JSON Web Token) is стандартизированный способ представления утверждений, которые должны безопасно передаваться между сторонами.
JWT состоит из трех частей: Header, Payload и Signature. Ваш бэкенд должен проверить подпись, чтобы убедиться, что токен не подделан. Для этого используется публичный ключ сервера авторизации. Не нужно каждый раз ходить на сервер авторизации для проверки каждого запроса - это убьет производительность. Достаточно локальной валидации подписи.
Типичные ошибки и как их избежать
Даже опытные команды спотыкаются на мелочах. Вот список частых проблем:
- Deep Links без защиты: При возврате из экрана логина система может открыть ваше приложение из другого процесса. Убедитесь, что Deep Link обрабатывается только вашим приложением.
- Утечка через логирование: Разработчики часто пишут токен в консоль для отладки. На проде это прямая дыра. Используйте библиотеки логирования, которые маскируют чувствительные поля.
- Отсутствие обработки ошибок сети: Если интернет пропадет во время обновления токена, приложение должно корректно обработать офлайн-режим, а не упасть с ошибкой 401.
Инструменты и библиотеки
Не пишите OAuth с нуля, если не обязаны. Существуют готовые SDK, которые берут на себя всю рутину: генерацию PKCE, работу с WebView, хранение в Keychain/Keystore.
- AppAuth-iOS и AppAuth-Android: официальные библиотеки от OpenID Foundation. Легкие, без лишних зависимостей.
- Firebase Authentication: если вы уже в экосистеме Google, это самый быстрый путь. Он скрывает детали OAuth за простыми API.
- Azure Identity: корпоративный стандарт для интеграции с Microsoft Graph.
Выбор инструмента зависит от того, где живут ваши пользователи. Если аудитория международная, берите нейтральные решения вроде AppAuth + свой IdP или Auth0.
Практический пример реализации
Представьте, что вы делаете фитнес-трекер. Пользователь хочет синхронизировать данные с Apple HealthKit. Вам нужен доступ к шагам, но не к фотографиям. Вы используете OAuth Scope health.read.
1. Пользователь жмет «Синхронизировать».
2. Приложение запускает WebView с экраном разрешения Apple.
3. Пользователь разрешает доступ.
4. Приложение получает токен.
5. Бэкенд сохраняет связь между ID пользователя в вашем приложении и ID в Apple.
Если завтра пользователь решит отменить доступ, Apple пришлет уведомление, или же ваш бэкенд получит ошибку 401 при следующем запросе данных. Ваша задача - аккуратно очистить локальную привязку и предложить пользователю повторить вход.
Чек-лист перед релизом
- [ ] Используется ли PKCE вместо Implicit Flow?
- [ ] Хранятся ли токены в защищенном хранилище (Keychain/Keystore)?
- [ ] Обработаны ли все случаи, когда WebView закрыт пользователем силой?
- [ ] Есть ли механизм автоматического обновления токена (silent refresh)?
- [ ] Проверена ли защита Deep Links от подмены?
FAQ
Какая разница между OAuth и OpenID Connect?
OAuth решает задачу авторизации (доступ к ресурсам), а OpenID Connect (OIDC) добавляет сверху идентификацию (кто этот пользователь). OIDC использует тот же транспорт OAuth, но возвращает ID Token, который содержит профиль пользователя. В мобильных приложениях чаще всего используют именно OIDC, чтобы знать, кто вошел.
Нужен ли Refresh Token в мобильном приложении?
Да, почти всегда. Без него пользователь будет логиниться каждые 15 минут. Refresh Token позволяет бесшумно обновлять сессию. Главное - хранить его безопасно и иметь возможность отозвать его с сервера при потере устройства.
Что делать, если WebView недоступна (например, в фоне)?
Если приложение работает в фоне и нужно обновить токен, используйте механизм Silent Refresh. Многие IdP позволяют получить новый токен без участия пользователя, если он недавно логинился. Если это невозможно, отложите обновление до момента, когда пользователь снова откроет приложение.
Безопасно ли передавать токен через Deep Link?
Средне. Deep Link может быть перехвачен другим приложением. Чтобы снизить риски, используйте Universal Links (iOS) или App Links (Android), которые гарантируют, что ссылка откроется только в вашем приложении. Также добавляйте параметр state для проверки целостности редиректа.
Какой срок жизни Access Token оптимален для мобилки?
Оптимально - от 5 до 15 минут. Чем короче, тем меньше окно для взлома. Но слишком короткий срок увеличивает нагрузку на сервер из-за частых обновлений. 10 минут - хороший баланс между безопасностью и производительностью.