Користувач входить у корпоративний додаток через портал, але при переході в мобільний додаток вимагається повторна аутентифікація. Це призводить до втрат часу та зниження продуктивності до 30%. Ми інтегруємо єдиний вхід (SSO) у корпоративні мобільні додатки — це не просто «вхід через корпоративний обліковий запис». За цим стоїть інтеграція з Identity Provider (IdP), вибір правильного протоколу, обробка токенів відповідно до корпоративних політик безпеки та коректна обробка сценаріїв примусового logout. Наш досвід: понад 20 впроваджень SSO для enterprise-клієнтів, що дозволяє знизити витрати на підтримку аутентифікації до 40% за рахунок централізованого управління. Окупність такого впровадження — від 6 до 12 місяців. Отримайте консультацію: оцінимо ваш проект за 2 дні.
Як вибрати між OIDC та SAML?
SAML 2.0 — XML-based протокол, широко застосовуваний в enterprise: ADFS, Okta, PingFederate. Для мобільних додатків незручний: SAML Assertions передаються через HTTP POST (browser-based flow), що вимагає WebView або браузерного редиректу. Нативного мобільного SDK для SAML практично немає.
OpenID Connect (OIDC) — надбудова над OAuth 2.0, використовує JWT. Нативно підтримується в мобільних бібліотеках. AppAuth — стандартна реалізація Authorization Code Flow з PKCE для iOS та Android. AppAuth знижує час розробки вдвічі порівняно з самописною реалізацією, а також підвищує безпеку за рахунок вбудованої підтримки PKCE. OIDC працює в 2-3 рази швидше за SAML на мобільних пристроях завдяки компактним JWT-токенам.
Якщо IdP підтримує обидва протоколи (Okta, Azure AD, PingFederate — підтримують), для мобільного вибираємо OIDC. SAML потрібен лише коли IdP змушує: on-premise ADFS без сучасного оновлення, або legacy корпоративна система лише з SAML.
Чому OIDC кращий для мобільних додатків?
OIDC використовує стандарт RFC 8252 — Authorization Code Flow з PKCE обов'язковий для мобільних. Згідно з RFC 8252, це забезпечує захист від перехоплення коду. SAML же вимагає браузерних редиректів і не має нативних мобільних SDK, що сповільнює розробку та знижує безпеку.
| Критерій | OIDC | SAML |
|---|---|---|
| Підтримка мобільних SDK | Нативна (AppAuth) | Відсутня, лише WebView |
| Формат токенів | JWT (компактний) | XML-твердження (громіздкий) |
| PKCE | Вбудована підтримка | Не підтримується |
| Простота впровадження | Висока | Низька (необхідний WebView) |
Як реалізувати OIDC за 5 кроків
- Налаштуйте IdP: зареєструйте мобільний додаток, вкажіть redirect URI та дозвольте Authorization Code Flow з PKCE.
-
Підключіть SDK AppAuth: додайте залежність у
build.gradle(Android) або через CocoaPods (iOS). - Створіть AuthorizationRequest: вкажіть issuer, client_id, redirect URI, scopes (openid, email, profile, offline_access).
-
Запустіть flow: викличте
getAuthorizationRequestIntent()та обробіть результат. - Збережіть AuthState: серіалізуйте в EncryptedSharedPreferences або Keychain.
Реалізація OIDC через AppAuth
Authorization Code Flow з PKCE — обов'язковий стандарт для мобільних. Жодних implicit flow — вони deprecated.
// Android — AppAuth-Android
class AuthManager(private val context: Context) {
private val authService = AuthorizationService(context)
fun startLogin(activity: Activity) {
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"),
null, // registration
Uri.parse("https://login.microsoftonline.com/$tenantId/v2.0/.well-known/openid-configuration")
)
val request = AuthorizationRequest.Builder(
serviceConfig,
BuildConfig.CLIENT_ID,
ResponseTypeValues.CODE,
Uri.parse("com.company.app:/oauth2redirect")
)
.setScopes(
AuthorizationRequest.SCOPE_OPENID,
AuthorizationRequest.SCOPE_EMAIL,
AuthorizationRequest.SCOPE_PROFILE,
"offline_access"
)
.setPrompt("login") // Не кешувати сесію IdP для корпоративних вимог
.build()
val intent = authService.getAuthorizationRequestIntent(request)
activity.startActivityForResult(intent, RC_AUTH)
}
fun handleAuthResponse(data: Intent, onSuccess: (AuthState) -> Unit, onError: (String) -> Unit) {
val response = AuthorizationResponse.fromIntent(data)
val exception = AuthorizationException.fromIntent(data)
if (response != null) {
authService.performTokenRequest(response.createTokenExchangeRequest()) { tokenResponse, ex ->
if (tokenResponse != null) {
val authState = AuthState(response, tokenResponse, ex)
saveAuthState(authState)
onSuccess(authState)
} else {
onError(ex?.message ?: "Token exchange failed")
}
}
} else {
onError(exception?.message ?: "Authorization failed")
}
}
}
На iOS аналогічно через AppAuth-iOS:
let configuration = OIDServiceConfiguration(
authorizationEndpoint: URL(string: "https://login.microsoftonline.com/\(tenantId)/oauth2/v2.0/authorize")!,
tokenEndpoint: URL(string: "https://login.microsoftonline.com/\(tenantId)/oauth2/v2.0/token")!
)
let request = OIDAuthorizationRequest(
configuration: configuration,
clientId: clientId,
scopes: [OIDScopeOpenID, OIDScopeEmail, OIDScopeProfile, "offline_access"],
redirectURL: URL(string: "com.company.app:/oauth2redirect")!,
responseType: OIDResponseTypeCode,
additionalParameters: nil
)
currentAuthorizationFlow = OIDAuthState.authState(
byPresenting: request,
presenting: self
) { authState, error in
if let authState = authState {
self.authStateManager.save(authState)
}
}
Зберігання токенів та автоматичний refresh
AppAuth надає AuthState — об'єкт, який керує токенами та автоматично виконує refresh коли access token закінчується:
fun makeApiRequest(url: String) {
authState.performActionWithFreshTokens(authService) { accessToken, _, exception ->
if (exception != null) {
// Refresh failed — потрібен повторний логін
navigateToLogin()
return@performActionWithFreshTokens
}
// accessToken гарантовано свіжий
apiClient.get(url, bearerToken = accessToken)
}
}
AuthState потрібно серіалізувати та зберігати в EncryptedSharedPreferences (Android) / Keychain (iOS). Жодних SharedPreferences без шифрування — корпоративні MDM-політики це виявляють.
Деталі зберігання токенів
Для Android використовуємо EncryptedSharedPreferences з бібліотеки AndroidX Security. Вони шифрують дані за допомогою AES256 та прив'язують до пристрою через Android Keystore. На iOS використовуємо Keychain Services — стандартний захищений контейнер для паролів та токенів. Налаштування займає 1-2 дні.
| Платформа | Захищене сховище | Шифрування |
|---|---|---|
| Android | EncryptedSharedPreferences | AES256 (Tink) |
| iOS | Keychain Services | AES256 (CommonCrypto) |
SAML через WebView
Якщо IdP підтримує лише SAML без OIDC-обгортки — використовуємо custom WebView / SFSafariViewController для проходження SAML assertion flow. Backend отримує SAML Assertion, верифікує, створює власний JWT та повертає його клієнту через Deep Link.
Це менш безпечний підхід (credentials проходять через WebView), але іноді єдиний варіант. Відзначаємо це замовнику як технічний борг та рекомендуємо IdP-апгрейд.
Примусовий logout та відкликання сесії
Корпоративна вимога: при звільненні співробітника або компрометації облікового запису — негайне відкликання доступу. Механізм: backend інвалідує refresh token в IdP, наступний performActionWithFreshTokens повертає помилку, додаток перенаправляє на логін.
Для Azure AD: відкликання через Microsoft Graph POST /users/{id}/revokeSignInSessions. Для Okta: POST /api/v1/users/{userId}/sessions.
End-session endpoint — стандартний механізм OIDC для logout з IdP. Важливо його викликати при logout, інакше користувач може повторно увійти без пароля через SSO-cookie в браузері:
fun logout() {
val endSessionRequest = EndSessionRequest.Builder(serviceConfig)
.setIdTokenHint(authState.idToken)
.setPostLogoutRedirectUri(Uri.parse("com.company.app:/logout"))
.build()
authService.performEndSessionRequest(endSessionRequest, pendingIntent)
clearLocalAuthState()
}
Типові проблеми
Clock skew. JWT перевіряється за часом — якщо годинник на пристрої користувача відстає на 5+ хвилин, валідний токен буде відхилено як прострочений. Потрібна обробка помилки з рекомендацією синхронізувати час.
Redirect URI mismatch. Одна з найчастіших помилок на етапі налаштування. Redirect URI в коді (com.company.app:/oauth2redirect) має збігатися до символу з тим, що зареєстровано в IdP. Custom scheme vs Universal Link — різні формати.
Multiple IdP в одній організації. Великі корпорації з історією M&A часто мають кілька IdP. Потрібен механізм виявлення IdP за доменом email (discovery). Підтримується через OIDAuthorizationService.discoverConfiguration(forIssuer:).
Що входить у роботу
- Аналіз поточної IdP та корпоративних політик безпеки
- Вибір протоколу (OIDC/SAML) та архітектури інтеграції
- Налаштування Authorization Code Flow з PKCE
- Реалізація зберігання токенів у захищеному сховищі
- Інтеграція механізму token refresh
- Реалізація примусового logout з відкликанням сесій
- Написання документації та передача доступу до тестового середовища
- Навчання команди замовника
Терміни та вартість
Налаштування SSO (OIDC + один IdP): 2–4 тижні. Мультитенантність + кілька IdP + SAML fallback: 5–8 тижнів. Вартість розраховується індивідуально. Отримайте консультацію — оцінимо ваш проект за 2 дні. Замовте впровадження SSO у ваш додаток вже сьогодні.







