Введение
OAuth 2.0 и OpenID Connect (OIDC) являются фундаментальными протоколами в современной веб-разработке, обеспечивающими безопасную авторизацию и аутентификацию пользователей. OAuth 2.0 предоставляет приложениям делегированный доступ к ресурсам пользователя без необходимости передавать логин и пароль, в то время как OpenID Connect, надстройка над OAuth 2.0, добавляет уровень аутентификации, позволяя приложениям получать информацию о пользователе.
В этой статье мы подробно разберем оба протокола, их основные концепции, практические примеры использования, а также рассмотрим типичные ошибки и способы их избежать.
Основные концепции
OAuth 2.0
OAuth 2.0 — это протокол авторизации, который позволяет приложениям получать ограниченный доступ к учетным записям пользователей на HTTP-сервисах, таких как Facebook, GitHub и Google. Протокол делегирует аутентификацию сервису, который владеет учетной записью пользователя, и предоставляет авторизацию сторонним приложениям.
Ключевые участники (Roles):
Resource Owner (Владелец ресурса): Пользователь, который предоставляет доступ к своим данным.
Client (Клиент): Приложение, которое хочет получить доступ к данным пользователя.
Authorization Server (Сервер авторизации): Сервер, который аутентифицирует владельца ресурса и выдает токен доступа.
Resource Server (Сервер ресурсов): Сервер, на котором хранятся защищенные данные пользователя.
Типы грантов (Grant Types):
OAuth 2.0 определяет несколько способов получения токена доступа, называемых “грантами”. Наиболее распространенные из них:
Authorization Code: Наиболее безопасный и распространенный способ, используемый для веб-приложений, работающих на стороне сервера.
Implicit: Упрощенный способ, предназначенный для одностраничных приложений (SPA), работающих в браузере.
Resource Owner Password Credentials: Используется, когда клиент является доверенным (например, официальное мобильное приложение сервиса).
Client Credentials: Используется для межсервисного взаимодействия, когда нет прямого участия пользователя.
OpenID Connect
OpenID Connect — это простой уровень идентификации поверх протокола OAuth 2.0. Он позволяет клиентам проверять личность конечного пользователя на основе аутентификации, выполненной сервером авторизации, а также получать базовую информацию профиля о конечном пользователе в совместимом и REST-подобном виде.
Ключевые дополнения к OAuth 2.0:
ID Token: JSON Web Token (JWT), который содержит информацию о пользователе (claims), такую как его идентификатор, имя, email и т.
UserInfo Endpoint: Дополнительная точка доступа, с которой клиент может запросить больше информации о пользователе.
Стандартизированные области (Scopes): OIDC определяет стандартные области, такие как
openid,profile,email,addressиphone.
Практические примеры
Пример 1: Authorization Code Flow в OAuth 2.0
Рассмотрим сценарий, в котором веб-приложение “MyWebApp” хочет получить доступ к списку контактов пользователя из его учетной записи Google.
Диаграмма последовательности:
sequenceDiagram
participant User
participant MyWebApp
participant GoogleAuthServer
participant GoogleResourceServer
User->>MyWebApp: Хочу войти через Google
MyWebApp->>User: Редирект на GoogleAuthServer с client_id, redirect_uri, scope=contacts
User->>GoogleAuthServer: Вводит логин и пароль
GoogleAuthServer->>User: Показывает экран согласия
User->>GoogleAuthServer: Дает согласие
GoogleAuthServer->>User: Редирект на MyWebApp с authorization_code
User->>MyWebApp: Передает authorization_code
MyWebApp->>GoogleAuthServer: Отправляет authorization_code, client_id, client_secret
GoogleAuthServer->>MyWebApp: Возвращает access_token
MyWebApp->>GoogleResourceServer: Запрашивает контакты с access_token
GoogleResourceServer->>MyWebApp: Возвращает список контактов Пример кода (Node.js с использованием passport-google-oauth20):
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: "http://www.example.com/auth/google/callback"
},
function(accessToken, refreshToken, profile, cb) {
// Здесь можно сохранить информацию о пользователе в базу данных
return cb(null, profile);
}
));
// Редирект на Google для аутентификации
app.get('/auth/google', passport.authenticate('google', { scope: ['profile', 'email'] }));
// Callback URL после аутентификации
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
function(req, res) {
// Успешная аутентификация, редирект на главную страницу.
res.redirect('/');
}); Пример 2: OpenID Connect для аутентификации
Теперь рассмотрим, как “MyWebApp” может использовать OpenID Connect для аутентификации пользователя и получения информации о нем.
Диаграмма последовательности:
sequenceDiagram
participant User
participant MyWebApp
participant GoogleAuthServer
User->>MyWebApp: Хочу войти через Google
MyWebApp->>User: Редирект на GoogleAuthServer с client_id, redirect_uri, scope=openid profile email
User->>GoogleAuthServer: Вводит логин и пароль
GoogleAuthServer->>User: Показывает экран согласия
User->>GoogleAuthServer: Дает согласие
GoogleAuthServer->>User: Редирект на MyWebApp с authorization_code
User->>MyWebApp: Передает authorization_code
MyWebApp->>GoogleAuthServer: Отправляет authorization_code, client_id, client_secret
GoogleAuthServer->>MyWebApp: Возвращает access_token и id_token
MyWebApp->>MyWebApp: Декодирует id_token и получает информацию о пользователе Пример кода (Node.js с использованием openid-client):
const { Issuer } = require('openid-client');
Issuer.discover('https://accounts.google.com')
.then(function (googleIssuer) {
const client = new googleIssuer.Client({
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
redirect_uris: ['http://localhost:3000/cb'],
response_types: ['code'],
});
// Редирект на Google для аутентификации
const authorizationUrl = client.authorizationUrl({
scope: 'openid profile email',
});
res.redirect(authorizationUrl);
// Callback URL после аутентификации
app.get('/cb', async (req, res) => {
const params = client.callbackParams(req);
const tokenSet = await client.callback('http://localhost:3000/cb', params);
console.log('received and validated tokens %j', tokenSet);
console.log('validated ID Token claims %j', tokenSet.claims());
res.send('Success!');
});
}); Типичные ошибки и как их избежать
- Хранение
client_secretв публично доступном месте:client_secretдолжен храниться на сервере и никогда не должен быть доступен в клиентском коде (например, в JavaScript в браузере). - Неправильная реализация
redirect_uri:redirect_uriдолжен быть строго задан и проверяться на сервере авторизации, чтобы предотвратить атаки с перенаправлением. - Использование Implicit Flow для получения
access_tokenв новых приложениях: Этот поток считается менее безопасным, и рекомендуется использовать Authorization Code Flow с PKCE (Proof Key for Code Exchange) для SPA и мобильных приложений. - Отсутствие проверки состояния (state): Параметр
stateдолжен использоваться для предотвращения CSRF-атак. Клиент должен генерировать случайное значение, отправлять его на сервер авторизации и проверять его при получении ответа.
Связь с другими темами
JWT (JSON Web Tokens): ID Token в OpenID Connect является JWT. Понимание структуры и принципов работы JWT необходимо для работы с OIDC.
RESTful APIs: OAuth 2.0 часто используется для защиты RESTful API. Токен доступа передается в заголовке
Authorizationкаждого запроса к API.Single Sign-On (SSO): OpenID Connect является основой для многих SSO-решений, позволяя пользователям входить в несколько приложений, используя одну учетную запись.
Заключение
OAuth 2.0 и OpenID Connect являются мощными инструментами для создания безопасных и удобных систем авторизации и аутентификации. Понимание их принципов работы, различий и правильных сценариев использования является ключевым навыком для любого современного веб-разработчика. Надеемся, что эта статья помогла вам разобраться в этих важных протоколах и вы сможете успешно применять их в своих проектах.