IdP SDK — Python
Add AuthPI login to Python backends with authpi-idp — the OIDC authorization-code flow with PKCE, token refresh, and per-organization authorization checks.
Last updated 2026-07-19
authpi-idp is the official Python SDK for authenticating users against your AuthPI issuer: it drives the OIDC authorization-code flow with PKCE, exchanges and refreshes tokens, and gives you an authenticated agent object with the user’s identity and per-organization permissions.
Requirements: Python 3.11+. All token operations are async.
Install
pip install authpi-idp
Initialize
from authpi_idp import IdpClient
idp = IdpClient(
issuer_url="https://idp.authpi.com/i_4r8w2k9m5x1p7q3e6t0y2u4i8",
client_id="c_xxx",
client_secret="...", # omit for public clients (SPAs, native apps)
redirect_uri="https://app.example.com/callback",
# resources=["https://api.example.com/reports"], # after configuring RFC 8707 policy
)
After adding an exact resource to both the issuer catalog and registered-client allowlist, optional resources values are serialized as repeated RFC 8707 resource parameters during authorization, code exchange, and refresh.
The login flow
1. Send the user to AuthPI. create_authorization_url is synchronous and generates the PKCE verifier, state, and nonce for you — store the returned object in the user’s session, then redirect:
auth = idp.create_authorization_url(scopes=["openid", "profile", "email"])
session["oauth"] = auth.model_dump()
return redirect(auth.url)
2. Handle the callback. Use the stored authorization object to validate state, exchange the code, and verify the ID-token nonce when an ID token is present:
from authpi_idp import AuthorizationUrl
agent = await idp.exchange_callback(callback_url, AuthorizationUrl(**session["oauth"]))
session["tokens"] = agent.tokens.model_dump()
AuthorizationUrl includes the effective resources list. Store the whole model: exchange_callback repeats that exact list during code exchange. To select a refresh subset explicitly, pass resources=["https://api.example.com/reports"] to refresh or create_agent. An empty list omits the parameter. Automatic refresh from create_agent also omits resource unless that call explicitly supplies resources, even when the client has an authorization default; under an optional policy the server then retains the refresh session’s selected set. A policy with required: true needs explicit create_agent resources and rejects omission. TokenSet intentionally has no resource field.
3. Use the agent. It carries the user’s identity and organization memberships, with an authorization helper that checks scopes within an organization:
if agent.has_access_in("org_0kfz3m8q1w5e9r2t6y4u7i3o5", "write", "projects"):
... # the user can write to projects in that organization
Sessions and refresh
Rebuild an agent from stored tokens on subsequent requests — create_agent refreshes expired access tokens automatically and hands you the rotated tokens through on_refresh:
agent = await idp.create_agent(
session["tokens"],
on_refresh=lambda new_tokens: session.update({"tokens": new_tokens}),
on_refresh_error=lambda error: handle_session_expired(error),
)
Refresh tokens rotate on use, so always persist what on_refresh gives you. Organization claims in refreshed tokens are recomputed at every refresh — membership changes propagate within the access-token TTL (the propagation contract).
Next steps
- Server-side auth with the TypeScript SDK — the same flow, step by step (the concepts transfer directly)
- Token claims reference — what’s inside
agent.tokens - Validate tokens in your API — verifying these tokens in downstream services
- Package on PyPI — full README, framework integration notes, and advanced options