Skip to main content

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.

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.2.0.tar.gz (21.9 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.2.0-py3-none-any.whl (18.7 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: mcp_mesh_auth_lib-0.2.0.tar.gz
  • Upload date:
  • Size: 21.9 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.2.0.tar.gz
Algorithm Hash digest
SHA256 48d840f6199b6494b05ff5a8a9be262cf8d58fdcb087490dec1d9efad9eb6b42
MD5 aa12bb33d41e0b740d1a631b1ed36145
BLAKE2b-256 39a45a6b415eef548344c467cad6c5a808e223b3f3839c2b2fbbbed6aaa11f0e

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for mcp_mesh_auth_lib-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 aca80bc44e52e5a78783d5ec28e96fff42674e2ad54cd33aa936a2c54fd78052
MD5 e8b5dbe3b6eb703a68f256f4ec9cd043
BLAKE2b-256 b7fd3c2169678eb4910b3b78bde5db2371a9162466752acaa4eb80791664e470

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 Sentry Error logging StatusPage Status page