Как работают OAuth 2.0 и OpenID Connect
Основы3 минРедакция Innovialabs
Коротко
OAuth 2.0 выдаёт приложению ограниченный доступ к чужому API без передачи пароля, а OpenID Connect добавляет поверх него ID-токен, который сообщает приложению, кто именно вошёл.
Коротко
OAuth 2.0 — это протокол авторизации: он отвечает на вопрос «что приложению разрешено делать от имени пользователя». Пользователь не отдаёт приложению свой пароль, а приложение получает access token с ограниченными правами.
OpenID Connect (OIDC) — надстройка над OAuth 2.0 для аутентификации: она отвечает на вопрос «кто этот пользователь». Для этого OIDC добавляет ID token — подписанный документ с данными о входе.
Кнопка «Войти через Google» — это почти всегда OIDC.
Четыре роли
- Resource owner — пользователь, которому принадлежат данные.
- Client — ваше приложение: сайт, мобильное приложение, бот.
- Authorization server — сервер, который проверяет пользователя и выдаёт токены (например, Google).
- Resource server — API, к которому нужен доступ (например, Google Calendar API).
Иногда authorization server и resource server принадлежат одной компании, но логически это разные роли.
Authorization code flow с PKCE
Это основной и рекомендуемый сценарий для сайтов, SPA и мобильных приложений. PKCE (Proof Key for Code Exchange) защищает от перехвата кода авторизации.
- Приложение генерирует случайную строку code_verifier и считает от неё code_challenge — SHA-256 в base64url.
- Пользователя перенаправляют на authorization server с параметрами:
client_id,redirect_uri,scope,stateиcode_challenge. - Пользователь входит и соглашается выдать доступ.
- Сервер возвращает пользователя на
redirect_uriс одноразовым кодом и тем жеstate. - Приложение отправляет код вместе с
code_verifierна token endpoint. - Сервер проверяет, что verifier соответствует challenge, и выдаёт токены.
GET /authorize?response_type=code
&client_id=my-app
&redirect_uri=https://app.example.com/callback
&scope=openid%20email%20profile
&state=af0ifjsldkj
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
Даже если злоумышленник перехватит код на шаге 4, без code_verifier он не обменяет его на токены. Параметр state защищает от подмены ответа (CSRF): приложение сверяет его с тем, что сохранило перед редиректом.
Старый implicit flow, где токен приходил прямо в адресной строке, больше не рекомендуется — используйте code flow с PKCE.
Access token и ID token
| Access token | ID token | |
|---|---|---|
| Для кого | Для API (resource server) | Для вашего приложения |
| Зачем | Доступ к ресурсам | Узнать, кто вошёл |
| Формат | Любой, может быть непрозрачной строкой | Всегда JWT |
| Что проверять | Проверяет API | Подпись, iss, aud, exp, nonce |
Главное правило: не используйте access token как доказательство входа, а ID token — как пропуск к API. Это разные документы для разных получателей.
Ещё бывает refresh token — долгоживущий токен для получения новых access-токенов без повторного входа. Храните его только на сервере или в защищённом хранилище устройства.
Scopes
Scope — это запрошенное право. В OIDC обязателен scope openid, к нему обычно добавляют email и profile. Для доступа к API запрашивают отдельные scopes, например на чтение календаря.
Запрашивайте минимум: чем шире список, тем больше пользователей откажутся на экране согласия и тем больше ущерб при утечке токена.
Пример: вход через Google
- Вы регистрируете приложение в консоли Google и получаете
client_id, указываете разрешённыеredirect_uri. - Кнопка «Войти через Google» отправляет пользователя на страницу Google со scope
openid email profile. - После входа Google возвращает код, ваш сервер меняет его на ID token и access token.
- Вы проверяете ID token и берёте из него
sub— постоянный идентификатор пользователя у Google — и email. - По
subнаходите или создаёте пользователя в своей базе и открываете обычную сессию.
Если приложению нужен ещё и календарь, вы добавляете соответствующий scope — это уже авторизация через OAuth, а не просто вход.
Частые ошибки
- Идентифицировать пользователя по email вместо
sub: email может смениться. - Не проверять подпись и
audу ID token. - Хранить
client_secretв мобильном приложении или во фронтенде. - Разрешать произвольные
redirect_uriили wildcard-адреса. - Писать протокол с нуля вместо проверенной библиотеки.