OAuth2/OIDC authentication and authorization for Flask APIs. Supports authentication and claim-based fine-grained authorization (scopes, roles, permissions) using JWT tokens.
Project description
axioms-flask-py

OAuth2/OIDC authentication and authorization for Flask APIs. Supports authentication and claim-based fine-grained authorization (scopes, roles, permissions) using JWT tokens.
Works with access tokens issued by various authorization servers including AWS Cognito, Auth0, Okta, Microsoft Entra, etc.
Using FastAPI or Django REST Framework? This package is specifically for Flask. For FastAPI applications, use axioms-fastapi. For DRF applications, use axioms-drf-py.
Features
- JWT token validation with automatic public key retrieval from JWKS endpoints
- Algorithm validation to prevent algorithm confusion attacks (only secure asymmetric algorithms allowed)
- Issuer validation (
issclaim) to prevent token substitution attacks - Authorization decorators for standard claims: scopes, roles, and permissions
- Flexible configuration with support for custom JWKS and issuer URLs
- Simple integration with Flask based Resource Server or API backends
- Support for custom claim and/or namespaced claims names to support different authorization servers
Prerequisite
- Python 3.7+
- An OAuth2/OIDC client which can obtain access token after user's authentication and authorization and include obtained access token as bearer in
Authorizationheader of all API request sent to Python/Flask application server.
Install SDK
Install axioms-flask-py in you Flask API project,
pip install axioms-flask-py
Documentation
Prerequisite
- Python 3.7+
- An Axioms client which can obtain access token after user's authentication and authorization and include obtained access token as bearer in
Authorizationheader of all API request sent to Python/Flask application server.
Install SDK
Install axioms-flask-py in you Flask API project,
pip install axioms-flask-py
Add environment variables
Create a .env file and add following configs (see .env.example for reference)
Option 1: Using AXIOMS_DOMAIN (Recommended - simplest configuration)
AXIOMS_AUDIENCE=<your-axioms-resource-identifier-or-endpoint>
AXIOMS_DOMAIN=<your-axioms-slug>.axioms.io
Option 2: Custom issuer URL (when your issuer includes a path)
AXIOMS_AUDIENCE=<your-axioms-resource-identifier-or-endpoint>
AXIOMS_ISS_URL=https://my-auth.domain.com/oauth2
Option 3: Explicit JWKS URL (for non-standard JWKS endpoints)
AXIOMS_AUDIENCE=<your-axioms-resource-identifier-or-endpoint>
AXIOMS_JWKS_URL=https://my-auth.domain.com/.well-known/jwks.json
For more information see API documentation.
Load environment variables
In your Flask app file (where flask app is declared) add following.
from flask_dotenv import DotEnv
env = DotEnv(app)
Register Error Handler
In your Flask app file (where flask app is declared), register the error handler.
from axioms_flask.error import register_axioms_error_handler
register_axioms_error_handler(app)
Guard Your Flask API Views
Use following decorators to guard you API views.
| Decorators | Description | Parameter |
|---|---|---|
axioms_flask.decorators.has_valid_access_token |
Checks if API request includes a valid bearer access token as authorization header. Check performed includes: token signature validation, expiry datetime validation, token audience validation, and issuer validation (if configured). Should be always the first decorator on the protected or private view. |
|
axioms_flask.decorators.has_required_scopes |
Check any of the given scopes included in scope claim of the access token. Should be after has_valid_access_token. |
An array of strings as conditional OR representing any of the allowed scope or scopes for the view as parameter. For instance, to check openid or profile pass ['profile', 'openid'] as parameter. |
axioms_flask.decorators.has_required_roles |
Check any of the given roles included in roles claim of the access token. Should be after has_valid_access_token. |
An array of strings as conditional OR representing any of the allowed role or roles for the view as parameter. For instance, to check sample:role1 or sample:role2 roles you will pass ['sample:role1', 'sample:role2'] as parameter. |
axioms_flask.decorators.has_required_permissions |
Check any of the given permissions included in permissions claim of the access token. Should be after has_valid_access_token. |
An array of strings as conditional OR representing any of the allowed permission or permissions for the view as parameter. For instance, to check sample:create or sample:update permissions you will pass ['sample:create', 'sample:update'] as parameter. |
OR vs AND Logic
By default, authorization decorators use OR logic - the token must have at least ONE of the specified claims. To require ALL claims (AND logic), chain multiple decorators.
OR Logic (Default) - Requires ANY of the specified claims:
@app.route('/api/resource')
@has_valid_access_token
@has_required_scopes(['read:resource', 'write:resource'])
def resource_route():
# User needs EITHER 'read:resource' OR 'write:resource' scope
return {'data': 'success'}
@app.route('/admin/users')
@has_valid_access_token
@has_required_roles(['admin', 'superuser'])
def admin_route():
# User needs EITHER 'admin' OR 'superuser' role
return {'users': []}
AND Logic (Chaining) - Requires ALL of the specified claims:
@app.route('/api/strict')
@has_valid_access_token
@has_required_scopes(['read:resource'])
@has_required_scopes(['write:resource'])
def strict_route():
# User needs BOTH 'read:resource' AND 'write:resource' scopes
return {'data': 'requires both scopes'}
@app.route('/admin/critical')
@has_valid_access_token
@has_required_roles(['admin'])
@has_required_roles(['superuser'])
def critical_route():
# User needs BOTH 'admin' AND 'superuser' roles
return {'message': 'requires both roles'}
Mixed Logic - Combine OR and AND by chaining:
@app.route('/api/advanced')
@has_valid_access_token
@has_required_scopes(['openid', 'profile']) # Needs openid OR profile
@has_required_roles(['editor']) # AND must have editor role
@has_required_permissions(['resource:read', 'resource:write']) # AND read OR write permission
def advanced_route():
# User needs: (openid OR profile) AND (editor) AND (read OR write)
return {'data': 'complex authorization'}
Examples
- Check
openidorprofilescope present in the token
from axioms_flask.decorators import has_valid_access_token, has_required_scopes
private_api = Blueprint("private_api", __name__)
@private_api.route('/private', methods=["GET"])
@has_valid_access_token
@has_required_scopes(['openid', 'profile'])
def api_private():
return jsonify({'message': 'All good. You are authenticated!'})
- Check
sample:rolerole present in the token
from axioms_flask.decorators import has_valid_access_token, has_required_roles
role_api = Blueprint("role_api", __name__)
@role_api.route("/role", methods=["GET", "POST", "PATCH", "DELETE"])
@has_valid_access_token
@has_required_roles(["sample:role"])
def sample_role():
if request.method == 'POST':
return jsonify({"message": "Sample created."})
if request.method == 'PATCH':
return jsonify({"message": "Sample updated."})
if request.method == 'GET':
return jsonify({"message": "Sample read."})
if request.method == 'DELETE':
return jsonify({"message": "Sample deleted."})
- Check permission present in the token at API method level
from axioms_flask.decorators import has_valid_access_token, has_required_permissions
permission_api = Blueprint("permission_api", __name__)
@permission_api.route("/permission", methods=["POST"])
@has_valid_access_token
@has_required_permissions(["sample:create"])
def sample_create():
return jsonify({"message": "Sample created."})
@permission_api.route("/permission", methods=["PATCH"])
@has_valid_access_token
@has_required_permissions(["sample:update"])
def sample_update():
return jsonify({"message": "Sample updated."})
@permission_api.route("/permission", methods=["GET"])
@has_valid_access_token
@has_required_permissions(["sample:read"])
def sample_read():
return jsonify({"message": "Sample read."})
@permission_api.route("/permission", methods=["DELETE"])
@has_valid_access_token
@has_required_permissions(["sample:delete"])
def sample_delete():
return jsonify({"message": "Sample deleted."})
Flask Sample
To see a complete working example download Flask sample from our Github repository or simply deploy to heroku by clicking following button. You will need to provide Axioms domain and Axioms audience to complete deployment.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file axioms_flask_py-0.0.19rc91762741672.tar.gz.
File metadata
- Download URL: axioms_flask_py-0.0.19rc91762741672.tar.gz
- Upload date:
- Size: 39.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bb324a7b5f8d3dc8f9fae96529a20b52d2fea6be3585793eb8492fcf8d019874
|
|
| MD5 |
691cd5200a379e1167815fa4f2a56ef2
|
|
| BLAKE2b-256 |
510c4f3fd44cc477c80d43bcc16767401931512810222f54d2c933421871f8ea
|
Provenance
The following attestation bundles were made for axioms_flask_py-0.0.19rc91762741672.tar.gz:
Publisher:
release.yml on abhishektiwari/axioms-flask-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
axioms_flask_py-0.0.19rc91762741672.tar.gz -
Subject digest:
bb324a7b5f8d3dc8f9fae96529a20b52d2fea6be3585793eb8492fcf8d019874 - Sigstore transparency entry: 686000855
- Sigstore integration time:
-
Permalink:
abhishektiwari/axioms-flask-py@34b56d636b38e4654e530787be62b50eff90c2c4 -
Branch / Tag:
refs/pull/9/merge - Owner: https://github.com/abhishektiwari
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@34b56d636b38e4654e530787be62b50eff90c2c4 -
Trigger Event:
pull_request
-
Statement type:
File details
Details for the file axioms_flask_py-0.0.19rc91762741672-py3-none-any.whl.
File metadata
- Download URL: axioms_flask_py-0.0.19rc91762741672-py3-none-any.whl
- Upload date:
- Size: 14.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d4f1e5f7e0c42815e00328b99d90a3053db6bd01277c87f1f48e98bb8f1cac6a
|
|
| MD5 |
2951c7ffcdbc942bab1e1db9c3935719
|
|
| BLAKE2b-256 |
324550510c6b830ea602e9b7955f03a542c1cbb78900f6a5d689f36c4e5c4fa9
|
Provenance
The following attestation bundles were made for axioms_flask_py-0.0.19rc91762741672-py3-none-any.whl:
Publisher:
release.yml on abhishektiwari/axioms-flask-py
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
axioms_flask_py-0.0.19rc91762741672-py3-none-any.whl -
Subject digest:
d4f1e5f7e0c42815e00328b99d90a3053db6bd01277c87f1f48e98bb8f1cac6a - Sigstore transparency entry: 686000857
- Sigstore integration time:
-
Permalink:
abhishektiwari/axioms-flask-py@34b56d636b38e4654e530787be62b50eff90c2c4 -
Branch / Tag:
refs/pull/9/merge - Owner: https://github.com/abhishektiwari
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@34b56d636b38e4654e530787be62b50eff90c2c4 -
Trigger Event:
pull_request
-
Statement type: