3 min read

O que é JSON Web Token (JWT)?

O que é JSON Web Token (JWT)?

JWT

Este é um padrão RFC 7519 que define uma maneira compacta e independente de passar informações entre aplicações por meio de uma representação JSON. As informações podem ser verificadas e validadas porque são assinadas com uma chave privada chamada "secret". O JWT é comumente usado para autenticação e autorização entre aplicativos da web.

Podemos dividir o JWT em três partes: header, payload e signature, abaixo está um exemplo de um JWT.

Exemplo de JWT

Contém informações específicas sobre o algoritmo usado para criar assinaturas.
Os algoritmos mais usados são HS256 (HMAC com SHA-256) ou RS256 (RSA Signature com SHA-256), segue o exemplo de header.

{
  "alg": "HS256",
  "typ": "JWT"
}

Payload

Contém as famosas "claims", toda informação no payload são consideradas "claims", que podem definir o comportamento e as permissões deste token, podemos ter claims adicionais, por exemplo perfil, abaixo temos o payload.

{
  "sub": "47A38F62-9357-58CF-04B7-36CE0C9CA6DF",
  "iss": "arn:will",
  "aud": "arrn:will",
  "name": "Will",
  "nbf": 1516239022,
  "iat": 1516239022,
  "exp": 7258129200,
  "user-role": "regular"
}
  • sub o assunto que o token se refere, neste caso o usuário;
  • iss indica o emissor ou gerador do token;
  • aud define o destino do JWT;
  • name o nome em relação ao assunto, sendo assim usuário;
  • iat define a data de criação do token no formato unix;
  • exp define a data de expiração do token no formato unix;
  • nbf define a data de inicio da validade do token;
  • user-role é uma claim que foi definida só para mostrar que podemos carregar qualquer informação dentro do JWT.

Você pode colocar qualquer informação dentro do JWT, mas observe que o token jwt é convertido para Base64 e não é criptografado 👌, essas informações devem ser públicas. Portanto, não guarde a senha do usuário, qualquer informação manipulável por uma requisição HTTP ou qualquer outro protocolo. Vamos evitar essa vulnerabilidade. É meu amigo lembre-se...

Signature

A assinatura é criada usando uma chave privada ou privada com possíveis algoritmos HS256 (HMAC com SHA-256) ou RS256 (RSA com SHA-256).

Exemplo de utilização

Abaixo temos um script que gera um JWT e executa o processo de validação.

Implementação

Primeiro vamos instalar pacotes Python necessários.

pip install pyjwt pytest

A implementação a seguir usa a biblioteca PyJWT para testes relacionados ao token JWT.

import pytest
import jwt
from datetime import datetime

EXPIRED_DATE = 1000000000  # September 9, 2001 1:46:40
FREEZE_DATE = datetime(2200, 1, 1, 0, 0, 0).timestamp()
ALGORITHM = "HS256"
DEFAULT_AUDIENCE = "urn:willsenadev"
JWT_SECRET = "jwt_secret"
DEFAULT_PAYLOAD = {
    "user-role": "regular",
    "issuer": "some-api",
    "aud": DEFAULT_AUDIENCE,
}


def generate_token(payload, secret):
    return jwt.encode(payload, secret, algorithm=ALGORITHM)


def validate_and_decode_token(token, secret):
    return jwt.decode(token, secret, audience=DEFAULT_AUDIENCE, algorithms=[ALGORITHM])


def decode_token(token):
    return jwt.decode(
        token, algorithms=[ALGORITHM], options={"verify_signature": False}
    )

Testes

Para não perder bons hábitos de teste, abaixo temos testes seguindo o padrão pytest.


def test_generate_jwt():
    token = generate_token(payload={"user-role": "admin"}, secret=JWT_SECRET)

    assert token.startswith("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9")


def test_decode_jwt_without_validate():
    token = generate_token(payload=DEFAULT_PAYLOAD, secret=JWT_SECRET)
    payload = decode_token(token=token)

    assert payload["user-role"] == "regular"


def test_decode_jwt():
    token = generate_token(payload=DEFAULT_PAYLOAD, secret=JWT_SECRET)
    payload = validate_and_decode_token(token=token, secret=JWT_SECRET)

    assert payload["user-role"] == "regular"


def test_decode_jwt_wrong_secret():
    token = generate_token(payload=DEFAULT_PAYLOAD, secret=JWT_SECRET)

    with pytest.raises(jwt.exceptions.InvalidSignatureError):
        validate_and_decode_token(token=token, secret="wrong-secret")


def test_decode_jwt_expired():
    token = generate_token(
        payload=DEFAULT_PAYLOAD | {"exp": EXPIRED_DATE}, secret=JWT_SECRET
    )

    with pytest.raises(jwt.exceptions.ExpiredSignatureError):
        validate_and_decode_token(token=token, secret=JWT_SECRET)


def test_decode_jwt_not_accepted_yet():
    token = generate_token(
        payload=DEFAULT_PAYLOAD | {"nbf": FREEZE_DATE}, secret=JWT_SECRET
    )

    with pytest.raises(jwt.exceptions.ImmatureSignatureError):
        validate_and_decode_token(token=token, secret=JWT_SECRET)

Você deve colocar tudo em um arquivo, nomeando como jwt_example.py e validar sua implementação executando o seguinte comando:

pytest -rA jwt_example.py      
> PASSED jwt_example.py::test_generate_jwt
> PASSED jwt_example.py::test_decode_jwt_without_validate
> PASSED jwt_example.py::test_decode_jwt
> PASSED jwt_example.py::test_decode_jwt_wrong_secret
> PASSED jwt_example.py::test_decode_jwt_expired
> PASSED jwt_example.py::test_decode_jwt_not_accepted_yet

Mais referências

Se você planeja usar JWT em sua API, é uma boa ideia verificar se o framework escolhido possui uma implementação no core ou adicional, exemplos de implementações para Flask e Django.

E isso é tudo!

Neste artigo falamos um pouco sobre JSON Web Tokens (JWT) tornando esse mecanismo reconhecido pelo nosso Kernel 🧠. Se você gostou do conteúdo comente e compartilhe, é rápido e ajuda muito.

Deus abençoe seu dia 🙏🏿, até a próxima.