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.
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
Option 1: Using AXIOMS_DOMAIN (for Axioms or standard OAuth2/OIDC providers)
AXIOMS_DOMAIN=<your-axioms-slug>.axioms.io
AXIOMS_AUDIENCE=<your-axioms-resource-identifier-or-endpoint>
Option 2: Using AXIOMS_JWKS_URL (for custom JWKS endpoints)
AXIOMS_JWKS_URL=https://my-auth.domain.com/oauth2/.well-known/jwks.json
AXIOMS_AUDIENCE=<your-axioms-resource-identifier-or-endpoint>
Configuration Options:
AXIOMS_AUDIENCE(required): Your resource identifier or API audienceAXIOMS_JWKS_URL(optional): Full URL to your JWKS endpointAXIOMS_DOMAIN(optional): Your auth domain (e.g.,my-auth.domain.com)
Note: You must provide either AXIOMS_JWKS_URL or AXIOMS_DOMAIN.
Claims Handling:
- Roles are checked from
rolesclaim, orhttps://{AXIOMS_DOMAIN}/claims/rolesif using namespaced claims - Permissions are checked from
permissionsclaim, orhttps://{AXIOMS_DOMAIN}/claims/permissionsif using namespaced claims - Scopes are checked from the standard
scopeclaim
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
In your Flask app file (where flask app is declared) add following.
from flask import jsonify
from axioms_flask.error import AxiomsError
@app.errorhandler(AxiomsError)
def handle_auth_error(ex):
response = jsonify(ex.error)
response.status_code = ex.status_code
if ex.status_code == 401:
response.headers[
"WWW-Authenticate"
] = "Bearer realm='{}', error='{}', error_description='{}'".format(
app.config["AXIOMS_DOMAIN"], ex.error["error"], ex.error["error_description"]
)
return response
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, and token audience validation. 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.17rc71762559297.tar.gz.
File metadata
- Download URL: axioms_flask_py-0.0.17rc71762559297.tar.gz
- Upload date:
- Size: 29.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc6a126a772820af42a3f927a2b1690b3d660bc5d7d4db8d24069abdba9e48c6
|
|
| MD5 |
fc9a904747d88bb84d8197e9f64e4832
|
|
| BLAKE2b-256 |
ede87c3e5c0dd1c2b4a9191d49d3a9f5e512a88ecf8236f81db61c07ffe83231
|
Provenance
The following attestation bundles were made for axioms_flask_py-0.0.17rc71762559297.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.17rc71762559297.tar.gz -
Subject digest:
dc6a126a772820af42a3f927a2b1690b3d660bc5d7d4db8d24069abdba9e48c6 - Sigstore transparency entry: 684094162
- Sigstore integration time:
-
Permalink:
abhishektiwari/axioms-flask-py@2d45afc1ccfbc065d0cd4b7088ad135b151d3e7c -
Branch / Tag:
refs/pull/7/merge - Owner: https://github.com/abhishektiwari
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2d45afc1ccfbc065d0cd4b7088ad135b151d3e7c -
Trigger Event:
pull_request
-
Statement type:
File details
Details for the file axioms_flask_py-0.0.17rc71762559297-py3-none-any.whl.
File metadata
- Download URL: axioms_flask_py-0.0.17rc71762559297-py3-none-any.whl
- Upload date:
- Size: 12.1 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 |
6984fe2697ca0a3e0231219efb3705829f8506de61a98f645df14cc01cda6636
|
|
| MD5 |
4265981b5e7e6d4992690976381b44cf
|
|
| BLAKE2b-256 |
0d2073726935c26ddad80de2f7625fbfa2aaffa506424c0bce1e259f3e320672
|
Provenance
The following attestation bundles were made for axioms_flask_py-0.0.17rc71762559297-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.17rc71762559297-py3-none-any.whl -
Subject digest:
6984fe2697ca0a3e0231219efb3705829f8506de61a98f645df14cc01cda6636 - Sigstore transparency entry: 684094171
- Sigstore integration time:
-
Permalink:
abhishektiwari/axioms-flask-py@2d45afc1ccfbc065d0cd4b7088ad135b151d3e7c -
Branch / Tag:
refs/pull/7/merge - Owner: https://github.com/abhishektiwari
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2d45afc1ccfbc065d0cd4b7088ad135b151d3e7c -
Trigger Event:
pull_request
-
Statement type: