Fulla Python Client
Typed Python SDK for Fulla — the embeddable C++ OAuth2/OIDC authorization server.
- Distribution name:
fulla-oauth2(the shorterfullaname on PyPI belongs to an unrelated project) - Import name:
fulla - API surface: generated from the server's single-source OpenAPI spec
(
apps/server/openapi.yaml) with openapi-python-client 0.29.0 — 78 operations, fully typed (py.typed),attrs-based models overhttpx - Auth layer: handwritten (token lifecycle is too sensitive to template — see the design doc)
pip install fulla-oauth2
Published to PyPI since v1.3.0 (release pipeline's
sdk-pythonjob). Prefer the released wheel; a checkout install (pip install clients/python) still works for development.
Quickstart: machine-to-machine (client_credentials)
from fulla import m2m_client
from fulla.generated.api.o_auth_2 import post_oauth2_introspect
from fulla.generated.models.post_oauth_2_introspect_body import PostOauth2IntrospectBody
client = m2m_client(
"http://localhost:5555",
client_id="backend-svc",
client_secret="…",
scopes=["tokens:read"],
)
# Every request carries a valid Bearer token: fetched lazily, cached,
# refreshed 30 s before expiry, force-refreshed once on a 401.
result = post_oauth2_introspect.sync(
client=client, body=PostOauth2IntrospectBody(token=some_access_token)
)
print(result.active)
# When done: closes BOTH the API client and the auth layer's token pool
# (closing the generated client alone leaks the token connection pool).
from fulla import close_m2m_client
close_m2m_client(client)
Async is symmetric:
from fulla import async_m2m_client
client = async_m2m_client("http://localhost:5555", "backend-svc", "…", scopes=["tokens:read"])
result = await post_oauth2_introspect.asyncio(client=client, body=body)
One-shot token fetch (scripts, benchmarks):
from fulla import fetch_client_credentials_token
token, expires_in = fetch_client_credentials_token(
"http://localhost:5555", "backend-svc", "…", ["tokens:read"]
)
Introspect / revoke (client Basic authentication)
/oauth2/introspect and /oauth2/revoke authenticate the calling client, not a user bearer
token (RFC 7662 §2.1). Use a client whose every request carries HTTP Basic — confidential
clients must use Basic; the server rejects credentials in the body (F-017):
from fulla import basic_auth_client
client = basic_auth_client("http://localhost:5555", "backend-svc", "…")
result = post_oauth2_introspect.sync(client=client, body=PostOauth2IntrospectBody(token=tok))
Authorization-code flow (web apps, with PKCE)
from fulla import AuthorizationCodeFlow, PkcePair
flow = AuthorizationCodeFlow(
"http://localhost:5555", "my-client", "my-secret",
redirect_uri="https://my.app/callback", scopes=["openid", "profile"],
)
pkce = PkcePair.generate()
authorize_url = flow.build_authorize_url(state=session_csrf, pkce=pkce)
# → send the user's browser to authorize_url; Fulla redirects back with ?code=…&state=…
# → VERIFY state, then:
tokens = flow.exchange_code(code, pkce.verifier)
# … later; Fulla rotates refresh tokens on every use (V008):
tokens = flow.refresh(tokens.refresh_token)
Generated API modules
Everything under fulla.generated is the typed surface of the whole server API:
from fulla.generated.api.open_id_connect import get_well_known_openid_configuration
from fulla.generated.api.o_auth_2 import get_oauth2_userinfo, post_oauth2_token
from fulla.generated.models.token_request import TokenRequest
Each endpoint module offers sync, sync_detailed, asyncio, asyncio_detailed. The
*_detailed variants return status code + raw response alongside the parsed model.
Development
cd clients/python
pip install -e ".[dev]"
pytest # unit tests (in-process MockTransport, no server needed)
# integration tests (needs a running full stack, see tests/integration/)
FULLA_BASE_URL=http://127.0.0.1:5555 pytest tests/integration
Regenerating the committed src/fulla/generated/ tree after an openapi.yaml change:
pip install openapi-python-client==0.29.0
python tools/clients/regen_clients.py # from the repo root
CI (.github/workflows/clients-sdk.yml) re-generates and diffs on every PR touching
clients/** or the spec — committed generated code can never go stale.
Versioning
The package version is locked to the server's cmake/Version.cmake (enforced by
tools/clients/regen_clients.py --version-only at release time). Breaking HTTP API changes
require a major bump on both sides (guarded by the openapi-governance oasdiff workflow).
Local network note
If go/module proxies are unreachable from your network, the Go generator download mentioned
in the regen docs needs a GOPROXY mirror (e.g. GOPROXY=https://goproxy.cn,direct). This only
affects regenerating clients/go — installing and using this Python package is unaffected.
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 fulla_oauth2-1.0.0.tar.gz.
File metadata
- Download URL: fulla_oauth2-1.0.0.tar.gz
- Upload date:
- Size: 58.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
44847de82772e0748fedeca5a95d9171863f8f16c6ac17c76fd1a2af283633fb
|
|
| MD5 |
226f3620ed42e749ed8c9119a9968b5c
|
|
| BLAKE2b-256 |
6c122fc288d89181130ddd185c02aa1618cfae60325f68a4cad3ac0062651661
|
File details
Details for the file fulla_oauth2-1.0.0-py3-none-any.whl.
File metadata
- Download URL: fulla_oauth2-1.0.0-py3-none-any.whl
- Upload date:
- Size: 173.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6146087a1138eddb7636e2be91cc9f805276038f85822e7e34fb92722bb0b919
|
|
| MD5 |
409e3ba27e89730408e1917e12fd4ffc
|
|
| BLAKE2b-256 |
e22292454385ec7ada4da644683f0ec359516a6c36c03507d839606b888a0b9b
|