auth-ai 0.1.0.dev202611021808__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. auth_ai-0.1.0.dev202611021808/.gitignore +25 -0
  2. auth_ai-0.1.0.dev202611021808/CHANGELOG.md +60 -0
  3. auth_ai-0.1.0.dev202611021808/LICENSE +21 -0
  4. auth_ai-0.1.0.dev202611021808/PKG-INFO +345 -0
  5. auth_ai-0.1.0.dev202611021808/README.md +308 -0
  6. auth_ai-0.1.0.dev202611021808/examples/minimal/README.md +79 -0
  7. auth_ai-0.1.0.dev202611021808/pyproject.toml +180 -0
  8. auth_ai-0.1.0.dev202611021808/src/auth_ai/__init__.py +127 -0
  9. auth_ai-0.1.0.dev202611021808/src/auth_ai/ai.py +101 -0
  10. auth_ai-0.1.0.dev202611021808/src/auth_ai/claims.py +88 -0
  11. auth_ai-0.1.0.dev202611021808/src/auth_ai/client.py +274 -0
  12. auth_ai-0.1.0.dev202611021808/src/auth_ai/config.py +142 -0
  13. auth_ai-0.1.0.dev202611021808/src/auth_ai/errors.py +162 -0
  14. auth_ai-0.1.0.dev202611021808/src/auth_ai/gateway.py +195 -0
  15. auth_ai-0.1.0.dev202611021808/src/auth_ai/gateway_errors.py +74 -0
  16. auth_ai-0.1.0.dev202611021808/src/auth_ai/grants.py +212 -0
  17. auth_ai-0.1.0.dev202611021808/src/auth_ai/http.py +129 -0
  18. auth_ai-0.1.0.dev202611021808/src/auth_ai/id_token.py +38 -0
  19. auth_ai-0.1.0.dev202611021808/src/auth_ai/introspection.py +19 -0
  20. auth_ai-0.1.0.dev202611021808/src/auth_ai/jwt.py +130 -0
  21. auth_ai-0.1.0.dev202611021808/src/auth_ai/messages.py +86 -0
  22. auth_ai-0.1.0.dev202611021808/src/auth_ai/provider.py +114 -0
  23. auth_ai-0.1.0.dev202611021808/src/auth_ai/py.typed +0 -0
  24. auth_ai-0.1.0.dev202611021808/src/auth_ai/server/__init__.py +59 -0
  25. auth_ai-0.1.0.dev202611021808/src/auth_ai/server/backchannel_logout.py +172 -0
  26. auth_ai-0.1.0.dev202611021808/src/auth_ai/server/bearer.py +87 -0
  27. auth_ai-0.1.0.dev202611021808/src/auth_ai/server/billing.py +110 -0
  28. auth_ai-0.1.0.dev202611021808/src/auth_ai/server/consumption.py +135 -0
  29. auth_ai-0.1.0.dev202611021808/src/auth_ai/server/machine_api.py +43 -0
  30. auth_ai-0.1.0.dev202611021808/src/auth_ai/server/machine_tokens.py +65 -0
  31. auth_ai-0.1.0.dev202611021808/src/auth_ai/server/protected_resource.py +70 -0
  32. auth_ai-0.1.0.dev202611021808/src/auth_ai/session_user.py +46 -0
  33. auth_ai-0.1.0.dev202611021808/src/auth_ai/starlette.py +121 -0
  34. auth_ai-0.1.0.dev202611021808/src/auth_ai/token_endpoint.py +122 -0
  35. auth_ai-0.1.0.dev202611021808/src/auth_ai/types.py +59 -0
  36. auth_ai-0.1.0.dev202611021808/src/auth_ai/wallet.py +221 -0
  37. auth_ai-0.1.0.dev202611021808/src/auth_ai/web/__init__.py +18 -0
  38. auth_ai-0.1.0.dev202611021808/src/auth_ai/web/ai_routes.py +56 -0
  39. auth_ai-0.1.0.dev202611021808/src/auth_ai/web/cookies.py +56 -0
  40. auth_ai-0.1.0.dev202611021808/src/auth_ai/web/handlers.py +419 -0
  41. auth_ai-0.1.0.dev202611021808/src/auth_ai/web/http.py +91 -0
  42. auth_ai-0.1.0.dev202611021808/src/auth_ai/web/seal.py +59 -0
  43. auth_ai-0.1.0.dev202611021808/src/auth_ai/web/sessions.py +94 -0
@@ -0,0 +1,25 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ .venv*/
5
+ dist/
6
+ build/
7
+ *.egg-info/
8
+ .mypy_cache/
9
+ .ruff_cache/
10
+ .pytest_cache/
11
+ .coverage
12
+ .coverage.*
13
+ htmlcov/
14
+ coverage.xml
15
+ .env
16
+ .env.*
17
+ !.env.example
18
+
19
+ # OS
20
+ .DS_Store
21
+ Thumbs.db
22
+
23
+ # Editor
24
+ .idea/
25
+ .vscode/settings.json
@@ -0,0 +1,60 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog 1.1.0](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html).
7
+
8
+ Sections, when present, appear in this order: `Added`, `Changed`, `Removed`,
9
+ `Fixed`, `Security`. Per our no-backward-compatibility policy
10
+ ([CONTRIBUTING.md](CONTRIBUTING.md#2-versioning-and-breaking-changes)) there is no
11
+ `Deprecated` section: incompatible changes go straight to `Removed` and bump the version.
12
+
13
+ ## [Unreleased]
14
+
15
+ <!-- Add user-visible changes here. Move to a dated section at release time. -->
16
+
17
+ ## [0.1.0] - 2026-11-02
18
+
19
+ First release: the Python port of `@circle-rd/auth-ai` 0.1.1, with the same behaviours,
20
+ wire contract and error codes.
21
+
22
+ ### Added
23
+
24
+ - **Core (`auth_ai`)**: `AuthClient` (discovery cache, PKCE login, callback with
25
+ ID-token verification, refresh with RFC 8707 resource narrowing, logout URL, revoke,
26
+ introspect, `client_credentials`), `GrantManager` (serialized refresh-token rotation,
27
+ per-audience access tokens, pluggable `GrantStore` and `GrantLock`), typed claims with
28
+ `has_role` / `has_permission` / `has_feature` / `get_feature`, the error registry
29
+ (`CFG_`, `OIDC_`, `TOK_`, `API_`, `GW_`, `SES_`, `REQ_`, `SRV_`) with `to_public_json()`.
30
+ - **Wallet and gateway**: `WalletClient` (balance, statistics, per-session usage,
31
+ transactions, top-up options and link; `int` micro-euros, `format_euros`),
32
+ `GatewayClient` (origin-locked `httpx` client, OpenAI-compatible base URL, models,
33
+ agents, MCP servers, MCP URLs, `post_json`, gateway error mapping),
34
+ `AiClient.for_user()`, and the `gateway_url` option that adds the gateway and
35
+ auth-service audiences and the `wallet:self:read` scope.
36
+ - **Server (`auth_ai.server`)**: Bearer-token verifier, back-channel logout handler with
37
+ replay protection, RFC 9728 protected-resource metadata and `WWW-Authenticate`
38
+ challenges, machine API (subscription, metered usage, invoices, consumption reporting).
39
+ - **Backend-for-frontend (`auth_ai.web`, `auth_ai.starlette`)**: `AuthHandlers`
40
+ (`/login`, `/callback`, `/me`, `/logout`, `/backchannel-logout`, optional `/wallet`,
41
+ `/wallet/stats`, `/models`), sealed one-shot login cookie (interoperable with the
42
+ TypeScript SDK), hashed session ids, `SessionStore` contract, scheduled claims
43
+ renewal; Starlette and FastAPI adapter with `install_auth`, `require_auth` and
44
+ `load_session`.
45
+ - Example application (`examples/minimal`) with an end-to-end self-test, and the
46
+ Docus documentation site.
47
+
48
+ ### Security
49
+
50
+ - Every failed sign-in callback answers one generic `403 SES_005`; messages and details
51
+ stay in the application's logs.
52
+ - `__Host-` cookies, same-origin redirect targets, refused cross-site logout, URLs built
53
+ on the registered origin, client credentials form-urlencoded per RFC 6749 §2.3.1.
54
+ - JWTs signed with `EdDSA` are refused: RFC 9864 deprecates that algorithm name. The
55
+ fully specified `Ed25519` and `Ed448` are accepted, next to the ES\*, RS\* and PS\*
56
+ families (auth-service signs with ES256).
57
+ - A server-side failure (5xx answer) of the sign-in routes is reported to `on_error`.
58
+
59
+ [Unreleased]: https://github.com/circle-rd/auth-ai-py/compare/v0.1.0...HEAD
60
+ [0.1.0]: https://github.com/circle-rd/auth-ai-py/releases/tag/v0.1.0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 CIRCLE <https://circle-cyber.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,345 @@
1
+ Metadata-Version: 2.5
2
+ Name: auth-ai
3
+ Version: 0.1.0.dev202611021808
4
+ Summary: Python SDK for applications relying on a CIRCLE auth-service identity provider: OIDC sign-in (backend-for-frontend), entitlements, billing, credit wallet and LiteLLM gateway.
5
+ Project-URL: Homepage, https://github.com/circle-rd/auth-ai-py
6
+ Project-URL: Documentation, https://docs.circle-cyber.com/auth-ai-py
7
+ Project-URL: Repository, https://github.com/circle-rd/auth-ai-py
8
+ Project-URL: Issues, https://github.com/circle-rd/auth-ai-py/issues
9
+ Project-URL: Changelog, https://github.com/circle-rd/auth-ai-py/blob/main/CHANGELOG.md
10
+ Author: CIRCLE
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: ai-gateway,asyncio,authentication,authorization,back-channel-logout,backend-for-frontend,bff,billing,circle,credits,entitlements,fastapi,httpx,identity-provider,jwt,litellm,llm,mcp,oauth2,oauth2.1,oidc,openid-connect,pkce,rbac,self-hosted,session,sso,starlette,subscription,token-wallet,usage-based-billing
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Framework :: AsyncIO
16
+ Classifier: Framework :: FastAPI
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Operating System :: OS Independent
20
+ Classifier: Programming Language :: Python :: 3
21
+ Classifier: Programming Language :: Python :: 3 :: Only
22
+ Classifier: Programming Language :: Python :: 3.12
23
+ Classifier: Programming Language :: Python :: 3.13
24
+ Classifier: Programming Language :: Python :: 3.14
25
+ Classifier: Topic :: Internet :: WWW/HTTP :: Session
26
+ Classifier: Topic :: Security
27
+ Classifier: Typing :: Typed
28
+ Requires-Python: >=3.12
29
+ Requires-Dist: httpx>=0.28
30
+ Requires-Dist: joserfc>=1.7
31
+ Requires-Dist: pydantic>=2.12
32
+ Provides-Extra: fastapi
33
+ Requires-Dist: fastapi>=0.115; extra == 'fastapi'
34
+ Provides-Extra: starlette
35
+ Requires-Dist: starlette>=0.46; extra == 'starlette'
36
+ Description-Content-Type: text/markdown
37
+
38
+ <p align="center">
39
+ <picture>
40
+ <source media="(prefers-color-scheme: dark)" srcset="docs-site/public/logo-dark.svg">
41
+ <img src="docs-site/public/logo-light.svg" alt="CIRCLE auth-ai logo" width="140" />
42
+ </picture>
43
+ </p>
44
+
45
+ <h1 align="center">CIRCLE auth-ai for Python</h1>
46
+
47
+ <p align="center">
48
+ <em>The Python SDK for CIRCLE auth-service — sign-in, entitlements, billing and LLM credit, in one library.</em>
49
+ </p>
50
+
51
+ <p align="center">
52
+ <a href="https://github.com/circle-rd/auth-ai-py/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/circle-rd/auth-ai-py/actions/workflows/ci.yml/badge.svg"></a>
53
+ <a href="https://pypi.org/project/auth-ai/"><img alt="PyPI" src="https://img.shields.io/pypi/v/auth-ai?logo=pypi&logoColor=white"></a>
54
+ <a href="https://pypi.org/project/auth-ai/"><img alt="Python" src="https://img.shields.io/pypi/pyversions/auth-ai?logo=python&logoColor=white"></a>
55
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-blue.svg"></a>
56
+ <a href="https://mypy-lang.org"><img alt="Typed" src="https://img.shields.io/badge/typed-mypy%20strict-2a6db2.svg"></a>
57
+ <a href="https://github.com/circle-rd/auth-service"><img alt="auth-service" src="https://img.shields.io/badge/for-auth--service-black.svg"></a>
58
+ </p>
59
+
60
+ Let your application sign users in with a self-hosted
61
+ [CIRCLE auth-service](https://github.com/circle-rd/auth-service), read what they
62
+ are entitled to (roles, permissions, plan features), meter what they consume,
63
+ and call LLMs on their behalf through a [LiteLLM](https://docs.litellm.ai)
64
+ gateway that bills their credit wallet — without writing any OAuth, token
65
+ refresh, logout or billing plumbing yourself.
66
+
67
+ This is the Python port of [`@circle-rd/auth-ai`](https://github.com/circle-rd/auth-ai):
68
+ same behaviours, same wire contract, same error codes. A Python backend can serve
69
+ the TypeScript browser client and the React and Vue bindings unchanged.
70
+
71
+ Full documentation: **https://docs.circle-cyber.com/auth-ai-py/**
72
+
73
+ ```python
74
+ handlers = AuthHandlers(auth=auth, grants=grants, sessions=sessions, secret=secret, ai=ai)
75
+ install_auth(app, handlers) # /auth/login, /callback, /me, /logout…
76
+
77
+
78
+ @app.get("/api/models")
79
+ async def models(session: Session = Depends(require_auth(handlers))):
80
+ return await ai.for_user(session.user_id).gateway.list_models()
81
+ ```
82
+
83
+ ## Principles
84
+
85
+ - **Do not reinvent OAuth or JOSE.** Signature, JWT and cookie encryption work stays
86
+ with the maintained library [joserfc](https://jose.authlib.org) and HTTP with
87
+ [httpx](https://www.python-httpx.org); the SDK adds what is specific to auth-service
88
+ on top of them. Every runtime dependency is permissively licensed (BSD-3-Clause or MIT).
89
+ - **Backend-for-frontend by default.** The browser holds one opaque session
90
+ cookie. Access and refresh tokens never reach it.
91
+ - **Fail loud, leak nothing.** Invalid state is rejected, not repaired. Errors
92
+ carry stable codes; HTTP responses expose the code and a generic message, and
93
+ the detail stays in your logs.
94
+ - **Strict at every boundary.** Every external payload (tokens, API answers,
95
+ cookies, configuration) is validated with [pydantic](https://docs.pydantic.dev);
96
+ the package ships `py.typed` and is checked with `mypy --strict`.
97
+ - **Async all the way.** Built on [httpx](https://www.python-httpx.org) and
98
+ `asyncio`; bring your own `httpx.AsyncClient` for proxies, transports or tracing.
99
+ - **Centralized spending.** The SDK never debits a wallet itself: the gateway
100
+ bills each call, so margin and pricing stay under the administrator's control.
101
+
102
+ ## Features
103
+
104
+ **Sign-in and sessions**
105
+
106
+ - Authorization-code flow with PKCE, `state` and `nonce`, multi-audience
107
+ tokens ([RFC 8707](https://www.rfc-editor.org/rfc/rfc8707)), TOTP handled by
108
+ the provider.
109
+ - One serialized refresh-token chain per user, with per-audience access tokens
110
+ minted on demand and never stored.
111
+ - Sealed one-shot login cookie, hashed session ids, `__Host-` cookies,
112
+ same-origin redirect targets, refused cross-site logout.
113
+ - Claims (roles, permissions, features, plan, organization) renewed from the
114
+ provider on a schedule.
115
+ - **Back-channel logout**: signature, `typ`, audience, event, lifetime and
116
+ replay checks, then the sessions and grant of the user end.
117
+
118
+ **Entitlements**
119
+
120
+ - Typed claims and helpers: `has_role`, `has_permission` (write implies read),
121
+ `has_feature`, `get_feature`.
122
+ - Bearer-token verification for your own API, with RFC 9728
123
+ protected-resource metadata and `WWW-Authenticate` challenges (MCP-friendly).
124
+
125
+ **LLM gateway and credit wallet**
126
+
127
+ - One switch in auth-service ("LiteLLM support") and one option here
128
+ (`gateway_url`) turn on the gateway and wallet audiences.
129
+ - An origin-locked `httpx` client that signs OpenAI-compatible calls with a
130
+ fresh user token (hand it to the `openai` package); model, agent and MCP
131
+ server listing; gateway errors mapped (`402` → `GW_001`, `429` with
132
+ `Retry-After`, …).
133
+ - Wallet reads: balance, spend statistics, per-session usage, transactions,
134
+ top-up link. Amounts are exact `int` micro-euros.
135
+
136
+ **Billing and consumption**
137
+
138
+ - Machine client (`client_credentials`, cached and single-flighted): a user's
139
+ subscription, metered usage and invoices; usage reporting with idempotency
140
+ keys for features billed per call.
141
+
142
+ **Delivery**
143
+
144
+ - Framework-agnostic `WebRequest` → `WebResponse` handlers (mount them
145
+ anywhere) and a Starlette / FastAPI adapter.
146
+ - Python 3.12+ (tested on 3.12, 3.13 and 3.14), full type declarations, MIT licensed.
147
+
148
+ ## Install
149
+
150
+ ```sh
151
+ pip install "auth-ai[fastapi]" # or auth-ai[starlette], or plain auth-ai
152
+ ```
153
+
154
+ Register your application in auth-service first: a confidential application with
155
+ the **LiteLLM support** switch on (if you call the gateway), the redirect URI
156
+ `https://<host>/auth/callback` and the back-channel logout URI
157
+ `https://<host>/auth/backchannel-logout`.
158
+
159
+ ## Quick start
160
+
161
+ A FastAPI backend (the stores below live in memory: demo only, see
162
+ [Production](#production)):
163
+
164
+ ```python
165
+ import os
166
+ from collections.abc import AsyncIterator
167
+ from contextlib import asynccontextmanager
168
+
169
+ from fastapi import Depends, FastAPI
170
+
171
+ from auth_ai import AiClient, AuthClient, GrantManager, MemoryGrantStore
172
+ from auth_ai.starlette import install_auth, require_auth
173
+ from auth_ai.web import AuthHandlers, MemorySessionStore, Session
174
+
175
+ auth = AuthClient(
176
+ {
177
+ "issuer": "https://auth.example.com",
178
+ "client_id": "my-app",
179
+ "client_secret": os.environ["AUTH_CLIENT_SECRET"],
180
+ "redirect_uri": "https://app.example.com/auth/callback",
181
+ "gateway_url": "https://llm.example.com",
182
+ }
183
+ )
184
+ grants = GrantManager(auth=auth, store=MemoryGrantStore())
185
+ ai = AiClient(auth=auth, grants=grants)
186
+ handlers = AuthHandlers(
187
+ auth=auth,
188
+ grants=grants,
189
+ ai=ai, # adds /auth/wallet, /auth/wallet/stats and /auth/models for your frontend
190
+ sessions=MemorySessionStore(),
191
+ secret=os.environ["COOKIE_SECRET"], # 32+ random characters
192
+ )
193
+
194
+
195
+ @asynccontextmanager
196
+ async def lifespan(_app: FastAPI) -> AsyncIterator[None]:
197
+ yield
198
+ await auth.aclose()
199
+
200
+
201
+ app = FastAPI(lifespan=lifespan)
202
+ install_auth(app, handlers)
203
+
204
+
205
+ @app.get("/api/models")
206
+ async def models(session: Session = Depends(require_auth(handlers))):
207
+ return await ai.for_user(session.user_id).gateway.list_models()
208
+ ```
209
+
210
+ Calling the gateway with the `openai` package (or any OpenAI-compatible SDK that
211
+ takes an `httpx` client):
212
+
213
+ ```python
214
+ from openai import AsyncOpenAI
215
+
216
+ gateway = ai.for_user(user_id).gateway
217
+ async with gateway.create_http_client() as http_client:
218
+ client = AsyncOpenAI(
219
+ base_url=gateway.openai_base_url,
220
+ api_key="unused", # replaced by a fresh user token on every request
221
+ http_client=http_client,
222
+ )
223
+ answer = await client.chat.completions.create(
224
+ model="my-model", messages=[{"role": "user", "content": "Hello"}]
225
+ )
226
+ ```
227
+
228
+ Reporting usage for a feature billed per call, from your backend:
229
+
230
+ ```python
231
+ from auth_ai.server import MachineApi
232
+
233
+ machine = MachineApi(auth)
234
+ await machine.consumption.report(user_id, "audits", 1, idempotency_key="audit-42")
235
+ ```
236
+
237
+ A complete, deployable application lives in [`examples/minimal`](examples/minimal).
238
+
239
+ ## Modules
240
+
241
+ | Import | What it gives you |
242
+ | --------------------- | --------------------------------------------------------------------------------------- |
243
+ | `auth_ai` | OIDC client, grants, claims helpers, wallet and gateway clients, error registry |
244
+ | `auth_ai.server` | Bearer verification, back-channel logout, protected-resource metadata, machine API |
245
+ | `auth_ai.web` | Sign-in handlers (`WebRequest` → `WebResponse`), session store contract |
246
+ | `auth_ai.starlette` | `install_auth`, `require_auth` and `load_session` for Starlette and FastAPI (`starlette` ≥ 0.46) |
247
+
248
+ ## Architecture
249
+
250
+ ```mermaid
251
+ sequenceDiagram
252
+ autonumber
253
+ participant B as Browser
254
+ participant A as Your backend (auth-ai)
255
+ participant S as auth-service
256
+ participant G as LiteLLM gateway
257
+ B->>A: GET /auth/login
258
+ A-->>B: 302 to auth-service (PKCE, resources) + sealed cookie
259
+ B->>S: sign in (password, TOTP…)
260
+ S-->>B: 302 /auth/callback?code
261
+ B->>A: GET /auth/callback
262
+ A->>S: code → tokens (client secret)
263
+ A-->>B: 303 + opaque session cookie
264
+ B->>A: GET /auth/wallet (cookie)
265
+ A->>S: wallet read (user token for auth-service)
266
+ B->>A: POST /api/chat (cookie)
267
+ A->>G: call (user token for the gateway)
268
+ G->>S: debit the wallet after the call
269
+ S-->>A: back-channel logout on sign-out anywhere
270
+ ```
271
+
272
+ ```
273
+ auth-ai-py/
274
+ ├── 📁 src/auth_ai/
275
+ │ ├── client.py AuthClient — discovery, PKCE login, refresh, logout, revoke, introspect
276
+ │ ├── config.py Validated configuration (gateway_url adds the gateway audiences)
277
+ │ ├── grants.py GrantManager — serialized refresh rotation, per-audience tokens
278
+ │ ├── claims.py Claim models and entitlement helpers
279
+ │ ├── wallet.py WalletClient and wallet models (int micro-euros)
280
+ │ ├── gateway.py GatewayClient — origin-locked httpx client, listings, MCP URLs
281
+ │ ├── ai.py AiClient.for_user(grant_id)
282
+ │ ├── errors.py AuthAiError + ERR registry (OIDC_*, TOK_*, GW_*, SES_*…)
283
+ │ ├── 📁 server/ Bearer verification, back-channel logout, machine API
284
+ │ ├── 📁 web/ Sign-in handlers, sealed cookies, session store contract
285
+ │ └── starlette.py Starlette / FastAPI adapter
286
+ ├── 📁 tests/ pytest suite on a scripted identity provider (no network)
287
+ ├── 📁 examples/minimal/ Deployable end-to-end app with a self-test
288
+ └── 📁 docs-site/ Docus documentation (built into a container image)
289
+ ```
290
+
291
+ ## Production
292
+
293
+ The SDK is storage-agnostic on purpose. Before going live:
294
+
295
+ - **Persist sessions and grants.** Implement `SessionStore` and `GrantStore` on
296
+ your database. The in-memory stores sign everyone out on restart and cannot
297
+ be shared by replicas.
298
+ - **Several replicas?** Provide a distributed `GrantLock` (a refresh token
299
+ rotates on every use; a replayed one revokes the whole family) and a shared
300
+ `ReplayStore` for back-channel logout tokens.
301
+ - **Secrets from the environment only:** the client secret and a 32+ character
302
+ cookie secret. Neither is ever logged or returned.
303
+ - **Registered URIs must match:** `redirect_uri` is `<origin><base_path>/callback`
304
+ and the back-channel URI is `<origin><base_path>/backchannel-logout`, reachable
305
+ over HTTPS by auth-service.
306
+ - **Log with `on_error`, answer with `to_public_json()`.** Never forward
307
+ `error.message` or `error.details` to a browser.
308
+ - **Keep your ASGI stack current:** keep `starlette` / `fastapi` on a release
309
+ that carries the latest security fixes.
310
+
311
+ ## Documentation
312
+
313
+ The full documentation is published at
314
+ [docs.circle-cyber.com/auth-ai-py](https://docs.circle-cyber.com/auth-ai-py/):
315
+ concepts, one guide per framework, the configuration and API reference, the
316
+ error-code registry and a production checklist.
317
+
318
+ **For AI agents** — the documentation is exposed through a built-in MCP server
319
+ (HTTP), so agents can query it directly:
320
+
321
+ | Environment | MCP endpoint |
322
+ | ----------- | ---------------------------------------------------------------------- |
323
+ | Production | `https://docs.circle-cyber.com/auth-ai-py/mcp` |
324
+ | Local dev | `http://localhost:3000/mcp` (run `npm run dev` in `docs-site/`) |
325
+
326
+ **Contributing** — the development rules in [AGENTS.md](AGENTS.md) (naming,
327
+ typing strictness, error handling, testing, security) and the process in
328
+ [CONTRIBUTING.md](CONTRIBUTING.md) are mandatory for any contribution.
329
+ Security reports: see [SECURITY.md](SECURITY.md). Releases are tracked in
330
+ [CHANGELOG.md](CHANGELOG.md).
331
+
332
+ ## Roadmap
333
+
334
+ - **More adapters** (Django, Flask, Litestar) on top of the framework-agnostic
335
+ handlers, as needs appear.
336
+ - **A synchronous facade**, if a real need shows up.
337
+
338
+ ## Credits & license
339
+
340
+ Built and maintained by [CIRCLE](https://circle-cyber.com). Stands on the
341
+ shoulders of [joserfc](https://jose.authlib.org),
342
+ [httpx](https://www.python-httpx.org), [pydantic](https://docs.pydantic.dev) and
343
+ [Starlette](https://www.starlette.io).
344
+
345
+ Licensed under the **MIT License** — see [LICENSE](LICENSE).