Общие сведения
OpenId Connect - простой слой учетных данных поверх протокола OAuth 2.0. Данный протокол является протоколом системы единого входа, позволяющей использовать пользователю одну учётную запись для авторизации на различных интернет ресурсах.
OpenId Connect содержит в себе несколько различных потоков взаимодейтсвия, наиболее распространённым из которых является Code Flow, его и рассмотрим.
Code Flow состоит из следующих шагов:
- Подготовка запроса авторизации
- Запрос авторизации
- Аутентификация пользователя
- Получение сервером авторизации данных пользователя
- Перенаправление пользователя обратно на ресурс, с которого был отправлен запрос на авторизацию
- Получение ресурсом кода авторизации из запроса пользователя
- Запрос токенов с использованием кода авторизации
- Получение токенов
- Валидация токенов
Всё взаимодействие с провайдером OpenId происходит по протоколу https через EndPoint'ы, информацию о которых можно найти по следующему пути /.well-known/openid-configuration
Пример конфигурации
{
"issuer":
"https://server.example.com",
"authorization_endpoint":
"https://server.example.com/connect/authorize",
"token_endpoint":
"https://server.example.com/connect/token",
"token_endpoint_auth_methods_supported":
["client_secret_basic", "private_key_jwt"],
"token_endpoint_auth_signing_alg_values_supported":
["RS256", "ES256"],
"userinfo_endpoint":
"https://server.example.com/connect/userinfo",
"check_session_iframe":
"https://server.example.com/connect/check_session",
"end_session_endpoint":
"https://server.example.com/connect/end_session",
"jwks_uri":
"https://server.example.com/jwks.json",
"registration_endpoint":
"https://server.example.com/connect/register",
"scopes_supported":
["openid", "profile", "email", "address",
"phone", "offline_access"],
"response_types_supported":
["code", "code id_token", "id_token", "token id_token"],
"subject_types_supported":
["public", "pairwise"],
"id_token_signing_alg_values_supported":
["RS256", "ES256", "HS256"],
}
-
authorization_endpoint - точка подключения, на которую перенаправляется пользователь для авторизации и получения кода авторизации
-
token_endpoint - точка подключения, используемая для получения id и access токенов с использованием кода авторизации.
-
end_session_endpoint - точка окончания сессии, позволяющая произвести выход пользователя из системы SSO.
-
jwks_uri - точка подключения, предоставляющая информацию о публичных ключах, используемых при проверке подлинности id токена.
-
userinfo_endpoint - точка, через которую мы можем запросить информацию о пользователе.
-
registration_endpoint - точка динамической регистрации нового ресурса в системе провайдера.
Регистрация в системе
Перво-наперво, нам необходимо зарегистрировать наш ресурс в системе провайдера OpenId, для этого формируется POST запрос с content-type=application/json для регистрации, в котором указывается метаинформация о нашем ресурсе. Обязательным является массив redirect_uris - список разрёшенных uri, на которые может быть перенаправлен пользователь после авторизации. Данный запрос отправляется на registration_endpoint. Полный список параметров можно найти здесь
Пример запроса
POST /connect/register HTTP/1.1
Content-Type: application/json
Accept: application/json
Host: server.example.com
Authorization: Bearer eyJhbGciOiJSUzI1NiJ9.eyJ ...
{
"application_type": "web",
"redirect_uris":
["https://client.example.org/callback",
"https://client.example.org/callback2"]
}
Пример ответа
HTTP/1.1 201 Created
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"client_id": "s6BhdRkqt3",
"client_secret":
"ZJYCqe3GGRvdrudKyZS0XhGv_Z45DuKhCUk0gBR1vZk",
"client_secret_expires_at": 1577858400,
"registration_access_token":
"this.is.an.access.token.value.ffx83",
"registration_client_uri":
"https://server.example.com/connect/register?client_id=s6BhdRkqt3",
"token_endpoint_auth_method":
"client_secret_basic",
"application_type": "web",
"redirect_uris":
["https://client.example.org/callback",
"https://client.example.org/callback2"]
}
Здесь
- clinet_id - идентификатор нашего ресурса в системе провайдера
- client_secret - секретный ключ, который используется для запроса информации о пользователе
Подготовка запроса и получение кода
Подготовка запроса заключается в формировании редиректа на authorization_endpoint, в котором в качестве параметров указывается следующее:
- scope - список разделённых пробелом типов информации, которые мы запрашиваем. Например: email - запрос email'а пользователя, profile - запрос имени, фамилии, юзернейма и тд. Обязательным является scope openid, без него запрос не может быть считать правильным запросом OpenId Connect.
- responce_type - тип ответа. В нашем случае устанавливается code.
- client_id - идентификатор нашего ресурса, который мы получили при регистрации
- redirect_uri - URI, на который будет перенаправлен пользователь в случае успешной авториазации. Это значение должно совпадать с одним из тех значений, которые мы узакали в redirect_uris при регистрации
- state - произвольное значение, которое трудно угадать. Используется для валидации ответа, который мы получим от сервера авторизации.
- nonce - произвольное значение, которое трудно угадать. Используется для валидации полученных токенов.
Пример такого запроса
Location: https://server.example.com/authorize?
response_type=code
&scope=openid%20profile%20email
&client_id=s6BhdRkqt3
&state=af0ifjsldkj
&nonce=ajsdg6jh2jh3fasd
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
В случае успешной авторизации пользователь будет перенаправлен на redirect_uri с параметрами code и state. Значение state должно совпадать с тем, что мы указали при запросе. Значение code мы будем использовать для запроса токенов.
Пример ответа
HTTP/1.1 302 Found
Location: https://client.example.org/cb?
code=SplxlOBeZQQYbYS6WxSbIA
&state=af0ifjsldkj
Запрос и получение токенов
Получив код авторизации и проверив его валидность, подготавливаем запрос токенов.
Данный запрос отправляется методом POST на token_endpoint с content-type=application/x-www-form-urlencoded и должен содержать следующие поля:
- code
- client_id
- client_secret
- grant_type = authorization_code
- redirect_uri - URI, указанный при запросе кода авторизации
Пример запроса
POST /token HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
Пример ответа
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
{
"access_token": "SlAV32hkKG",
"token_type": "Bearer",
"refresh_token": "8xLOxBtZp8",
"expires_in": 3600,
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjFlOWdkazcifQ.ewogImlzc
yI6ICJodHRwOi8vc2VydmVyLmV4YW1wbGUuY29tIiwKICJzdWIiOiAiMjQ4Mjg5
NzYxMDAxIiwKICJhdWQiOiAiczZCaGRSa3F0MyIsCiAibm9uY2UiOiAibi0wUzZ
fV3pBMk1qIiwKICJleHAiOiAxMzExMjgxOTcwLAogImlhdCI6IDEzMTEyODA5Nz
AKfQ.ggW8hZ1EuVLuxNuuIJKX_V8a_OMXzR0EHR9R6jgdqrOOF4daGU96Sr_P6q
Jp6IcmD3HP99Obi1PRs-cwh3LO-p146waJ8IhehcwL7F09JdijmBqkvPeB2T9CJ
NqeGpe-gccMg4vfKjkM8FcGvnzZUN4_KSP0aAp1tOJ1zZwgjxqGByKHiOtX7Tpd
QyHE5lcMiKPXfEIQILVq0pc_E2DzL7emopWoaoZTF_m0_N0YzFC6g6EJbOEoRoS
K5hoDalrcvRYLSrQAZZKflyuVCyixEoV9GfNQC3_osjzw2PAithfubEEBLuVVk4
XUVrWOLrLl0nx7RkKU8NXNHq-rvKMzqg"
}
id_token - JWT токен, содержащий информацию об аутентификации и, опционально, дополнительную информацию о пользователе.
access_token - токен, с помощью которого мы можем запросить дополнительную информацию о пользователе.
Проверка валидности токена:
- Поле iss должно совпадать с полем issuer, которое указано в конфигурации /.well-known/openid-configuration
- Поле aud должно содержать наш client_id
- Поле nonce должно совпадать со значением, указанным нами при при запросе кода авторизации
- Время, указанное в поле exp, должно быть позже текущего времени
- Подпись должна быть валидна
Проверка подписи
Токен JWT содержит поле alg, обычно равное RS256, и, опционально, поле kid, содержащее идентификатор ключа, которым можно проверить правильность подписи.
Для проверки подписи мы получаем открытый ключ из jwks_uri, если указан kid, мы выбираем ключ с таким id. Если kid не указан, то jwks_uri должен содержать только один ключ. Далее, используя данный ключ и указанный алгоритм проверяем правильность подписи токена.
Запрос информации о пользователе
В случае, когда id_token не содержит необходимые нам данные о пользователе, мы можем отправить запрос на userinfo_endpoint, передав в заголовке Authorization наш access_token в качестве Bearer токена
Пример запроса
GET /userinfo HTTP/1.1
Host: server.example.com
Authorization: Bearer SlAV32hkKG
Пример ответа
HTTP/1.1 200 OK
Content-Type: application/json
{
"sub": "248289761001",
"name": "Jane Doe",
"given_name": "Jane",
"family_name": "Doe",
"preferred_username": "j.doe",
"email": "janedoe@example.com",
"picture": "http://example.com/janedoe/me.jpg"
}
Выход из системы
Для завершения сессии пользователю отправляется reirect на end_session_endpoint, в качестве параметров к которому указывается redirect_uri, на который будет перенаправлен пользователь, после успешного завершения сессии в системе провайдера OpenId.
Более детально про данную технологию можно почитать в спецификации