Аутентификация с использованием скрытого ключа: как внешний пользователь может получить токен, не раскрывая секрет клиента

#keycloak #openid-connect

#скрытый ключ #OpenID-connect

Вопрос:

У меня есть запрос о том, как keycloak должен работать с клиентом без доступа к GUI.

В принципе, у меня есть:

  • Сервер скрытого ключа, настроенный с областью, клиентами (тип доступа конфиденциальный) и пользователями
  • Серверное приложение с графическим интерфейсом, которое также предоставляет API, защищенное с помощью keycloak (клиент, пользователь, blablabla)

Это уже работает, поскольку я могу войти в графический интерфейс, выполнить перенаправление и т.д..

Даже доступ к API работает хорошо, когда у меня есть доступ к графическому интерфейсу: я захожу в свой пользовательский интерфейс, следую перенаправлению и заставляю свой пользовательский интерфейс отображать токен. Пользователь (чтобы отличать пользователя от приложения) может использовать токен в любом клиенте API.

В этом контексте пользователь никогда не видит секрет клиента, что инстинктивно является правильным способом. (обратите внимание, что я очень открыт для людей, которые говорят мне, что мой инстинкт неверен!)

Чего я пока не могу сделать, так это найти способ, которым серверное приложение (без GUI) может получить действительный токен?

Для авторизации authorization_endpoint, насколько я понимаю, требуются как идентификатор клиента, так и секрет клиента), чтобы получить токен, чего я бы предпочел избежать: я не думаю, что предоставление моего клиентского секрета всем моим «клиентам» является правильным способом сделать это.

В качестве альтернативы я мог бы создать API на своем клиенте, который запрашивал бы учетные данные пользователя и запрашивал токен от его имени, но это предоставило бы учетные данные клиентов моему приложению, что противоречит всей концепции!

Я попытался установить свой тип доступа клиента как общедоступный, но когда я использую приведенный ниже вызов API, я также получаю ошибку:

 POST /auth/realms/realmname/protocol/openid-connect/tokenAPI 
  
 'grant_type=client_credentials'
'client_id=client_id'
'username=username'
'password=password'
  
 {
    "error": "unauthorized_client",
    "error_description": "Public client not allowed to retrieve service account"
}
  

Кто-нибудь знает, как это должно быть сделано?

Заранее спасибо.

Макс

Комментарии:

1. Еще раз привет, если мой ответ соответствует вашим потребностям, вы не возражаете «принять» его? (ср. meta.stackexchange.com/questions/5234 / … ). Если нет, пожалуйста, дайте мне знать, чего не хватает. Это поможет нам и будущим посетителям.

Ответ №1:

(…) Серверное приложение (без графического интерфейса пользователя) может получить действительный токен… обычно используется Client Credentials поток.

Но в этом случае мы бы определили выделенный клиент для вашего серверного (клиентского?) приложения для аутентификации. Возвращенный токен (не привязанный к конкретному пользователю) будет использоваться для авторизации в разрешенных приложениях (т. Е. ваших классических клиентах с графическим интерфейсом или API).

Итак, в принципе, вы должны (вкратце):

  • определите конкретную конфиденциальную Client информацию в вашем Keycloak
  • добавьте нужные приложения (или других клиентов) к клиенту Scope(s) . Те, которые вы хотите авторизовать транзитивно с этого клиента.
  • аутентифицируйтесь на этом клиенте с Client Credentials потоком (учитывая конечную точку токена, идентификатор клиента, учетные данные, область действия)
  • убедитесь, что вы проходите аутентификацию через TLS и что параметры включены в request body (а не в заголовки — для повышения конфиденциальности)
  • дальнейшее усиление безопасности вашего клиента (ов)

Если вы больше не хотите, чтобы это конкретное серверное (клиентское?) приложение получало доступ к вашим приложениям, вы можете изменить секрет / учетные данные соответствующего клиента «аутентификации» или просто удалить его.


«Я не думаю, что предоставление моего клиентского секрета всем моим «клиентам» является правильным способом сделать это».

Вы правы, и предложенный выше метод строго избегает этого. У каждого клиента будут свои собственные учетные данные.

Редактировать

(добавляю больше деталей)

Выполнив описанное выше, вы получите следующую схему:

                                    Flow         Keycloak Server
C/S app. or Customer X  <--- Client Creds --->  Auth. Client X
                         --- Access Token --->  Appl. Client    <-->  Appl. Server

C/S app. or Customer Y  <--- Client Creds --->  Auth. Client Y
                         --- Access Token --->  Appl. Client    <-->  Appl. Server

         Browser users  <--- Standard  ------>  Appl. Client    <-->  Appl. Server
  

Примечание: это не подробная блок-схема. Стрелки здесь в основном показывают взаимосвязи.

Наконец, пожалуйста, обратите внимание, что терминология здесь может немного отличаться, но предлагаемый метод в основном тот же, что использует Google. Таким образом, вы также можете извлечь оттуда некоторое вдохновение:

Комментарии:

1. Большое спасибо @bsaverino, кажется, это ответ на мой вопрос. Хотелось бы, чтобы был более простой способ… Есть ли способ, которым приложение-потребитель может запросить токен (токен 1) для скрытого ключа напрямую (без упоминания клиента вообще). Затем приложение вызовет API на клиенте и предоставит token1. Затем клиент вызовет keycloak со своим id / secret token1 и получит окончательный токен доступа и вернет его отправителю запроса?

2. Насколько мне известно, все потоки OAuth2, применимые к контексту M2M (где пользователи и социальные сети не имеют смысла), требуют указания идентификатора клиента. И насколько я понимаю, что вы собираетесь делать, это выглядит очень гибридно, и я бы предпочел выбрать в этом случае классический поток кода авторизации (с пользователем / паролем, т.Е. auth0.com/docs/flows/authorization-code-flow ) и внедрите в свое приложение способ возврата токена доступа пользователя (когда обычно он должен оставаться скрытым, чтобы избежать token leakage …).

3. Я не рекомендую этот метод, поскольку возвращенный токен может предоставлять больше областей или утверждений, чем необходимо, или может быть слишком разрешительным, недолговечным или неправильно сконфигурированным для публичного / внешнего использования. В некотором смысле это в основном соответствовало бы неявному потоку, который также устарел или больше не рекомендуется по некоторым из этих причин (и фактически еще нескольким).

Ответ №2:

У меня была такая же проблема несколько недель назад

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

Итак, вот мое решение:

  1. При keycloak создайте клиента (например, front_end_client) с типом предоставления public Этот клиент будет использоваться приложением внешнего интерфейса для аутентификации пользователей с использованием неявного потока (с PKCE будет более безопасным)

  2. При keycloak создайте второго клиента (в той же области, что и первый клиент) с типом grant confidential , этот клиент будет использоваться внутренним API

Итак, вот как это работает:

  1. Внешнее приложение аутентифицирует пользователей и получает токен доступа (используя font_end_client)

  2. Внешнее приложение отправляет этот токен для каждого запроса на серверную часть

  3. Серверное приложение проверяет этот токен и может извлекать из него разрешения