Keycloak - поток аутентификации Oauth-2 - PullRequest
0 голосов
/ 17 мая 2019

Мы пытаемся интегрировать (я и мои коллеги) аутентификацию Oauth2 в связь некоторых из моих REST-приложений.В качестве поставщика и менеджера аутентификации мы хотели бы использовать Keycloak.Мы читаем некоторую документацию и думаем, что поняли, как это работает.Теперь нам хотелось бы узнать от вас, соблюдает ли разработанная нами аутентификация потока принципы протокола Oauth2.Аутентификация потока, которую мы решили использовать, включает генерацию токенов, и их валидация:

графическое представление аутентификации потока

Давайте приступим к описанию примераобмен данными между двумя приложениями (приложение A и приложение B), которые используют Oauth2 для аутентификации друг друга.На изображении выше показано, как приложение A запрашивает Keycloak для запроса токена;впоследствии приложение A отправляет запрос приложению B, вводя токен, полученный от Keycloak;в этот момент приложение B после получения токена запрашивает Keycloak и просит его проверить полученный токен;наконец, приложение B получит ответ относительно действительности токена, и на основе этого ответа оно сможет решить, принять ли запрос, полученный от приложения A, или отклонить его.

Давайте посмотримтехнические подробности обмена:

1 - Приложение A запрашивает Keycloak посредством аутентификации для создания нового токена.

Пример HTTP-запроса:

POST /auth/realms/OMS/protocol/openid-connect/token HTTP/1.1
Host: local-keycloak.it:8080
Content-Type: application/x-www-form-urlencoded
cache-control: no-cache

client_id=oms-test&client_secret=039a6b94-44a7-4dae-b8a4-e7b673eee8e5&grant_type=client_credentials&scope=openid
  • / auth / realms / {REALM_NAME} / protocol / openid-connect / token: конечная точка Keycloak для вызова для генерации и получения токена.

Тело HTTP-сообщения, отправляемого на сервер, по сути представляет собой одну гигантскую строку запроса - пары имя / значение отделяются амперсандом (&), а имена отделяются от значений символом равенствазнак равноФактически тип содержимого тела указывается как «application / x-www-form-urlencoded».

2 - Keycloak, если аутентификация приложения A прошла успешно, сгенерируйте токен иперешлите его на него.

Пример ответа HTTP:

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJvU2ZDYjlzSTV6eTNha1BLU2hSMFVYeXJqNjltUFhEcFdjdWM1SG1mUlFvIn0.eyJqdGkiOiJhNmEzZmQ3ZS00NDdhLTQzNTMtOWM2Yi03ZjFhN2QwZDAxYTEiLCJleHAiOjE1NTc5OTUzNTUsIm5iZiI6MCwiaWF0IjoxNTU3OTk1MDU1LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvT01TIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjYwZThiNjVjLTY1OTYtNDMyYS1hNjY4LTEzOTljMTY3ZDM4NiIsInR5cCI6IkJlYXJlciIsImF6cCI6Im9tcy10ZXN0IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiZWE1ZTFjNDItYjkzNS00ZGEwLTlkNjYtYTAyOWZkZjc3N2IyIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0OjgwODAiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjEyNy4wLjAuMSIsImNsaWVudElkIjoib21zLXRlc3QiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInVzZXJfbmFtZSI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdCIsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdCIsImNsaWVudEFkZHJlc3MiOiIxMjcuMC4wLjEiLCJlbWFpbCI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdEBwbGFjZWhvbGRlci5vcmcifQ.RkQ178gLfWoA1-F7w5e4q7FXzhLhigAOPrUN1QrX3oz7PxFuqhm_GopWcX0hHNgX0uFNtSGC1iWn04H5VzcevcDK42w5gV5TWo9WJ5CJp-NRjYdsEST_PhI6KlHsXgik53qF_kCeKwB-_eal1rVdlEY7WO1kv1p8cih-bEA9NNBdA5C6_iA4IF6Jfrdp8lJ_DeRtnbXqsc1dgYdJbYru_BGiYTkolLXxIqfTOTENH64to3EAEVMQ21c_zQtmRxVOaD_fvNOZMqOmWeKk02Z6rfq2m77M6edv1LvlGAnVmx7-zRG6a6eL-t6rZiOwr3eohJ67U77ndzJKrl5J5Wuiwg",
    "expires_in": 300,
    "refresh_expires_in": 1800,
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI2NzhjY2M0NC03ZTY3LTRmODAtOTk5NC1hOTA0NmI3NGY2YTgifQ.eyJqdGkiOiJiYTU3NzZjYi03Zjg1LTRhNTAtOGM5Ni1kYWQ3OTRlZGRjZWIiLCJleHAiOjE1NTc5OTY4NTUsIm5iZiI6MCwiaWF0IjoxNTU3OTk1MDU1LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvT01TIiwiYXVkIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL2F1dGgvcmVhbG1zL09NUyIsInN1YiI6IjYwZThiNjVjLTY1OTYtNDMyYS1hNjY4LTEzOTljMTY3ZDM4NiIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJvbXMtdGVzdCIsImF1dGhfdGltZSI6MCwic2Vzc2lvbl9zdGF0ZSI6ImVhNWUxYzQyLWI5MzUtNGRhMC05ZDY2LWEwMjlmZGY3NzdiMiIsInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJvcGVuaWQgZW1haWwgcHJvZmlsZSJ9.BkXWMLmuf1c0OBUeg2P2262LLvTmhXg46y4-rrvebNE",
    "token_type": "bearer",
    "id_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJvU2ZDYjlzSTV6eTNha1BLU2hSMFVYeXJqNjltUFhEcFdjdWM1SG1mUlFvIn0.eyJqdGkiOiIyYjdjZGI2Ny1kYjM3LTQ5MTQtYWNiYi0xNmU5MDA4YzQ4N2IiLCJleHAiOjE1NTc5OTUzNTUsIm5iZiI6MCwiaWF0IjoxNTU3OTk1MDU1LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvT01TIiwiYXVkIjoib21zLXRlc3QiLCJzdWIiOiI2MGU4YjY1Yy02NTk2LTQzMmEtYTY2OC0xMzk5YzE2N2QzODYiLCJ0eXAiOiJJRCIsImF6cCI6Im9tcy10ZXN0IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiZWE1ZTFjNDItYjkzNS00ZGEwLTlkNjYtYTAyOWZkZjc3N2IyIiwiYWNyIjoiMSIsImNsaWVudEhvc3QiOiIxMjcuMC4wLjEiLCJjbGllbnRJZCI6Im9tcy10ZXN0IiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJ1c2VyX25hbWUiOiJzZXJ2aWNlLWFjY291bnQtb21zLXRlc3QiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzZXJ2aWNlLWFjY291bnQtb21zLXRlc3QiLCJjbGllbnRBZGRyZXNzIjoiMTI3LjAuMC4xIiwiZW1haWwiOiJzZXJ2aWNlLWFjY291bnQtb21zLXRlc3RAcGxhY2Vob2xkZXIub3JnIn0.C0YkK-B4LnKH3NBCxHjuAkVZKVikh3FaUPIUpToCVFKkgefZRF7JS2yddC4ejxn4_B4y56TBMdVSXg5dEk-ghkz_f1VOR1whRY0HAC6Z5izEJBOHesASWoxJE43QJHXoDYzNWJK1S4JQ6W_BF5KobVHrXL2fmb-ypLBJCc8EAMTYEC-fpxT_T3NkDbsAjmnoCTl1YmRiDkV0sqUKerx5irIZJ3S297Z0Ub4Ahal8ObX7t3JbpJ-SBEvRvNo0PriZdk7C1DZQEhc77v2qnpeyqkwcRkAhZ0uXb5QF32J6dxhKh8-gZHYCauMdzeNmkh-962RnWXqyhGOYipLarnmzjg",
    "not-before-policy": 0,
    "session_state": "ea5e1c42-b935-4da0-9d66-a029fdf777b2",
    "scope": "openid email profile"
}

Как мы видим, нам предоставлен токен доступа для использования в качестве токена аутентификации и авторизации.между нашими платформами.Токен доступа имеет более быстрый срок действия, чем токен обновления, и с его помощью можно регенерировать токен доступа.

3 - Приложение A отправляет запрос приложению B, используя полученный токен (access_token).

Пример HTTP-запроса:

POST /omsesb/order/placeOrder HTTP/1.1
Host: application-b.it:8081
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJvU2ZDYjlzSTV6eTNha1BLU2hSMFVYeXJqNjltUFhEcFdjdWM1SG1mUlFvIn0.eyJqdGkiOiJhNmEzZmQ3ZS00NDdhLTQzNTMtOWM2Yi03ZjFhN2QwZDAxYTEiLCJleHAiOjE1NTc5OTUzNTUsIm5iZiI6MCwiaWF0IjoxNTU3OTk1MDU1LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvT01TIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjYwZThiNjVjLTY1OTYtNDMyYS1hNjY4LTEzOTljMTY3ZDM4NiIsInR5cCI6IkJlYXJlciIsImF6cCI6Im9tcy10ZXN0IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiZWE1ZTFjNDItYjkzNS00ZGEwLTlkNjYtYTAyOWZkZjc3N2IyIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0OjgwODAiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjEyNy4wLjAuMSIsImNsaWVudElkIjoib21zLXRlc3QiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInVzZXJfbmFtZSI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdCIsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdCIsImNsaWVudEFkZHJlc3MiOiIxMjcuMC4wLjEiLCJlbWFpbCI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdEBwbGFjZWhvbGRlci5vcmcifQ.RkQ178gLfWoA1-F7w5e4q7FXzhLhigAOPrUN1QrX3oz7PxFuqhm_GopWcX0hHNgX0uFNtSGC1iWn04H5VzcevcDK42w5gV5TWo9WJ5CJp-NRjYdsEST_PhI6KlHsXgik53qF_kCeKwB-_eal1rVdlEY7WO1kv1p8cih-bEA9NNBdA5C6_iA4IF6Jfrdp8lJ_DeRtnbXqsc1dgYdJbYru_BGiYTkolLXxIqfTOTENH64to3EAEVMQ21c_zQtmRxVOaD_fvNOZMqOmWeKk02Z6rfq2m77M6edv1LvlGAnVmx7-zRG6a6eL-t6rZiOwr3eohJ67U77ndzJKrl5J5Wuiwg
Client-Id: oms-test
cache-control: no-cache

  {
    "orders": 
        {
            "order": {
                    ...
            }
        }
   }

Как мы видим, заголовок «Авторизация» содержит токен доступа, полученный приложением A от Keycloak.Мы добавили еще один заголовок «Client-Id», который будет содержать идентификатор пользователя, с которым приложение A запросило генерацию токена из Keycloak.

В этом примере приложение A просит приложение B ввести новый заказ, но должно сначала подтвердить свою подлинность и, следовательно, должно быть авторизовано.

4 - Приложение B запрашивает Keycloak,с запросом, чтобы проверить токен доступа, полученный из приложения A.

Пример HTTP-запроса:

POST /auth/realms/OMS/protocol/openid-connect/token/introspect HTTP/1.1
Host: local-keycloak.it:8080
Content-Type: application/x-www-form-urlencoded
cache-control: no-cache

client_id=account&client_secret=d67da47e-387a-4930-a89a-eda0296c4896&token=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJvU2ZDYjlzSTV6eTNha1BLU2hSMFVYeXJqNjltUFhEcFdjdWM1SG1mUlFvIn0.eyJqdGkiOiJhNmEzZmQ3ZS00NDdhLTQzNTMtOWM2Yi03ZjFhN2QwZDAxYTEiLCJleHAiOjE1NTc5OTUzNTUsIm5iZiI6MCwiaWF0IjoxNTU3OTk1MDU1LCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvYXV0aC9yZWFsbXMvT01TIiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjYwZThiNjVjLTY1OTYtNDMyYS1hNjY4LTEzOTljMTY3ZDM4NiIsInR5cCI6IkJlYXJlciIsImF6cCI6Im9tcy10ZXN0IiwiYXV0aF90aW1lIjowLCJzZXNzaW9uX3N0YXRlIjoiZWE1ZTFjNDItYjkzNS00ZGEwLTlkNjYtYTAyOWZkZjc3N2IyIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyJodHRwOi8vbG9jYWxob3N0OjgwODAiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInJlc291cmNlX2FjY2VzcyI6eyJhY2NvdW50Ijp7InJvbGVzIjpbIm1hbmFnZS1hY2NvdW50IiwibWFuYWdlLWFjY291bnQtbGlua3MiLCJ2aWV3LXByb2ZpbGUiXX19LCJzY29wZSI6Im9wZW5pZCBlbWFpbCBwcm9maWxlIiwiY2xpZW50SG9zdCI6IjEyNy4wLjAuMSIsImNsaWVudElkIjoib21zLXRlc3QiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsInVzZXJfbmFtZSI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdCIsInByZWZlcnJlZF91c2VybmFtZSI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdCIsImNsaWVudEFkZHJlc3MiOiIxMjcuMC4wLjEiLCJlbWFpbCI6InNlcnZpY2UtYWNjb3VudC1vbXMtdGVzdEBwbGFjZWhvbGRlci5vcmcifQ.RkQ178gLfWoA1-F7w5e4q7FXzhLhigAOPrUN1QrX3oz7PxFuqhm_GopWcX0hHNgX0uFNtSGC1iWn04H5VzcevcDK42w5gV5TWo9WJ5CJp-NRjYdsEST_PhI6KlHsXgik53qF_kCeKwB-_eal1rVdlEY7WO1kv1p8cih-bEA9NNBdA5C6_iA4IF6Jfrdp8lJ_DeRtnbXqsc1dgYdJbYru_BGiYTkolLXxIqfTOTENH64to3EAEVMQ21c_zQtmRxVOaD_fvNOZMqOmWeKk02Z6rfq2m77M6edv1LvlGAnVmx7-zRG6a6eL-t6rZiOwr3eohJ67U77ndzJKrl5J5Wuiwg
  • / auth / realms / {REALM_NAME} / protocol / openid-connect / token / introspect: конечная точка Keycloak для вызова для проверки токена.

5 - Keycloak в этот момент отвечает, давая результатпроверка в качестве ответа.Результат может быть положительным или отрицательным.

Пример ответа HTTP, содержащего положительный результат проверки:

{
    "jti": "26b6794d-3d83-446a-a106-10dfb14793c3",
    "exp": 1557997077,
    "nbf": 0,
    "iat": 1557996777,
    "iss": "http://local-keycloak:8080/auth/realms/OMS",
    "aud": "account",
    "sub": "60e8b65c-6596-432a-a668-1399c167d386",
    "typ": "Bearer",
    "azp": "oms-test",
    "auth_time": 0,
    "session_state": "88a63302-b148-42e4-81d0-5cb81c446903",
    "preferred_username": "service-account-oms-test",
    "email": "service-account-oms-test@placeholder.org",
    "email_verified": false,
    "acr": "1",
    "allowed-origins": [
        "http://local-keycloak:8080"
    ],
    "realm_access": {
        "roles": [
            "offline_access",
            "uma_authorization"
        ]
    },
    "resource_access": {
        "account": {
            "roles": [
                "manage-account",
                "manage-account-links",
                "view-profile"
            ]
        }
    },
    "scope": "openid email profile",
    "clientHost": "application-a.it",
    "clientId": "oms-test",
    "user_name": "service-account-oms-test",
    "clientAddress": "80.10.10.1",
    "client_id": "oms-test",
    "username": "service-account-oms-test",
    "active": true
}

Как мы видим, проверка прошла успешно (active = true): Keycloak распознал токен (еще не истек) и вернул приложению B набор информации, с помощью которой он может решить, принимать или нет запрос приложения A.

С помощью этой информации приложение B может распознать пользователя, который запросил токен ранее, по идентификатору клиента, IP-адресу, адресу и другой информации.

В этом случае, чтобы избежать возможной атаки "человек посередине", приложение B может сопоставлять данные, полученные из приложения A, с данными, полученными провайдером аутентификации: оно может проверить, что идентификатор клиента и IP совпадают.

Пример ответа HTTP, содержащего отрицательный результат проверки:

{
    "active": false
}

Результат отрицательный, если токен не существует, токен истек или приложение, запрашивающее проверку токена, использует пользователя, который не обладает необходимыми полномочиями для запроса проверки.

6 - На основании результата, полученного от Keycloak, приложение B сообщает приложению A, принят его запрос или нет.

Итак, по вашему мнению, может ли этот процесс аутентификации быть правильным и, следовательно, уважать принципы Oauth2?

Ответы [ 2 ]

0 голосов
/ 06 июня 2019

Я не получил никакого ответа от пользователей стека, но, глядя на поток с моими коллегами и изучая Oauth2, мы поняли, что реализованный нами поток эффективен и соответствует правилам и характеристикам Oauth2. Кроме того, введя окончательный элемент управления на основе IP-адреса и имени пользователя клиента, мы можем убедиться в том, что клиент, запросивший токен, тот же, который затем представил его в качестве авторизации.

0 голосов
/ 06 июня 2019

Удалось ли вам проверить, является ли это действительным потоком аутентификации? Я нашел нечто подобное здесь: Выдача «ключей API» с использованием Keycloak .

...