joserfc JWT signing + JWE encryption round-trip: working versions (tested 2026-10-18)
Sign and verify JWTs and encrypt JWE tokens with joserfc using OctKey and RSAKey, including claims validation and expiry, all offline.
passed6 passed · 0 failed2026-10-18 03:57 UTC- Status
- passed
- Last passed
- 2026-10-18 03:57 UTC
- Tests
- 6 / 6
- Language
- Python
- Runtime
- Python 3.13.16
pinned versions2
joserfc@1.7.5pytest@9.1.1
run it
uv venv -q .venv && uv pip install -q --python .venv/bin/python -r requirements.txt .venv/bin/python -m pytest -q
test environment
Python 3.13.16 linux 6.17.0-1022-azure x64 runner: github-actions 39095370682
The code
Exactly the files that passed. Your agent can pull them with get_recipe("joserfc-jwt-jwe-roundtrip") (install), or see them on GitHub.
requirements.txt
joserfc==1.7.5 pytest==9.1.1
tokens.py
import datetime as dt
from joserfc import jwt
from joserfc.jwe import JWERegistry
from joserfc.jwk import OctKey, RSAKey
from joserfc.jwt import JWTClaimsRegistry
ISSUER = "https://issuer.example"
JWE_REGISTRY = JWERegistry()
def now() -> int:
return int(dt.datetime.now(dt.UTC).timestamp())
def issue_hs256(claims: dict, secret: str) -> str:
return jwt.encode({"alg": "HS256"}, claims, OctKey.import_key(secret))
def read_hs256(token: str, secret: str) -> dict:
return jwt.decode(token, OctKey.import_key(secret)).claims
def validate(claims: dict, issuer: str = ISSUER) -> None:
JWTClaimsRegistry(
iss={"essential": True, "value": issuer},
exp={"essential": True},
).validate(claims)
def rsa_pair(size: int = 2048) -> tuple[RSAKey, RSAKey]:
private = RSAKey.generate_key(size)
public = RSAKey.import_key(private.as_dict(private=False))
return private, public
def sign_rs256(claims: dict, private: RSAKey) -> str:
return jwt.encode({"alg": "RS256"}, claims, private)
def verify_rs256(token: str, public: RSAKey) -> dict:
return jwt.decode(token, public).claims
def encrypt_rsa_oaep(claims: dict, public: RSAKey) -> str:
header = {"alg": "RSA-OAEP", "enc": "A256GCM"}
return jwt.encode(header, claims, public, registry=JWE_REGISTRY)
def decrypt_rsa_oaep(token: str, private: RSAKey) -> dict:
return jwt.decode(token, private, registry=JWE_REGISTRY).claims
tests/test_tokens.py
import pytest
from joserfc.errors import BadSignatureError, ExpiredTokenError, UnsupportedKeyOperationError
from tokens import (
ISSUER,
decrypt_rsa_oaep,
encrypt_rsa_oaep,
issue_hs256,
now,
read_hs256,
rsa_pair,
sign_rs256,
validate,
verify_rs256,
)
SECRET = "a-32-byte-secret-used-only-in-tests!!"
@pytest.fixture(scope="module")
def rsa_keys():
return rsa_pair()
def test_hs256_roundtrip_and_claims():
token = issue_hs256({"iss": ISSUER, "sub": "u1", "exp": now() + 60}, SECRET)
claims = read_hs256(token, SECRET)
assert claims["sub"] == "u1" and claims["iss"] == ISSUER
validate(claims)
def test_expired_token_is_rejected_by_registry():
token = issue_hs256({"iss": ISSUER, "exp": now() - 1}, SECRET)
claims = read_hs256(token, SECRET)
with pytest.raises(ExpiredTokenError):
validate(claims)
def test_wrong_secret_fails_signature():
token = issue_hs256({"iss": ISSUER}, SECRET)
with pytest.raises(BadSignatureError):
read_hs256(token, SECRET + "x")
def test_rs256_verifies_with_public_key(rsa_keys):
private, public = rsa_keys
token = sign_rs256({"iss": ISSUER, "sub": "u1"}, private)
assert verify_rs256(token, public)["sub"] == "u1"
def test_rsa_oaep_jwe_roundtrip(rsa_keys):
private, public = rsa_keys
token = encrypt_rsa_oaep({"iss": ISSUER, "exp": now() + 60}, public)
assert token.count(".") == 4
claims = decrypt_rsa_oaep(token, private)
validate(claims)
def test_jwe_cannot_be_decrypted_with_public_key(rsa_keys):
_, public = rsa_keys
token = encrypt_rsa_oaep({"iss": ISSUER}, public)
with pytest.raises(UnsupportedKeyOperationError):
decrypt_rsa_oaep(token, public)
pytest.ini
[pytest] pythonpath = .
Connect to ProvenPin
Add the free MCP server so your agent can pull this recipe and others like it. Same setup as the docs.
terminal
claude mcp add --transport http provenpin https://mcp.provenpin.com/mcp
~/.cursor/mcp.json
{ "mcpServers": { "provenpin": { "url": "https://mcp.provenpin.com/mcp" } } }.vscode/mcp.json
{ "servers": { "provenpin": { "type": "http", "url": "https://mcp.provenpin.com/mcp" } } }~/.codeium/windsurf/mcp_config.json
{ "mcpServers": { "provenpin": { "serverUrl": "https://mcp.provenpin.com/mcp" } } }MCP settings
{ "mcpServers": { "provenpin": { "type": "streamableHttp", "url": "https://mcp.provenpin.com/mcp" } } }Any other MCP client: Streamable HTTP at https://mcp.provenpin.com/mcp. No API key, no signup.