Как проверить JWT id_token, выпущенный MS Azure AD?

У меня есть веб-приложение angularjs SPA, которое использует ADAL-JS (и adal- angular). Он настроен на аутентификацию с нашим корпоративным AD в MS Azure. Кажется, что входной поток работает правильно, и SPA получает id_token.

Затем, когда пользователь нажимает кнопку, SPA делает запрос к API REST, который я размещаю на AWS API Gateway. Я передаю id_token в заголовке Authorization: Bearer <id_token>. Шлюз API получает заголовок, как предполагалось, и теперь должен определить, является ли данный токен хорошим или не разрешать или запрещать доступ.

У меня есть тестовый токен, и он правильно анализирует на https://jwt.io/, но до сих пор мне не удалось найти открытый ключ или сертификат я должен использовать для проверки подписи. Я просмотрел:

Я думаю, что я должен использовать значение свойства x5c ключа в https://login.microsoftonline.com/common/discovery/keys, соответствующее свойствам kid и x5t из JWT id_token (в настоящее время a3QN0BZS7s4nN-BdrjbF0Y_LdMM, что приводит к значению x5c, начиная с "MIIDBTCCAe2gAwIBAgIQY..." ). Тем не менее, страница https://jwt.io/ сообщает "Недействительная подпись" (я также попытался обернуть ключевое значение с помощью "----- BEGIN CERTIFICATE - ---" и "----- END CERTIFICATE -----" ).

Кроме того, существует ли (возможно, python) библиотека, которая может облегчить проверку данного id_token, как в приведенном выше случае (так что мне не нужно будет сам захватить ключ подписи на лету?)... Лучшее, что я мог найти (ADAL для python), похоже, не предоставляет эту функцию?

Ответ 1

Лучшее решение, которое я мог бы собрать до сих пор:

Возьмите сертификат (первое значение в массиве свойств x5c) из https://login.microsoftonline.com/common/discovery/keys или https://login.microsoftonline.com/common/discovery/v2.0/keys, сопоставляя kid и x5t с id_token.

Оберните сертификат в -----BEGIN CERTIFICATE-----\n и \n-----END CERTIFICATE----- (как правило, это означает, что новые строки), и используйте результат как открытый ключ (в сочетании с id_token, https://jwt.io/).

Конечно, ваш фактический прецедент, скорее всего, будет иметь некоторую программу для проверки входящего JWT id_tokens, поэтому ваша цель не будет заключаться в том, чтобы просто получить токен для проверки через веб-интерфейс на https://jwt.io/.

Например, в python, мне нужно что-то вроде этого:

#!/usr/bin/env python

import jwt
from cryptography.x509 import load_pem_x509_certificate
from cryptography.hazmat.backends import default_backend

PEMSTART = "-----BEGIN CERTIFICATE-----\n"
PEMEND = "\n-----END CERTIFICATE-----\n"

mspubkey = "The value from the x5c property"
IDTOKEN = "the id_token to be validated"
tenant_id = "your tenant id"

cert_str = PEMSTART + mspubkey + PEMEND
cert_obj = load_pem_x509_certificate(cert_str, default_backend())
public_key = cert_obj.public_key()

decoded = jwt.decode(IDTOKEN, public_key, algorithms=['RS256'], audience=tenant_id)
if decoded:
    print "Decoded!"
else:
    print "Could not decode token."

Список библиотек JWT на разных языках см. в разделе сайта JWT. Я использую pyjwt, а cryptography (которая имеет двоичные зависимости, поэтому ее нужно создавать и упаковывать для целевой ОС).

И тогда, конечно, вы можете проверить дополнительные данные, такие как претензии, как рекомендованные здесь.

Ответ 2

Для решения JVM использование com.nimbusds:numbus-jose-jwt:4.29 является самым прямым способом анализа и проверки подписанного RSA256 id_token. Следующий код Scala анализирует токен JWT с помощью веб-ключа JSON:

    val jwt = SignedJWT.parse(token)

    val n = new Base64URL("Your Modulus Component of RSA Key")
    val e = new Base64URL("AQAB")
    val rsaKey = new RSAKey.Builder(n, e).keyUse(KeyUse.SIGNATURE).algorithm(JWSAlgorithm.RS256).build()

    val verified = jwt.verify(new RSASSAVerifier(rsaKey))

Чтобы получить набор ключей, потенциально используемых AAD B2C, вашему приложению по-прежнему необходимо будет получить набор ключей Java JSON из Azure Active Directory B2C discovery/v2.0/key. Вероятно, это должно быть кэшировано и иметь TTL не более 24 часов для повышения эффективности.

Ответ 3

  Кроме того, есть ли (возможно, python) библиотека, которая может облегчить проверку заданного id_token, как в случае выше (так что мне не придется самому брать ключ подписи на лету?)... Лучше всего я может найти (ADAL для Python), кажется, не предоставляют эту функцию?

Что бы это ни стоило, MSAL Python, являющийся преемником ADAL Python, проверяет токен идентификатора под капотом и предоставляет вам декодированные утверждения внутри идентификатора токена для вашего приложения. Чтобы ваше приложение, которое только что получило этот токен идентификатора через MSAL Python, может использовать его локально.

Затем, когда пользователь нажимает кнопку, SPA отправляет запрос REST API, который я размещаю на AWS API Gateway. Я передаю id_token в заголовке Authorization: Bearer <id_token>. Шлюз API получает заголовок, как и предполагалось, и теперь должен определить, хорош ли данный токен или нет, чтобы разрешить или запретить доступ.

Обычно идентификатор токена не используется "в заголовке Authorization: Bearer <id_token>" для отправки третьему лицу с целью авторизации. YMMV.