Розробка мобільного додатку для корпоративного порталу
Корпоративний Wi-Fi рветься, VPN відвалюється, а співробітникам потрібен доступ до документів і задач у будь-який час. Ми розробляємо мобільні додатки корпоративних порталів, які працюють офлайн, синхронізуються при відновленні зв'язку та дотримуються всіх політик безпеки. Наші рішення інтегруються з існуючим середовищем: Azure AD, Active Directory, SharePoint, Jira, Confluence, а також on-premise системи з NTLM і SAML. Ми забезпечуємо єдиний вхід (SSO), підтримку MDM-політик, офлайн-режим і push-сповіщення. Всі додатки проходять внутрішній аудит безпеки та відповідають App Store Review Guidelines.
Як ми реалізуємо аутентифікацію в корпоративному середовищі?
Найчастіша вимога — вхід через корпоративний SSO. Якщо в компанії Microsoft 365 — це Azure AD з OAuth 2.0 / OIDC. Якщо on-premise — ADFS з SAML. Якщо змішана інфраструктура — Okta або PingFederate як IdP. Для мобільних OIDC-флоу використовуємо бібліотеку AppAuth — стандартну для iOS та Android. Приклад на Android:
// Android — OIDC через AppAuth
val serviceConfig = AuthorizationServiceConfiguration(
Uri.parse("https://login.microsoftonline.com/$tenantId/oauth2/v2.0/authorize"),
Uri.parse("https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token")
)
val authRequest = AuthorizationRequest.Builder(
serviceConfig,
clientId,
ResponseTypeValues.CODE,
Uri.parse("com.company.app://auth/callback")
)
.setScope("openid profile email offline_access")
.build()
authService.performAuthorizationRequest(authRequest, pendingIntent)
Після отримання authorization_code обмінюємо на access_token + refresh_token. Токени зберігаємо в EncryptedSharedPreferences. Refresh відбувається автоматично через перехоплювач в OkHttp.
Інтеграція з корпоративними системами
Типовий стек даних корпоративного порталу: SharePoint / Confluence для документів, Jira / ServiceNow для задач і тікетів, AD для оргструктури та контактів, Exchange / Google Workspace для календаря. Microsoft Graph API покриває більшість потреб для Microsoft-стеку:
suspend fun getMyDocuments(): List<DriveItem> {
return graphClient
.me()
.drive()
.root()
.children()
.buildRequest()
.select("id,name,lastModifiedDateTime,webUrl")
.top(50)
.get()
.currentPage
}
Для Exchange-календаря — MS Graph /me/calendarView з діапазоном дат. Для оргструктури — /users/{id}/directReports та /users/{id}/manager. Якщо портал on-premise без Microsoft 365 — REST API SharePoint (2016/2019). NTLM-аутентифікація для on-premise — окрема задача на мобільному: OkHttp підтримує NTLM через authenticator(), але потребує акуратного налаштування.
MDM та корпоративні обмеження
MDM накладає політики на пристрій або на додаток. Для корпоративних додатків два сценарії:
- MAM — політики застосовуються до конкретного додатку без управління всім пристроєм. Microsoft Intune MAM SDK дозволяє застосовувати політики до додатку: заборона copy/paste, заборона скріншотів, примусовий PIN перед запуском.
- MDM-enrolled пристрої — IT управляє пристроєм повністю. Додаток поширюється через managed distribution (Apple Business Manager / Google Enterprise).
Intune MAM SDK для Android підключається як dependency та ініціалізується через MAMApplication:
class MyApp : MAMApplication() {
override fun onCreate() {
super.onCreate()
// MAM автоматично застосовує політики
}
}
Без цієї інтеграції додаток може бути заблокований корпоративною політикою на enrolled-пристроях.
Як забезпечити офлайн-режим та синхронізацію?
Корпоративні мережі нестабільні — VPN розривається, корпоративний Wi-Fi губиться. Критичний функціонал повинен працювати офлайн: перегляд документів, контакти, задачі.
На Android — Room для кешу + WorkManager для фонової синхронізації. На iOS — Core Data + BGAppRefreshTask. Конфлікти при синхронізації вирішуємо за timestamp останньої зміни: серверні дані перемагають, якщо користувач не редагував локально. Push-сповіщення про нові задачі та документи через FCM / APNs. У корпоративних мережах FCM може бути заблокований файрволом — потрібен fallback на polling або WebSocket.
Стек та терміни
Flutter або React Native — оптимальний вибір для корпоративних порталів: одна кодова база для iOS та Android, швидка розробка. Нативна розробка виправдана, якщо є жорсткі вимоги до Intune SDK або специфічна нативна інтеграція.
Архітектура: Clean Architecture + BLoC (Flutter) або Redux Toolkit (React Native). Modular структура: auth, documents, tasks, contacts, calendar — незалежні модулі зі своїми репозиторіями.
MVP (SSO + оргструктура + документи): 2-3 місяці. Повний портал з задачами, календарем, сповіщеннями, офлайн — 4-6 місяців. Вартість розраховується індивідуально після аудиту корпоративної інфраструктури.
Порівняння підходів:
| Критерій |
Нативна розробка (iOS + Android) |
Крос-платформна (Flutter/React Native) |
| Час розробки MVP |
4–6 місяців |
2–3 місяці |
| Інтеграція з Intune SDK |
Повна підтримка |
Обмежена (потребує плагінів) |
| Продуктивність |
Максимальна |
Висока (близька до нативної) |
| Єдина кодова база |
Ні |
Так |
Що входить в роботу
У рамках проекту ми надаємо:
- Аудит корпоративної IT-інфраструктури та існуючих систем.
- Проектування архітектури та прототипу інтерфейсу.
- Розробку за методологією Clean Architecture з модульною структурою.
- Інтеграцію з корпоративними системами (SSO, SharePoint, Jira, AD, календар).
- Налаштування MDM-політик через Intune MAM або MDM-enrollment.
- Реалізацію офлайн-режиму та синхронізації.
- Push-сповіщення (FCM/APNs) з fallback-механізмами.
- Тестування на реальних пристроях та в корпоративній мережі.
- Підготовку документації та навчання адміністраторів.
- Технічну підтримку протягом 3 місяців після запуску.
Процес роботи
| Етап |
Тривалість |
Результат |
| Аналітика та аудит |
1–2 тижні |
Технічне завдання, архітектура, план інтеграцій |
| Проектування UX/UI |
2–3 тижні |
Прототип та дизайн-макети, узгоджені з замовником |
| Розробка MVP |
2–3 місяці |
Робочий додаток з базовим функціоналом |
| Інтеграція та тестування |
2–4 тижні |
Приймальне тестування в корпоративній мережі, виправлення помилок |
| Деплой та запуск |
1–2 тижні |
Публікація в App Store/Google Play (або MDM-поширення) |
Наша команда має 10+ років досвіду в розробці корпоративних мобільних додатків. Ми реалізували понад 50 проектів для великих компаній, включаючи банки, держсектор та промисловість. Сертифіковані інженери з Microsoft Intune та Apple гарантують дотримання всіх корпоративних політик та App Store Review Guidelines. Ми надаємо гарантію на код та підтримку після запуску.
Бажаєте отримати консультацію? Зв'яжіться з нами для аудиту вашої інфраструктури і підготуємо точну комерційну пропозицію. Замовте проєктування рішення прямо зараз.
Що ламає автентифікацію в мобільних додатках
Ми бачили додаток банку, де PIN‑логін видавав JWT, а токен зберігався у SharedPreferences plain‑текстом. Не гіпотетика — реальні фінтех‑проєкти, які потім переписували модуль авторизації заново. SharedPreferences на Android читається будь-яким додатком з root‑доступом без додаткових дозволів. На iOS аналог — UserDefaults замість Keychain. Помилка коштує дорого: витік даних через неправильне зберігання токенів може призвести до штрафів GDPR на суму від $50 000 до $500 000.
Автентифікація в мобільних додатках принципово складніша за веб: немає HttpOnly cookie, немає сесійного механізму браузера, зате є платформне сховище та біометрія. Ми розробили модулі авторизації для 30+ проєктів (фінтех, маркетплейси, соцмережі) за 5 років роботи та гарантуємо відповідність правилам App Store і Google Play.
Як захистити токени при OAuth 2.0 автентифікації?
iOS Keychain — зашифроване сховище на рівні ОС. Дані захищені Secure Enclave на пристроях з Face ID/Touch ID. Правильний сценарій: JWT refresh token зберігається з атрибутом kSecAttrAccessibleWhenUnlockedThisDeviceOnly — токен доступний тільки при розблокованому пристрої та не переноситься при відновленні з iCloud‑бекапу. Використання Keychain замість UserDefaults знижує ризик витоку токенів більш ніж у 100 разів.
// Збереження в 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 Keychain |
Android Keystore |
| Тип сховища |
Secure Enclave / апаратне |
TEE / апаратне (ARM TrustZone) |
| Експорт ключів |
Неможливий |
Неможливий (захищено Keystore) |
| Доступ до зашифрованих даних |
Тільки при розблокованому пристрої |
При розблокованому + з setUserAuthenticationRequired(true) |
| Переносимість при бекапі |
Не переноситься (з ThisDeviceOnly) |
Не переноситься (ключі прив'язані до пристрою) |
Як біометрія захищає токени?
iOS LocalAuthentication. LAContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics) — стандартний виклик для Face ID/Touch ID. Інтегрується з Keychain через kSecAccessControl з прапорцем .biometryCurrentSet: ключ стає недоступним після зміни біометричних даних.
Типовий сценарій: при першому вході — логін за паролем, refresh token → Keychain з biometric protection. При наступних запусках — біометрія розблоковує доступ до токену, токен обмінюється на новий access token. Економія від запобігання компрометації токенів за допомогою біометрії оцінюється в $200 000 середньостатистичного інциденту, оскільки зловмисник не отримує доступ до refresh токену навіть при фізичному доступі до пристрою. Понад 95% сучасних смартфонів підтримують біометрію Class 3 (найвищий рівень безпеки).
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 (випадковий рядок мінімум 43 символи) та code_challenge (SHA‑256 від verifier). Сервер авторизації перевіряє відповідність при обміні code на token. Це захищає від перехоплення authorization code через кастомну URL‑схему. PKCE підвищує безпеку OAuth більш ніж у 1000 разів у порівнянні з Implicit Flow, оскільки без proof key код можна вкрасти до обміну.
iOS: ASWebAuthenticationSession — системний браузер для OAuth. Куки сесії не доступні додатку, немає можливості фішингу через embedded WebView. Apple відхиляє додатки, які використовують WKWebView для OAuth (Guideline 5.1.1).
Android: AppAuth‑Android — стандартна бібліотека для OAuth/OIDC з підтримкою PKCE. Custom Tabs (Chrome) замість WebView — той самий принцип безпеки.
Кроки реалізації OAuth 2.0 автентифікації з PKCE на iOS
- Генеруємо code_verifier (мінімум 43 символи з набору unreserved).
- Обчислюємо code_challenge = SHA256(code_verifier), кодуємо base64url.
- Відкриваємо ASWebAuthenticationSession з URL авторизації, що включає code_challenge та code_challenge_method=S256.
- Після редиректу отримуємо authorization code.
- Відправляємо серверу POST‑запит з code, code_verifier, client_id.
- Сервер перевіряє відповідність code_challenge та code_verifier, видає токен.
Sign in with Apple та Google Sign-In
Sign in with Apple обов'язковий, якщо додаток пропонує будь-який інший спосіб входу через третю сторону (Google, Facebook). Apple вимагає це вже кілька років, порушення — rejection за Guideline 4.8. Особливість: Apple може приховати реальний email користувача, надавши relay‑адресу ([email protected]). Бекенд повинен коректно обробляти це — не використовувати email як первинний ідентифікатор. ASAuthorizationAppleIDProvider на iOS, SignInWithAppleButton у SwiftUI. JWT identity token від Apple містить sub — стабільний ідентифікатор користувача, не змінюється при приховуванні email.
Google Sign-In. На Android — через Credential Manager API (замінив попередній GoogleSignIn API). На 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.
Як організувати ротацію refresh токенів?
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 намагалися використати — компрометація, всі токени користувача відкликаються. Ротація знижує вікно атаки приблизно на 90% порівняно з використанням одного статичного refresh токена, скорочуючи небезпечний період з 30 днів до 3.
| Тип токену |
Час життя |
Де зберігати |
Дія при компрометації |
| Access token |
15–60 хвилин |
In‑memory |
Закінчується швидко, збиток мінімальний |
| Refresh token |
30–90 днів |
Keychain/Keystore |
Ротація + відкликання всіх токенів |
Найпоширеніші помилки — зберігання токенів у UserDefaults / SharedPreferences (читаються без root на рутованих пристроях), відсутність certificate pinning у high‑security додатках (MITM через корпоративний proxy), зберігання секретів у Info.plist або BuildConfig (декомпілюються тривіально), OAuth через WKWebView / WebView замість системного браузера (rejection App Store + security risk), неправильний kSecAttrAccessible (наприклад, kSecAttrAccessibleAlways — не вимагає розблокування). Усі ці помилки усуваються на етапі проектування.
Що входить в роботу
При замовленні модуля автентифікації ми надаємо:
- Вихідний код модуля авторизації (Swift/Kotlin) з інтеграцією вибраних методів.
- Документацію з архітектури та схеми токенів.
- Налаштований PKCE‑флоу для OAuth 2.0.
- Інтеграцію Sign in with Apple та Google Sign-In за вашими client_id.
- Конфігурацію біометрії з правильними protection‑прапорцями.
- Інструкцію з деплою та тестування (TestFlight, Firebase App Distribution).
- Чек‑лист для проходження рев'ю App Store та Google Play.
Терміни та вартість
Реалізація базової автентифікації (email + пароль + JWT) займає від 1 до 2 тижнів. Додавання OAuth, біометрії та 2FA — ще 1–3 тижні. Підсумкова вартість розраховується після аудиту вашого проєкту. Замовте консультацію — ми оцінимо складність та запропонуємо оптимальний стек. Зв'яжіться з нами для аудиту вашого проекту. Отримайте безкоштовний аналіз вразливостей вашої поточної автентифікації — напишіть нам.