Verifying JWTs in Python
Validate EmbedAuth's RS256 tokens in Python using PyJWT and its JWKS client, with a reusable verifier and ready-to-use Flask and Django examples.
This guide verifies EmbedAuth's RS256 tokens in Python with PyJWT, which ships a JWKS client that fetches and caches EmbedAuth's public keys for you.
Install
pip install "pyjwt[crypto]"
The [crypto] extra pulls in cryptography, which is required for RS256 (asymmetric) verification.
The verifier
import jwt
from jwt import PyJWKClient
CLIENT_ID = "YOUR_CLIENT_ID"
ISSUER = "https://embedauth.com"
JWKS_URL = f"{ISSUER}/.well-known/jwks.json?clientId={CLIENT_ID}"
# Create the client once at import time — it caches keys internally.
_jwks_client = PyJWKClient(JWKS_URL)
def verify_token(token: str) -> dict:
signing_key = _jwks_client.get_signing_key_from_jwt(token)
return jwt.decode(
token,
signing_key.key,
algorithms=["RS256"],
issuer=ISSUER,
audience=CLIENT_ID,
)
get_signing_key_from_jwt reads the token's kid header and returns the matching public key, so rotation is handled for you. jwt.decode then checks the signature, exp, iss, and aud in one call.
Why algorithms, issuer, and audience are mandatory
algorithms=["RS256"]— pinning the algorithm blocks the classic "alg confusion" attack where a token claimsalg: noneorHS256.issuer— rejects tokens not minted by EmbedAuth.audience— rejects an otherwise-valid EmbedAuth token issued for a different client. Do not omit it.
If any check fails, jwt.decode raises a subclass of jwt.InvalidTokenError. Treat every such exception as a hard 401.
Flask
from functools import wraps
from flask import Flask, request, jsonify, g
app = Flask(__name__)
def require_auth(view):
@wraps(view)
def wrapped(*args, **kwargs):
header = request.headers.get("Authorization", "")
if not header.startswith("Bearer "):
return jsonify(error="missing_token"), 401
try:
g.user = verify_token(header[7:])
except jwt.InvalidTokenError:
return jsonify(error="invalid_token"), 401
return view(*args, **kwargs)
return wrapped
@app.get("/api/me")
@require_auth
def me():
return jsonify(id=g.user["sub"], email=g.user["email"])
Django
A lightweight DRF authentication class:
from rest_framework import authentication, exceptions
import jwt
class EmbedAuthAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
header = request.META.get("HTTP_AUTHORIZATION", "")
if not header.startswith("Bearer "):
return None # let other authenticators try
try:
payload = verify_token(header[7:])
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailed("invalid_token")
user, _ = User.objects.get_or_create(
username=payload["sub"],
defaults={"email": payload.get("email", "")},
)
return (user, payload)
Keying get_or_create on payload["sub"] maps the stable EmbedAuth user ID to your own user row on first sign-in.
Establishing your own session
Verification proves identity for one request. For anything longer, issue your own session — a Django session, a signed cookie, or your API's own token — keyed on sub, so your session lifetime and revocation stay independent of EmbedAuth's token expiry.
Next steps
- The frontend that produces the token: React, Vue.
- Other backends: Node.js, Go.
- Common failures: Troubleshooting.