Migrating from PyJWT

When migrating from PyJWT to joserfc, there are a few key differences to be aware of. joserfc provides implementations for JWS (JSON Web Signature), JWE (JSON Web Encryption), JWK (JSON Web Key), and JWT (JSON Web Token), whereas PyJWT focuses primarily on JWS and JWT. Additionally, joserfc supports both JWT on JWS and JWT on JWE, offering more flexibility for token handling.

jwt.encode

Both PyJWT and joserfc use the .encode method to generate a JWT, but the parameter structure differs between the two libraries.

PyJWT
import jwt
# jwt.encode(payload, key, algorithm)
encoded_jwt = jwt.encode({"some": "payload"}, "secret", algorithm="HS256")
joserfc
from joserfc import jwt
# jwt.encode(header, payload, key)
encoded_jwt = jwt.encode({"alg": "HS256"}, {"some": "payload"}, "secret")

jwt.decode

Similarly, both PyJWT and joserfc use the .decode method to verify and decode a JWT, but the parameter structure differs.

PyJWT
token = jwt.decode(encoded_jwt, "secret", algorithms=["HS256"])
# => {"some": "payload"}
joserfc
token = jwt.decode(encoded_jwt, "secret")
# => token.header : {"alg": "HS256"}
# => token.claims : {"some": "payload"}

Non-plain string key

When using a non-plain string key (equivalent to an “oct” key) in joserfc, such as RSA, EC, or OKP keys, the library provides built-in implementations to handle these key types. This eliminates the need for manual key handling, which is required in PyJWT.

Let’s take an example using an RSA key:

PyJWT
import jwt
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.backends import default_backend
private_pem = b"-----BEGIN PRIVATE KEY-----\nMIGEAgEAMBAGByqGSM49AgEGBS..."
private_key = serialization.load_pem_private_key(
    private_pem, password=None, backend=default_backend()
)
encoded_jwt = jwt.encode({"some": "payload"}, private_key, algorithm="RS256")
joserfc
from joserfc.jwk import RSAKey
from joserfc import jwt

private_pem = b"-----BEGIN PRIVATE KEY-----\nMIGEAgEAMBAGByqGSM49AgEGBS..."

# Import the RSA key using joserfc's RSAKey
key = RSAKey.import_key(private_pem)

header = {'alg': 'RS256'}
payload = {'some': 'payload'}
encoded = jwt.encode(header, payload, key)

Claims validation

Both PyJWT and joserfc provide mechanisms for claims validation, although they differ in their approach.

In PyJWT, claims validation is performed within the .decode method itself. When decoding a token, you can specify options such as verify_exp to validate the expiration time, verify_aud to validate the audience, and other options for additional claim validations. Claims validation is an integral part of the decoding process.

On the other hand, joserfc follows a different approach by separating the decoding and claims validation steps. The .decode method in joserfc is focused solely on decoding the token and extracting the header and payload information. Claims validation is performed separately using claims validators.

Verify “exp”

PyJWT
import jwt
jwt.decode(encoded_jwt, options={"verify_exp": True})
joserfc
from joserfc import jwt

# claims requests has built-in validators for exp, nbf, iat
claims_requests = jwt.JWTClaimsRegistry()
token = jwt.decode(encoded_jwt, key)
claims_requests.validate(token.claims)

Required claims

PyJWT
import jwt
jwt.decode(encoded_jwt, options={"require": ["exp", "iss", "sub"]})
joserfc
from joserfc import jwt

claims_requests = jwt.JWTClaimsRegistry(
    exp={"essential": True},
    iss={"essential": True},
    sub={"essential": True},
)
token = jwt.decode(encoded_jwt, key)
claims_requests.validate(token.claims)

The JWTClaimsRegistry accepts each claim as an Individual Claims Requests JSON object. You can learn more from Validate claims.