Skip to main content

FastAPI client library for the mcp-mesh auth platform (JWT + UMA permission aggregation).

Project description

mcpmesh-auth-lib

FastAPI client library for the mcp-mesh auth platform. Python equivalent of libs/auth-lib/ (the Spring Boot / Java auth-lib v2). Validates Keycloak-issued JWTs, aggregates UMA permissions across one-or-more resource-server audiences, and ships a Pydantic MeResponse model that matches the Java DTO byte-for-byte.

Install

# from a sibling repo / monorepo path
pip install -e /path/to/auth-manager/libs/auth-lib-python

# or via git URL (private use)
pip install "mcpmesh-auth-lib @ git+https://github.com/your-org/auth-manager@main#subdirectory=libs/auth-lib-python"

Requires Python 3.11+.

Environment variables

Var Required Default Notes
AUTH_LIB_ISSUER_URI yes e.g. https://auth.mcp-mesh.io/auth/realms/t-app1
AUTH_LIB_CLIENT_ID yes This app's OIDC client_id; used as default audience
AUTH_LIB_CLIENT_SECRET no None Confidential client secret (only used by callers that need extra KC calls; not used for UMA itself)
AUTH_LIB_AUDIENCES no [client_id] Comma-separated list. Extra resource servers to aggregate UMA permissions across.
AUTH_LIB_JWKS_CACHE_TTL_SECONDS no 3600 How long PyJWKClient caches JWKS keys
AUTH_LIB_PERMISSION_CACHE_TTL_SECONDS no 60 UMA permission cache TTL
AUTH_LIB_REDIS_URL no None e.g. redis://redis:6379/0. If set, permissions cache uses Redis.
AUTH_LIB_HTTP_TIMEOUT_SECONDS no 5.0 HTTP timeout for JWKS + UMA calls.
AUTH_LIB_PERMISSIONS_SOURCE no claims claims reads perms from JWT resource_access.<client>.roles; uma calls KC's UMA endpoint. See below.

AUTH_LIB_PERMISSIONS_SOURCE

Controls where atomic permissions come from when Permissions.all_for(token, claims) is called.

  • claims (default) — reads from the JWT's resource_access.<client>.roles claim for each configured audience (AUTH_LIB_AUDIENCES, falling back to AUTH_LIB_CLIENT_ID). This is the auth-manager pattern: atomic permissions are KC client roles on the backend client, composite realm roles bundle them, and KC's role expansion flattens them into the token at mint time. No round-trip to KC is needed — the perms are already in the token. Typical when you've onboarded the tenant with manifest:apply and have a permission catalog defined.
  • uma — calls Keycloak's UMA ticket-grant endpoint (the original Permissions behavior). Only needed for legacy tenants that still have KC Authorization Services (resources/scopes/policies/permissions) configured on their backend client. UMA is being phased out across the platform; a matching Java auth-lib migration is on the backlog.

The selection is wired up by auth_lib_init(app). Pass an explicit permissions= instance to override (e.g., a custom subclass).

Quickstart

from fastapi import Depends, FastAPI
from mcpmesh_auth_lib import (
    Tenant, auth_lib_init, build_me_response,
    current_user, require_permission,
)

app = FastAPI()
auth_lib_init(app)  # reads AUTH_LIB_* env vars

TENANT = Tenant(id="t-1", slug="app1", display_name="App One", realm_name="t-app1")


@app.get("/api/me")
def me(claims: dict = Depends(current_user)):
    return build_me_response(
        claims,
        app.state.auth_lib_permissions,
        TENANT,
        raw_token=claims["_raw_token"],
    ).model_dump(by_alias=True)


@app.get("/api/orders", dependencies=[Depends(require_permission("ORDER_VIEW"))])
def orders():
    return [{"id": 1, "item": "Widget", "qty": 3}]

A working version of this is in examples/fastapi_minimal/.

API surface

from mcpmesh_auth_lib import (
    AuthLibSettings,        # pydantic-settings: reads AUTH_LIB_* env vars
    auth_lib_init,          # one-call setup: auth_lib_init(app)

    current_user,           # Depends(): claims dict (raises 401 on bad/missing token)
    optional_user,          # Depends(): claims | None
    require_permission,     # Depends-factory: require_permission("ORDER_VIEW")
    require_any_permission, # Depends-factory: require_any_permission("ORDER_VIEW", "ORDER_APPROVE")
    require_role,           # Depends-factory: require_role("tenant-admin", client="usermanagement")

    Permissions,            # service: UMA-based .all_for(token, claims) -> Set[str]
    ClaimRolesPermissions,  # service: claims-based (default); reads resource_access.<client>.roles
    JwtValidator,           # service: .decode_and_verify(token) -> dict
    JwtValidationError,

    MeResponse, User, Tenant,
    build_me_response,
)

Permission strings

UMA responses (response_mode=permissions) come back as [{rsname, scopes: [...]}]. The lib flattens these to <RSNAME>_<SCOPE> uppercased with _ separator. So an "order" resource with a "view" scope is exposed as the permission string ORDER_VIEW. This matches the React auth-lib-react usePermission("ORDER_VIEW") hook and the Java side's Permissions.all_for(jwt) set semantics.

(The Java side ALSO publishes PERMISSION_ORDER_VIEW-prefixed Spring authorities for @PreAuthorize("hasAuthority('PERMISSION_ORDER_VIEW')") style checks. The Python require_permission(...) factory uses the same un-prefixed strings as Permissions.all_for for ergonomics — no PERMISSION_ prefix needed.)

/me payload shape

{
  "user": {
    "id": "uuid",
    "email": "alice@app1.test",
    "preferredUsername": "alice@app1.test",
    "name": "Alice Tester"
  },
  "context": "tenant",
  "tenant": {
    "id": "t-1",
    "slug": "app1",
    "displayName": "App One",
    "realmName": "t-app1"
  },
  "isPlatformAdmin": false,
  "isTenantAdmin": false,
  "permissions": ["ORDER_VIEW", "ORDER_APPROVE"]
}

This is the same JSON the Java app1-backend returns — auth-lib-react can decode either source with one helper.

Differences vs. Java auth-lib v2

  • Single-realm. Just like the Java side: one AuthLibSettings per app.
  • Multi-audience UMA aggregation. Set AUTH_LIB_AUDIENCES=orders,invoices to pull UMA permissions from multiple resource servers in one /me response.
  • Permission decorator names. The Python require_permission("ORDER_VIEW") uses the un-prefixed permission string. The Java side has both Permissions#has(jwt, "ORDER_VIEW") (un-prefixed) AND @PreAuthorize("hasAuthority('PERMISSION_ORDER_VIEW')") (prefixed). Python only ships the un-prefixed flavor — there's no Spring GrantedAuthority equivalent to mirror in FastAPI.
  • Sync only. v1 uses httpx.Client + redis-py (sync). Async callers can wrap Permissions.all_for in run_in_executor if needed; an async variant may land in a later release.
  • No Spring autoconfig. Call auth_lib_init(app) explicitly in your FastAPI app's startup. (Spring Boot magic doesn't have a Python analogue.)

Testing

python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
pytest -v

Tests use respx to mock both the JWKS and UMA endpoints — no Keycloak needed.

License

Apache-2.0.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_mesh_auth_lib-0.1.1.tar.gz (21.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_mesh_auth_lib-0.1.1-py3-none-any.whl (18.4 kB view details)

Uploaded Python 3

File details

Details for the file mcp_mesh_auth_lib-0.1.1.tar.gz.

File metadata

  • Download URL: mcp_mesh_auth_lib-0.1.1.tar.gz
  • Upload date:
  • Size: 21.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for mcp_mesh_auth_lib-0.1.1.tar.gz
Algorithm Hash digest
SHA256 63ed75a4598aa2e75940eb36b2610090b0bc6b150c168b363d9d9037ce2dde67
MD5 2e64a0c024d53ee5cb9943059b84c56e
BLAKE2b-256 651ddedc55d9db6ad3bef26402e1ae116478411adf0598d91f86342c852d953c

See more details on using hashes here.

File details

Details for the file mcp_mesh_auth_lib-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for mcp_mesh_auth_lib-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 438f36a3ed38bb02a5fac64c7737b28a8935d013c1f6f0698612586c2a325e40
MD5 537f695933b6c8db8c505af8ac77be49
BLAKE2b-256 f8b85f57f3fefe7022774dcad7e94aac5a75eab0980978aa4d79975292b2eb00

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page