null

Коротко об OpenId Connect

Общие сведения

OpenId Connect - простой слой учетных данных поверх протокола OAuth 2.0. Данный протокол является протоколом системы единого входа, позволяющей использовать пользователю одну учётную запись для авторизации на различных интернет ресурсах.

OpenId Connect содержит в себе несколько различных потоков взаимодейтсвия, наиболее распространённым из которых является Code Flow, его и рассмотрим.

Code Flow состоит из следующих шагов:

  1. Подготовка запроса авторизации
  2. Запрос авторизации
  3. Аутентификация пользователя
  4. Получение сервером авторизации данных пользователя
  5. Перенаправление пользователя обратно на ресурс, с которого был отправлен запрос на авторизацию
  6. Получение ресурсом кода авторизации из запроса пользователя
  7. Запрос токенов с использованием кода авторизации
  8. Получение токенов
  9. Валидация токенов   
 

 

Всё взаимодействие с провайдером 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.

 

Более детально про данную технологию можно почитать в спецификации

 

 
 
Назад