Python SDK
axowl-sdk is the backend SDK for Python services: it verifies the access token an end user
presents to your API and tells you who they are and what they may do. It mirrors
@axowl/sdk-backend (Node) and Axowl.Sdk.Identity.Client (.NET).
Python 3.9+, one dependency (PyJWT[crypto]).
Install
Section titled “Install”pip install axowl-sdk # + "axowl-sdk[fastapi]" for the FastAPI dependencyVerify a token — no server round trip
Section titled “Verify a token — no server round trip”from axowl import AxowlConfig, verify_token, has_permission, AxowlAuthError
config = AxowlConfig( org_slug="my-org", # your Axowl organization audience="app_my_main", # optional: your application key — rejects tokens minted for another app base_url="https://api.axowl.com", # default)
try: ctx = verify_token(bearer_token, config)except AxowlAuthError as e: ... # e.reason: missing_token | invalid_token | expired | signature | issuer | audience | jwks
ctx.user_id, ctx.email, ctx.org_slug, ctx.connected_id, ctx.is_employeectx.permissions # ["wallet.read", "report.*"]has_permission(ctx.permissions, "report.monthly") # True — wildcards honouredWhat is checked, and against what:
| Check | Source |
|---|---|
| Signature (RS256) | {base_url}/api/public/orgs/{org_slug}/.well-known/jwks.json — cached 10 min, re-fetched on an unknown kid |
iss | must equal {base_url}/api/public/orgs/{org_slug} — what Axowl writes into every end-user token |
aud | only when audience is configured; then it must equal your application key |
exp / nbf | enforced; leeway_seconds on the config absorbs clock drift |
The returned AxowlContext carries every claim (ctx.token) plus typed fields: user_id (sub),
email, org_id, org_slug, app_key, app_group_id, connected_id (the user’s
org badge), type ("enduser"), is_employee, and permissions —
the token stores permissions as a JSON string; the SDK hands you a list.
FastAPI
Section titled “FastAPI”from fastapi import Depends, FastAPIfrom axowl import AxowlConfig, AxowlContextfrom axowl.fastapi import AxowlAuth
auth = AxowlAuth(AxowlConfig(org_slug="my-org", audience="app_my_main"))app = FastAPI()
@app.get("/wallet")def wallet(ctx: AxowlContext = Depends(auth)): return {"user": ctx.email}
@app.post("/wallet/withdraw")def withdraw(ctx: AxowlContext = Depends(auth.require("wallet.withdraw"))): ...A missing or invalid token answers 401 {"error": …, "reason": …}; a missing scope answers
403 {"error": …, "required": [...]} — the same shapes as the Express middleware. Any other
framework: extract_bearer_token(request.headers["Authorization"]) then verify_token.
Server-authoritative checks
Section titled “Server-authoritative checks”The JWT fast path cannot see a permission revoked after the token was issued. For decisions that
must, ask Axowl with your org API key (ah_live_…, from Organization → API keys):
from axowl import AxowlIdentityClient
identity = AxowlIdentityClient(api_key="ah_live_...", base_url="https://api.axowl.com")
res = identity.introspect(bearer_token)# IntrospectResult(active, principal(end_user_id, organization_id, connected_id, is_employee,# email, display_name, application_key, permissions), expires_at, issued_at)
chk = identity.check_permission(bearer_token, "wallet.withdraw")# PermissionCheckResult(granted, matched_scopes, reason)These call POST /v1/identity/introspect and POST /v1/identity/check-permission — the REST
form of the gRPC IdentityService described on the Identity SDK page. Both are
synchronous and stdlib-only; run them in a thread from async code.
Permission matching
Section titled “Permission matching”Identical to every other Axowl SDK and to the server:
| Pattern | Scope | Match |
|---|---|---|
sap.fi.document.post | sap.fi.document.post | yes |
sap.fi.* | sap.fi.document.post | yes |
* | anything | yes |
sap.fi | sap.fi.document.post | no — a prefix without * is not a wildcard |
has_permission, has_all_permissions, has_any_permission and match_scope are exported for
use outside a request context.