Декодирование jwt с использованием открытого ключа в python

Введение:

JSON Web Token (JWT) — это компактное, безопасное для URL-адресов средство представления требований, которые должны быть переданы между двумя сторонами. Утверждения в JWT кодируются как объект JSON, который используется в качестве полезной нагрузки структуры веб-подписи JSON
(JWS) или в качестве открытого текста структуры веб-шифрования JSON (JWE), что позволяет преобразовывать утверждения в цифровом виде. подписано или защищено целостностью с помощью кода аутентификации сообщения (MAC) и/или зашифровано.

https://www.rfc-editor.org/rfc/rfc7519

В простом тексте JWT или JSON Web Token — это открытый стандарт, используемый для безопасного обмена информацией между двумя сторонами — клиентом и сервером. В большинстве случаев это закодированный JSON, содержащий набор утверждений и подпись.

Python предоставляет несколько библиотек для кодирования и декодирования веб-токенов JSON. Назвать несколько,

PyJWT:

Для нашего обсуждения мы будем использовать PyJWT в качестве библиотеки.

Кодировать:

import jwt


def encode_user():
    """
    encode user payload as a jwt
    :param user:
    :return:
    """
    encoded_data = jwt.encode(payload={"name": "Dinesh"},
                              key='secret',
                              algorithm="HS256")

    return encoded_data


if __name__ == "__main__":
    print(encode_user())

Выход:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiRGluZXNoIn0.7Fwj-RvoEP2-LfB5q05pdTvMl7pFpoQgwXYq3EOLens


Process finished with exit code 0

Приведенный выше код говорит сам за себя. У нас есть полезная нагрузка в виде python dict, то есть данные для кодирования и секретный ключ, используемый для кодирования. Также мы использовали алгоритм HS256 для подписи токена. Алгоритмы, поддерживаемые PyJWT, представлены здесь.

Разбивка токена JWT:

Давайте попробуем понять структуру токена JWT. Токен JWT обычно содержит утверждения пользователя. Они представляют данные о пользователе, которые API может использовать для предоставления разрешений или отслеживания пользователя, предоставившего токен. Различные компоненты токена JWT отделяется точкой (.). Токен JWT состоит из 3 частей. Каждый раздел состоит из base64url-закодированного JSON, содержащего конкретную информацию для этого токена.

  1. Заголовок
  2. Полезная нагрузка
  3. Подпись

jwt.io также может использоваться для декодирования токена JWT и разбивает его на вышеупомянутые компоненты.

Расшифровать:

import jwt


def decode_user(token: str):
    """
    :param token: jwt token
    :return:
    """
    decoded_data = jwt.decode(jwt=token,
                              key='secret',
                              algorithms=["HS256"])

    print(decoded_data)

Выход:

/Users/dkb/VirtualEnvs/py3.11-env/bin/python3.11 /Users/dkb/Code/practice/my_jwt.py

{'name': 'Dinesh'}

Process finished with exit code 0

Декодирование JWT с использованием открытого ключа:

Открытый ключ можно использовать для декодирования JWT. Обычно эти открытые ключи должны предоставляться арендатору в указанном ниже формате URI. Каждый сервер с открытым идентификатором должен предоставлять эту конечную точку. В нашем случае открытый ключ называется веб-ключом JSON.

Веб-ключ JSON (JWK) — это объект JSON, содержащий общеизвестный открытый ключ, который можно использовать для проверки подписи подписанного JWT.

Если эмитент вашего JWT использовал асимметричный ключ для подписи JWT, он, скорее всего, будет размещать файл, называемый набором веб-ключей JSON (JWKS). JWKS — это объект JSON, содержащий свойство keys, которое, в свою очередь, содержит массив объектов JWK.

 https://--YOUR DOMAIN----/.well-known/jwks.json


Sample Response:

{
  keys: [
    {
      alg: 'RS256',
      kty: 'RSA',
      use: 'sig',
      n: 'tTMpnrc4dYlD8MtmPnW3xZNbLxkaGCUwTqeKB4dfLg11dEpMyQEc4JRxUvRzp9tz00r6lkZ1ixcvIiuB_eMVckU8VyFSFWBSAxp5duBk6lRpYk-QjK3kEdPxYLxyW84gNzwMi-XW8zxJbsOa-cRM9sCb62Qz2yfWoQfimoFXsCnVHq496kizO7gZ972JefvTce1_n9dd_1p0K6c14qcCXtF6hbA_gQ0N7h3IyloBqiusKyTsV-ZrMZDldZkI-4v7s49TdcRZgEOvSapMz5YyoDvAWzuWGEiljkjkCOo0Mr5Sioi2x0dBm6nJ2WVYfZrwEF5J',
      e: 'AQAB',
      kid: 'NTY2MjBCNzQ1RTLPQzk3NzczRRTMQ0E4NzE2MjcwOUFCRkUwRTUxNA',
      x5t: 'NTY2MjBCNzQ1RTJPLzk3NzczRUNPO0E4NzE2MjcwOUFCRkUwRTUxNA',
      x5c: [Array]
    }
  ]
}

Теперь давайте напишем код на Python для декодирования токена JWT с использованием python-jose.

import jwt
import httpx


def decode_access_token(authorisation_token):

    # get public key from jwks uri
    response = httpx.get(url="your open id wellknown url to fetch public key")

    # gives the set of jwks keys.the keys has to be passed as it is to jwt.decode() for signature verification.
    key = response.json()

    # get the algorithm type from the request header
    algorithm = jwt.get_unverified_header(authorisation_token).get('alg')

    user_info = jwt.decode(token=authorisation_token,
                           key=key,
                           algorithms=algorithm)

    return user_info

Я использовал здесь python-jose только для того, чтобы показать, что между этими библиотеками нет существенной разницы. Код действительно не отличается от PyJWT. Имеет ли это?

Краткое содержание:

Обратите внимание, что любой может расшифровать информацию, содержащуюся в JWT, не зная закрытых ключей. По этой причине никогда не следует помещать в JWT секретную информацию, такую ​​как пароли или криптографические ключи.

python-jose — это надстройка над PyJWT.

Примечание. Причина, по которой я упомянул обе библиотеки, заключается в том, что иногда ваш конвейер сборки, такой как gitlab/Jenkins, жалуется (без причины) на наличие разных/несовместимых версий криптографии с PyJWT. Однако использование python-jose в таких сценариях было бы быстрое решение без изменения кода. Я лично столкнулся с этой проблемой и хотел поделиться ею здесь.