Реализация авторизации через OpenID Connect в мобильном приложении
OpenID Connect (OIDC) — это OAuth 2.0 плюс стандартизованный способ получить информацию о пользователе. OAuth 2.0 решает задачу авторизации (доступ к ресурсам), OIDC добавляет аутентификацию (кто этот пользователь). Разница принципиальная: OAuth 2.0 access token не гарантирует, что в нём есть данные пользователя в предсказуемом формате; OIDC ID token — JWT с фиксированным набором claims (sub, iss, aud, exp, iat как минимум).
ID Token: что с ним делать правильно
Главная ошибка — доверять ID token без верификации подписи. Видел проекты, где мобильное приложение парсит payload JWT base64-декодингом и читает sub claim — без проверки подписи, без проверки iss и aud. Это эквивалентно доверию любому JWT от кого угодно.
Правильный flow:
- Получаем ID token от Authorization Server.
- Скачиваем JSON Web Key Set (JWKS) по
jwks_uriиз discovery document (.well-known/openid-configuration). - Верифицируем подпись ID token по публичному ключу из JWKS.
- Проверяем
iss== ожидаемый issuer,audсодержит нашclient_id,expне истёк,nonceсовпадает (защита от replay-атак).
На практике всё это делает AppAuth в связке с JWTDecode (iOS) или nimbus-jose-jwt (Android). Самостоятельная реализация JWKS-верификации — источник уязвимостей.
Nonce
Nonce генерируем криптографически до начала authorization request, сохраняем в памяти, передаём в параметрах запроса. После получения ID token — проверяем, что nonce в token совпадает. Если сервер вернул token без nonce или с другим — отклоняем аутентификацию. Это защищает от CSRF и replay-атак на мобильном уровне.
Discovery Document и автоконфигурация
OIDC провайдеры публикуют метаданные по .well-known/openid-configuration. AppAuth умеет загружать их автоматически — нет нужды хардкодить endpoints:
// iOS — автодискавери
OIDAuthorizationService.discoverConfiguration(forIssuer: issuerURL) { config, error in
guard let config else { return }
// config содержит authorizationEndpoint, tokenEndpoint, jwksURL и т.д.
}
// Android
AuthorizationServiceConfiguration.fetchFromIssuer(issuerUri) { config, error ->
// используем config для построения AuthorizationRequest
}
Кэшируем discovery document на разумный срок (час-два), не скачиваем при каждой операции.
UserInfo endpoint
После получения access token можно запросить userinfo_endpoint для получения дополнительных claims (email, name, picture, phone_number). Claims в ID token намеренно минимальны — OIDC Core не гарантирует их наличие без явного запроса через scope (profile, email, phone, address).
Важно: userinfo endpoint защищён access token. Если access token истёк — надо обновить его через refresh token до запроса userinfo. Делаем это прозрачно через interceptor/middleware в HTTP-клиенте.
Logout: часто забывают
OIDC определяет три варианта завершения сессии:
-
RP-Initiated Logout (rp = Relying Party, то есть ваше приложение): редиректим на
end_session_endpoint. - Front-Channel Logout: сервер пингует известные клиенты через iframe (не применимо для нативных приложений).
-
Back-Channel Logout: сервер отправляет POST-запрос на зарегистрированный
backchannel_logout_uriвашего сервера.
Для мобильных приложений работает только RP-Initiated. Открываем end_session_endpoint в браузере (ASWebAuthenticationSession / Custom Tabs), передаём id_token_hint и post_logout_redirect_uri. Без id_token_hint некоторые провайдеры (Keycloak, Auth0) не завершат сессию на сервере — пользователь выйдет из приложения, но SSO-сессия в браузере останется активной.
Интеграция с корпоративными IdP
Keycloak, Azure AD, Okta, Ping Identity — все поддерживают OIDC. Основные различия: формат claims (Azure AD использует oid вместо sub как стабильный идентификатор пользователя), дополнительные non-standard claims (роли, группы), особенности refresh token rotation.
Azure AD B2C добавляет User Flows — каждый flow имеет свой issuer, что ломает стандартный JWKS-кэш. Надо либо динамически резолвить issuer из ID token, либо настраивать статический список trusted issuers.
Сроки
Один OIDC провайдер со стандартной конфигурацией — 5–8 рабочих дней (включая тесты и настройку redirect). Корпоративный IdP с нестандартными claims и B2C user flows — 10–15 дней с учётом coordination с командой IdP.







