Аутентифікація в мобільних додатках
Додаток банку: користувач вводить PIN, сервер відповідає JWT, токен зберігається в SharedPreferences як plain text. Це не гіпотетичний приклад — це реальна історія кількох fintech-стартапів. SharedPreferences читається будь-яким додатком з root-доступом на Android без додаткових дозволів. На iOS аналог — збереження токена в UserDefaults замість Keychain.
Аутентифікація на мобільних складніша, ніж у вебі: немає HttpOnly cookie, немає сесійного механізму браузера, є специфіка платформенного сховища та біометрії.
Збереження токенів: Keychain та Android Keystore
iOS Keychain — зашифроване сховище на рівні ОС. Дані захищені Secure Enclave на пристроях з Face ID/Touch ID. Правильний сценарій: JWT refresh token зберігається в Keychain з атрибутом kSecAttrAccessibleWhenUnlockedThisDeviceOnly — токен доступний тільки коли пристрій розблокований і не переноситься при відновленні з iCloud-бекапу.
// Збереження в Keychain через Security framework
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: "com.yourapp.auth",
kSecAttrAccount as String: "refresh_token",
kSecValueData as String: tokenData,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemAdd(query as CFDictionary, nil)
Android Keystore System — апаратний (або програмний на старих пристроях) модуль зберігання криптографічних ключів. Ключі не можуть бути експортовані — операції шифрування/розшифрування відбуваються всередині Keystore. Паттерн: генеруємо ключ в Keystore, шифруємо ним refresh token, зберігаємо зашифрований blob в EncryptedSharedPreferences (Jetpack Security).
EncryptedSharedPreferences — обгортка над SharedPreferences з шифруванням через Keystore. Додається за 5 хвилин, усуває клас уразливостей, який зустрічається в половині Android-додатків.
Біометрична аутентифікація
iOS LocalAuthentication. LAContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics) — стандартний виклик для Face ID / Touch ID. Інтегрується з Keychain через kSecAccessControl з флагом .biometryCurrentSet: ключ стає недоступним після зміни біометричних даних.
Типовий сценарій: при першому вході — логін по email/паролю, refresh token → Keychain з biometric protection. При наступних запусках — біометрія розблокує доступ до токена, токен обмінюється на новий access token.
Android BiometricPrompt. Єдиний API для відбитка пальця, обличчя та райдужки. BiometricManager.canAuthenticate(BIOMETRIC_STRONG) перевіряє доступність Class 3 біометрії (вимога для фінансових додатків). BIOMETRIC_STRONG + Keystore-ключ з setUserAuthenticationRequired(true) — ключ використовується тільки після успішної біометрії в поточній сесії.
OAuth 2.0 та PKCE
OAuth 2.0 Authorization Code Flow з PKCE (Proof Key for Code Exchange) — стандарт для мобільних додатків. Implicit Flow офіційно застарілий в RFC 8252.
PKCE додає code_verifier (випадкова строка) та code_challenge (SHA-256 від verifier). Сервер авторизації перевіряє відповідність при обміні code на token. Це захищає від перехоплення authorization code через кастомну URL-схему.
iOS: ASWebAuthenticationSession — системний браузер для OAuth. Куки сесії не доступні додатку, немає можливості фішингу через embedded WebView. Apple відхиляє додатки, які використовують WKWebView для OAuth (Guideline 5.1.1).
Android: AppAuth-Android — стандартна бібліотека для OAuth/OIDC з PKCE-підтримкою. Custom Tabs (Chrome) замість WebView — той же принцип безпеки.
Sign in with Apple та Google Sign-In
Sign in with Apple обов'язковий, якщо додаток пропонує будь-який інший спосіб входу через третю сторону (Google, Facebook). Apple вимагає його з 2019 року, порушення — відхилення по Guideline 4.8.
Особливість: Apple може приховати реальний email користувача, надавши relay-адресу ([email protected]). Backend повинен правильно це обробляти — не використовувати email як первинний ідентифікатор.
ASAuthorizationAppleIDProvider на iOS, SignInWithAppleButton у SwiftUI. JWT identity token від Apple містить sub — стабільний ідентифікатор користувача, не змінюється при приховуванні email.
Google Sign-In. На Android — через Credential Manager API (замінив старий GoogleSignIn API з Android 14). На iOS — GoogleSignIn SDK, який відкриває Safari або Google App для авторизації.
2FA та одноразові паролі
TOTP (Time-based One-Time Password, RFC 6238) — стандарт для 2FA. base32-кодований секрет генерується на сервері, користувач сканує QR в Google Authenticator або Authy.
На мобільних вбудований Authenticator через Password AutoFill (iOS 15+) працює з Keychain: одноразовий код заповнюється автоматично без окремого додатку. Для цього поле OTP повинне мати textContentType = .oneTimeCode.
SMS OTP — найменш безпечний варіант (SIM-swapping), але найбільш конверсійний для користувачів. Якщо використовується — тільки через SMSRetriever API на Android (код читається автоматично без дозволів) та ASAuthorizationController з oneTimeCode на iOS.
JWT: access та refresh токени
Паттерн: короткоживущий access token (15 хвилин — 1 година) + довгоживущий refresh token (30–90 днів). Access token в пам'яті (in-memory — не в Keychain), refresh token в Keychain/EncryptedSharedPreferences.
Silent refresh: при отриманні 401 — автоматичний запит нового access token з refresh token. Якщо refresh token закінчився — примусовий логін.
Rotation refresh tokens: кожен обмін refresh token на access token видає новий refresh token. Старий інвалідується. Якщо старий refresh token спробував використатися — компрометація, усі токени користувача відзиваються.
Типові помилки
- Збереження токенів в
UserDefaults/SharedPreferences— читаються без root на рутованих пристроях - Відсутність certificate pinning у high-security додатках — MITM через корпоративний proxy
- Збереження секретів в
Info.plistабоBuildConfig— декомпілюються тривіально - OAuth через
WKWebView/WebViewзамість системного браузера — відхилення App Store + security risk - Неправильний
kSecAttrAccessible— токен в Keychain зkSecAttrAccessibleAlwaysне потребує розблокування пристрою
Терміни
Реалізація аутентифікації займає від 1 до 4 тижнів залежно від набору методів: логін по email/паролю, OAuth (Google, Apple), біометрія, 2FA. Backend для auth часто окремий мікросервіс або Auth0 / Firebase Auth як BaaS. Вартість розраховується після уточнення вимог до безпеки та набору методів входу.







