JWT Token#

A wrapper around PyJWT that simplifies encoding, decoding, and validation of JSON Web Tokens.

Quick Start#

from core_auth import JwtToken

client = JwtToken(private_key="S3cr3t")

# Encode
token = client.encode(subject="user-123")

# Decode
payload = client.decode(token)
print(payload["sub"])   # "user-123"
print(payload["exp"])   # expiry timestamp
print(payload["iat"])   # issued-at timestamp

Custom Claims & Expiry#

Any additional JWT claims can be passed via claims. The token lifetime is controlled by expire (seconds, default 3600).

from core_auth import JwtToken

client = JwtToken(private_key="S3cr3t", expire=7200)

token = client.encode(
    subject="user-123",
    claims={
        "iss": "my-auth-service",
        "aud": "my-api",
        "role": "admin",
    },
)

payload = client.decode(token, audience="my-api", issuer="my-auth-service")
print(payload["role"])  # "admin"

Decode Options#

Audience and issuer validation

from core_auth import JwtToken, JwtException

client = JwtToken(private_key="S3cr3t")
token = client.encode(subject="user-123", claims={"iss": "auth", "aud": "api"})

# Validates that iss == "auth" and aud == "api"
payload = client.decode(token, issuer="auth", audience="api")
print(payload)

# Raises JwtException if either claim does not match
try:
    client.decode(token, issuer="wrong")
except JwtException as exc:
    print(exc)  # "Invalid token."

Expiry leeway

Allow a small clock-skew grace period when validating exp:

from datetime import timedelta
from core_auth import JwtToken

client = JwtToken(private_key="S3cr3t")
token = client.encode(subject="user-123")
payload = client.decode(token, leeway=timedelta(seconds=30))

Skip signature verification

Useful for inspecting a token’s claims without validating the signature (e.g. in tests or debugging):

from core_auth import JwtToken

client = JwtToken(private_key="S3cr3t")
token = client.encode(subject="user-123")
payload = client.decode(token, options={"verify_signature": False})

Full decode — returns header, payload, and raw signature together:

from core_auth import JwtToken

client = JwtToken(private_key="S3cr3t")
token = client.encode(subject="user-123")
result = client.decode(token, full_decode=True)

print(result["header"])     # {'alg': 'HS256', 'typ': 'JWT'}
print(result["payload"])    # {'exp': ..., 'iat': ..., 'sub': 'user-123'}
print(result["signature"])  # b'...'

Custom Headers#

Additional JOSE header fields (e.g. kid for key ID rotation) can be injected via headers:

from core_auth import JwtToken

client = JwtToken(private_key="S3cr3t")
token = client.encode(subject="user-123", headers={"kid": "key-v2"})
result = client.decode(token, full_decode=True)
print(result["header"]["kid"])  # "key-v2"

Authorization Header Parsing#

Extract the Bearer token from an HTTP Authorization header:

from core_auth import JwtToken, JwtException

client = JwtToken(private_key="S3cr3t")

# Build a header from an encoded token
token       = client.encode(subject="user-123")
auth_header = f"Bearer {token}"

# Extract and decode
try:
    raw_token = JwtToken.from_auth_header(auth_header)
    payload = client.decode(raw_token)
    print(payload["sub"])  # "user-123"
except JwtException as exc:
    print(exc)

# Malformed header raises JwtException
try:
    JwtToken.from_auth_header("Token xyz")
except JwtException as exc:
    print(exc)  # "Bad format in Authorization header. Must be: Bearer <token>"

The header must follow the Bearer <token> format exactly; anything else raises JwtException.

Asymmetric Keys (RSA / ECDSA / EdDSA)#

For asymmetric algorithms pass both private_key (for signing) and public_key (for verification). Keys can be PEM strings, PEM bytes, or cryptography key objects.

RSA — RS256

from core_auth import JwtToken, ALGORITHM

private_pem = open("tests/resources/private.pem").read()
public_pem = open("tests/resources/public.pem").read()

client = JwtToken(private_key=private_pem, public_key=public_pem)

token = client.encode(subject="user-123", algorithm=ALGORITHM.RS256)
payload = client.decode(token, algorithms=[ALGORITHM.RS256])
print(payload["sub"])  # "user-123"

ECDSA — ES256

from cryptography.hazmat.primitives.asymmetric import ec
from core_auth import JwtToken, ALGORITHM

private_key = ec.generate_private_key(ec.SECP256R1())
client = JwtToken(private_key=private_key, public_key=private_key.public_key())

token = client.encode(subject="user-123", algorithm=ALGORITHM.ES256)
payload = client.decode(token, algorithms=[ALGORITHM.ES256])

EdDSA — Ed25519 / Ed448

from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from core_auth import JwtToken, ALGORITHM

private_key = Ed25519PrivateKey.generate()
client = JwtToken(private_key=private_key, public_key=private_key.public_key())

token = client.encode(subject="user-123", algorithm=ALGORITHM.EdDSA)
payload = client.decode(token, algorithms=[ALGORITHM.EdDSA])

Using cryptography key objects directly:

from cryptography.hazmat.primitives.asymmetric import rsa
from core_auth import JwtToken, ALGORITHM

private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
client      = JwtToken(private_key=private_key, public_key=private_key.public_key())

token = client.encode(subject="user-123", algorithm=ALGORITHM.RS256)
payload = client.decode(token, algorithms=[ALGORITHM.RS256])

Error Handling#

All errors raised by JwtToken are instances of JwtException, which is itself a subclass of PyJWTError:

from core_auth import JwtToken, JwtException

client = JwtToken(private_key="S3cr3t")

try:
    payload = client.decode("an.invalid.token")
except JwtException as exc:
    print(exc)  # "Invalid token."

# Expired token
expired_client = JwtToken(private_key="S3cr3t", expire=-1)
token = expired_client.encode(subject="user-123")

try:
    expired_client.decode(token)
except JwtException as exc:
    print(exc)  # "Signature expired."

API Reference#

JWT token wrapper module for authentication operations.

class core_auth.auth.jwt_token.jwt_auth.JwtToken(private_key: RSAPrivateKey | EllipticCurvePrivateKey | Ed25519PrivateKey | Ed448PrivateKey | PyJWK | bytes | str, public_key: RSAPublicKey | EllipticCurvePublicKey | Ed25519PublicKey | Ed448PublicKey | PyJWK | str | bytes | None = None, expire: int = 3600)[source]#

Bases: object

Wrapper around JWT tokens

__init__(private_key: RSAPrivateKey | EllipticCurvePrivateKey | Ed25519PrivateKey | Ed448PrivateKey | PyJWK | bytes | str, public_key: RSAPublicKey | EllipticCurvePublicKey | Ed25519PublicKey | Ed448PublicKey | PyJWK | str | bytes | None = None, expire: int = 3600) None[source]#
Parameters:
  • private_key – Secret key to create, encode and decode the tokens.

  • expire – Seconds until the token expires.

static from_auth_header(auth_header: str) str[source]#

It retrieves the token from the authentication headers

encode(subject: Any = None, algorithm: ALGORITHM | str = ALGORITHM.HS256, claims: Dict[str, Any] | None = None, headers: Dict | None = None, json_encoder: type[JSONEncoder] | None = None) str[source]#

Encode the payload as Json Web Token. More Info: https://pyjwt.readthedocs.io/en/stable/api.html#jwt.encode

Parameters:
Returns:

The Json Web Token.

decode(token: str, algorithms: List[ALGORITHM | str] | None = None, options: Dict | None = None, audience: List[str] | str | None = None, issuer: str | None = None, leeway: timedelta | float = 0, full_decode: bool = False) Dict[source]#

It decodes and verifies the JWT token signature and return the token claims. More Info: https://pyjwt.readthedocs.io/en/stable/api.html#jwt.decode

Parameters:
  • token – Token to decode.

  • algorithms – Algorithms to use like: HS256 or RS256.

  • options – Extended decoding and validation options. - verify_signature=True verify the JWT cryptographic signature. - require=[] claims that must be present. - verify_aud=verify_signature check that aud claim matches audience - verify_iss=verify_signature check that iss claim matches issuer - verify_exp=verify_signature check that exp claim is in the future - verify_iat=verify_signature check that iat claim is an integer - verify_nbf=verify_signature check that nbf claim is in the past - strict_aud=False check aud is a single value matching audience exactly

  • audience – The value for verify_aud check.

  • issuer – The value for verify_iss check.

  • leeway – A time margin in seconds for the expiration check

  • full_decode – If True, full_decode will be performed.

Returns:

The JWT claims or full payload.

Example:

# If full_decode.

{
    'payload': {
        'exp': ...,
        'iat': ...,
        'sub': '...',
        'iss': '...'
    },
    'header': {
        'alg': 'HS256',
        'typ': 'JWT'
    },
    'signature': b'...'
}

# Else.

{
    'exp': ...,
    'iat': ...,
    'sub': '...'
}

exception core_auth.auth.jwt_token.jwt_auth.JwtException[source]#

Bases: PyJWTError

JWT Exception

Algorithms#

JWT algorithm enumeration for cryptographic signing operations.

class core_auth.auth.jwt_token.algorithm.ALGORITHM(*values)[source]#

Bases: StrEnum

Supported algorithms for cryptographic signing. More info: https://pyjwt.readthedocs.io/en/stable/algorithms.html

HS256 = 'HS256'#
HS384 = 'HS384'#
HS512 = 'HS512'#
ES256 = 'ES256'#
ES256K = 'ES256K'#
ES384 = 'ES384'#
ES512 = 'ES512'#
RS256 = 'RS256'#
RS384 = 'RS384'#
RS512 = 'RS512'#
PS256 = 'PS256'#
PS384 = 'PS384'#
PS512 = 'PS512'#
EdDSA = 'EdDSA'#
static _generate_next_value_(name, start, count, last_values)#

Return the lower-cased version of the member name.