Skip to content

Integrating with Python Flask API

This guide explains how to implement JWT authentication in your Flask API using AuthAction’s JWKS (JSON Web Key Set) endpoint.

Example Repository: For a complete working example, check out our example repository.

Before you begin, ensure you have:

  1. Python 3.11+: Download from python.org
  2. AuthAction Account: You’ll need your AuthAction tenant domain and API identifier
Terminal window
pip install flask python-jose[cryptography] httpx python-dotenv

Create a .env file in your project root:

Terminal window
AUTHACTION_DOMAIN=your-authaction-tenant-domain
AUTHACTION_AUDIENCE=your-authaction-api-identifier

Create auth/jwt_validator.py:

import os
import httpx
from functools import wraps
from flask import request, jsonify
from jose import JWTError, jwt
from jose.exceptions import ExpiredSignatureError
AUTHACTION_DOMAIN = os.getenv("AUTHACTION_DOMAIN")
AUTHACTION_AUDIENCE = os.getenv("AUTHACTION_AUDIENCE")
_jwks_cache: dict | None = None
def _get_jwks() -> dict:
global _jwks_cache
if _jwks_cache is None:
jwks_uri = f"https://{AUTHACTION_DOMAIN}/.well-known/jwks.json"
response = httpx.get(jwks_uri)
response.raise_for_status()
_jwks_cache = response.json()
return _jwks_cache
def _find_rsa_key(token: str) -> dict:
jwks = _get_jwks()
unverified_header = jwt.get_unverified_header(token)
kid = unverified_header.get("kid")
for key in jwks.get("keys", []):
if key.get("kid") == kid:
return {"kty": key["kty"], "kid": key["kid"],
"use": key["use"], "n": key["n"], "e": key["e"]}
# Key not found — could be rotation; bust cache and retry once
global _jwks_cache
_jwks_cache = None
jwks = _get_jwks()
for key in jwks.get("keys", []):
if key.get("kid") == kid:
return {"kty": key["kty"], "kid": key["kid"],
"use": key["use"], "n": key["n"], "e": key["e"]}
raise ValueError("Unable to find matching public key")
def verify_token(token: str) -> dict:
rsa_key = _find_rsa_key(token)
return jwt.decode(
token, rsa_key,
algorithms=["RS256"],
audience=AUTHACTION_AUDIENCE,
issuer=f"https://{AUTHACTION_DOMAIN}",
)
def require_auth(f):
"""Decorator that validates the Bearer JWT before calling the route handler."""
@wraps(f)
def decorated(*args, **kwargs):
auth_header = request.headers.get("Authorization", "")
if not auth_header.startswith("Bearer "):
return jsonify({"error": "Missing or invalid Authorization header"}), 401
token = auth_header.split(" ", 1)[1].strip()
try:
payload = verify_token(token)
except ExpiredSignatureError:
return jsonify({"error": "Token has expired"}), 401
except (JWTError, ValueError) as exc:
return jsonify({"error": str(exc)}), 401
request.current_payload = payload
return f(*args, **kwargs)
return decorated

Create app.py:

from dotenv import load_dotenv
load_dotenv()
from flask import Flask, jsonify, request
from auth.jwt_validator import require_auth
app = Flask(__name__)
@app.get("/public")
def public_route():
return jsonify({"message": "This is a public message!"})
@app.get("/protected")
@require_auth
def protected_route():
return jsonify({
"message": "This is a protected message!",
"sub": request.current_payload.get("sub"),
})
if __name__ == "__main__":
app.run(port=5000)
Terminal window
python app.py

The API will be available at http://localhost:5000.

  1. Obtain an Access Token:
Terminal window
curl --request POST \
--url https://your-authaction-tenant-domain/oauth2/m2m/token \
--header 'content-type: application/json' \
--data '{
"client_id": "your-authaction-m2m-app-clientid",
"client_secret": "your-authaction-m2m-app-client-secret",
"audience": "your-authaction-api-identifier",
"grant_type": "client_credentials"
}'
  1. Call the Public Endpoint:
Terminal window
curl http://localhost:5000/public
  1. Call the Protected Endpoint:
Terminal window
curl --request GET \
--url http://localhost:5000/protected \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
  • Verify AUTHACTION_DOMAIN and AUTHACTION_AUDIENCE match your AuthAction dashboard exactly
  • Ensure the token is signed with the RS256 algorithm
  • Verify your application can reach https://your-authaction-tenant-domain/.well-known/jwks.json
  • Ensure the Authorization: Bearer <token> header is present and the token is not expired
  • Confirm the token’s audience matches your API identifier