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.
Prerequisites
Section titled “Prerequisites”Before you begin, ensure you have:
- Python 3.11+: Download from python.org
- AuthAction Account: You’ll need your AuthAction tenant domain and API identifier
Configuration
Section titled “Configuration”1. Install Required Packages
Section titled “1. Install Required Packages”pip install flask python-jose[cryptography] httpx python-dotenv2. Configure AuthAction Settings
Section titled “2. Configure AuthAction Settings”Create a .env file in your project root:
AUTHACTION_DOMAIN=your-authaction-tenant-domainAUTHACTION_AUDIENCE=your-authaction-api-identifier3. Implement JWT Authentication
Section titled “3. Implement JWT Authentication”Create auth/jwt_validator.py:
import osimport httpxfrom functools import wrapsfrom flask import request, jsonifyfrom jose import JWTError, jwtfrom 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 decorated1. Create Your Application
Section titled “1. Create Your Application”Create app.py:
from dotenv import load_dotenvload_dotenv()
from flask import Flask, jsonify, requestfrom 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_authdef protected_route(): return jsonify({ "message": "This is a protected message!", "sub": request.current_payload.get("sub"), })
if __name__ == "__main__": app.run(port=5000)2. Run the Server
Section titled “2. Run the Server”python app.pyThe API will be available at http://localhost:5000.
3. Testing the API
Section titled “3. Testing the API”- Obtain an Access Token:
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" }'- Call the Public Endpoint:
curl http://localhost:5000/public- Call the Protected Endpoint:
curl --request GET \ --url http://localhost:5000/protected \ --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'Common Issues
Section titled “Common Issues”Invalid Token Errors
Section titled “Invalid Token Errors”- Verify
AUTHACTION_DOMAINandAUTHACTION_AUDIENCEmatch your AuthAction dashboard exactly - Ensure the token is signed with the RS256 algorithm
Public Key Fetching Errors
Section titled “Public Key Fetching Errors”- Verify your application can reach
https://your-authaction-tenant-domain/.well-known/jwks.json
Unauthorized Access
Section titled “Unauthorized Access”- Ensure the
Authorization: Bearer <token>header is present and the token is not expired - Confirm the token’s audience matches your API identifier