Backend & Token Verification

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.

1 min readUpdated July 5, 2026

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 claims alg: none or HS256.
  • 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