Python port of Hallmark — open, IDP-agnostic identity for AI agents via OAuth 2.0 Token Exchange (RFC 8693).
Sync API, zero runtime dependencies, Python ≥3.10.
pip install hallmark-identityfrom hallmark_identity import create_identity, oidc
identity = create_identity(
idp=oidc(issuer="https://your-idp/", client_id="agent", client_secret="…"),
)
# "Who am I?" — the agent's own machine identity (client-credentials grant)
mine = identity.agent().token(audience="https://api.internal")
# "Who am I acting for?" — on behalf of a user (you bring the user's token)
on_behalf = identity.on_behalf_of(user_token).token(
audience="https://api.github.com", scopes=["repo"],
)
import urllib.request
req = urllib.request.Request(
"https://api.github.com/user/repos",
headers={"authorization": f"Bearer {on_behalf.raw}"},
)Any RFC 8693-capable OIDC provider via oidc(), plus a keycloak() convenience adapter:
from hallmark_identity import oidc, keycloak
oidc(issuer="https://login.microsoftonline.com/<tenant>/v2.0", client_id=client_id, client_secret=client_secret)
keycloak(base_url="https://kc.example", realm="agents", client_id=client_id, client_secret=client_secret)| Export | Purpose |
|---|---|
create_identity(idp, store=None, refresh_skew_seconds=30, http_request=None) |
Factory. |
identity.agent().token(audience=None, scopes=None) |
The agent's own identity. |
identity.on_behalf_of(subject_token, type="access_token").token(...) |
On-behalf-of a user. |
oidc(...) / keycloak(...) |
IDP adapters. |
memory_store() |
Default in-memory TokenStore; implement get/set to plug in Redis, etc. |
parse_token, is_expired, will_expire_within |
Token-claim helpers. |
HallmarkError, GrantError, TokenExchangeUnsupportedError |
Typed errors. |
A returned Token exposes .raw, .sub, .act, .aud, .scope, .exp — .raw is the string sent as a bearer token.
uv sync # install deps
uv run pytest # unit tests
uv run pytest -c pytest-integration.ini # opt-in, requires a running Keycloak — see tests/integration/README.md
uv run mypy src # typecheck
uv build # build sdist + wheelMIT