authpi-admin 0.3.0__tar.gz → 0.7.0__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.
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/.gitignore +9 -0
- authpi_admin-0.7.0/CHANGELOG.md +71 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/PKG-INFO +37 -11
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/README.md +36 -10
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/__init__.py +1 -1
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/_version.py +1 -1
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/client.py +33 -16
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/__init__.py +13 -4
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/models.py +288 -9
- authpi_admin-0.3.0/authpi_admin/generated/resources/api_keys.py → authpi_admin-0.7.0/authpi_admin/generated/resources/account_api_keys.py +88 -10
- authpi_admin-0.7.0/authpi_admin/generated/resources/account_tokens.py +85 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/accounts.py +8 -53
- authpi_admin-0.3.0/authpi_admin/generated/resources/verifiers.py → authpi_admin-0.7.0/authpi_admin/generated/resources/agent_verifiers.py +6 -6
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/agents.py +11 -8
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/approvals.py +3 -3
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/auth_methods.py +11 -8
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/clients.py +5 -5
- authpi_admin-0.7.0/authpi_admin/generated/resources/credits.py +100 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/deliveries.py +1 -1
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/domains.py +12 -9
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/events.py +53 -2
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/groups.py +14 -14
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/invitations.py +15 -14
- authpi_admin-0.3.0/authpi_admin/generated/resources/sessions.py → authpi_admin-0.7.0/authpi_admin/generated/resources/issuer_sessions.py +8 -8
- authpi_admin-0.7.0/authpi_admin/generated/resources/issuer_tokens.py +46 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/issuers.py +11 -8
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/members.py +12 -11
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/notes.py +61 -4
- authpi_admin-0.3.0/authpi_admin/generated/resources/trusted_devices.py → authpi_admin-0.7.0/authpi_admin/generated/resources/organization_api_keys.py +22 -30
- authpi_admin-0.7.0/authpi_admin/generated/resources/organizations.py +182 -0
- authpi_admin-0.7.0/authpi_admin/generated/resources/payment_methods.py +95 -0
- authpi_admin-0.7.0/authpi_admin/generated/resources/sso.py +134 -0
- authpi_admin-0.7.0/authpi_admin/generated/resources/trusted_devices.py +133 -0
- authpi_admin-0.7.0/authpi_admin/generated/resources/user_sessions.py +62 -0
- authpi_admin-0.3.0/authpi_admin/generated/resources/tokens.py → authpi_admin-0.7.0/authpi_admin/generated/resources/user_tokens.py +6 -6
- authpi_admin-0.3.0/authpi_admin/generated/resources/users.py → authpi_admin-0.7.0/authpi_admin/generated/resources/user_verifiers.py +82 -63
- authpi_admin-0.7.0/authpi_admin/generated/resources/users.py +162 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/webhooks.py +4 -4
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/scopes/agent_scope.py +6 -6
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/scopes/issuer_scope.py +16 -35
- authpi_admin-0.7.0/authpi_admin/generated/scopes/organization_scope.py +108 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/scopes/user_scope.py +12 -12
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/scopes/webhook_scope.py +3 -3
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/http_client.py +94 -15
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/pyproject.toml +1 -1
- authpi_admin-0.7.0/tests/test_client.py +176 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/tests/test_http_client.py +44 -18
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/tests/test_integration.py +55 -31
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/uv.lock +240 -238
- authpi_admin-0.3.0/CHANGELOG.md +0 -25
- authpi_admin-0.3.0/authpi_admin/generated/resources/organizations.py +0 -244
- authpi_admin-0.3.0/tests/test_client.py +0 -90
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/errors.py +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/resources/__init__.py +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/generated/scopes/__init__.py +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/pagination.py +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/py.typed +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/authpi_admin/user_agent.py +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/tests/__init__.py +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/tests/test_errors.py +0 -0
- {authpi_admin-0.3.0 → authpi_admin-0.7.0}/tests/test_pagination.py +0 -0
|
@@ -13,6 +13,7 @@ vite.config.js.timestamp-*
|
|
|
13
13
|
vite.config.ts.timestamp-*
|
|
14
14
|
oracle.sql
|
|
15
15
|
dist
|
|
16
|
+
dist-templates/
|
|
16
17
|
storybook-static
|
|
17
18
|
target
|
|
18
19
|
filter/
|
|
@@ -29,6 +30,7 @@ test-adyen/
|
|
|
29
30
|
stripe-authpi/
|
|
30
31
|
stripe-app/
|
|
31
32
|
secrets.json
|
|
33
|
+
*.secrets.json
|
|
32
34
|
.idea
|
|
33
35
|
.vscode
|
|
34
36
|
*.pem
|
|
@@ -49,3 +51,10 @@ worker-configuration.d.ts
|
|
|
49
51
|
.worktrees/
|
|
50
52
|
docs/plans/
|
|
51
53
|
docs/superpowers/
|
|
54
|
+
docs/.obsidian/
|
|
55
|
+
|
|
56
|
+
# Operator-rendered ops-db bootstrap files — carry environment-specific
|
|
57
|
+
# captured `agt_*` IDs from the Step 3 bootstrap. The .template stays in
|
|
58
|
+
# git as the canonical structure; rendered copies stay local.
|
|
59
|
+
d1-databases/ops-db/0005_bootstrap_capability_matrix.sql
|
|
60
|
+
d1-databases/ops-db/0005_bootstrap_capability_matrix_prod.sql
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.7.0](https://github.com/arbfay/authpi/compare/authpi-admin-v0.6.0...authpi-admin-v0.7.0) (2026-06-12)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* GET /v1/me — caller identity endpoint (AUT-163) ([#286](https://github.com/arbfay/authpi/issues/286)) ([b9f35dc](https://github.com/arbfay/authpi/commit/b9f35dc2fd8cca77cdd2079b240ce28dc11d35a8))
|
|
9
|
+
|
|
10
|
+
## [0.6.0](https://github.com/arbfay/authpi/compare/authpi-admin-v0.4.0...authpi-admin-v0.6.0) (2026-06-11)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### ⚠ BREAKING CHANGES
|
|
14
|
+
|
|
15
|
+
* **admin-py:** collision-split class renames (see admin-ts) and issuer-scope org-subresource accessors moved to the organization scope; the removed accessors 404'd on every call, so no working integration is affected.
|
|
16
|
+
* **admin-ts:** cross-scope name collisions split generated classes (ApiKeysResource -> AccountApiKeysResource, TokensResource -> AccountTokensResource, SessionsResource -> IssuerSessionsResource / UserSessionsResource, VerifiersResource -> AgentVerifiersResource / UserVerifiersResource), and the issuer-scope groups/invitations/ members/domains accessors moved to the new organization scope. The removed accessors produced 404s on every call, so no working integration is affected.
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* **admin-py:** regenerate from corrected spec — org scope, restored endpoints, credits ([2cd96b5](https://github.com/arbfay/authpi/commit/2cd96b569ab9ddff8589fd8e786a16052af4c7e5))
|
|
21
|
+
* **admin-ts:** regenerate from corrected spec — org scope, restored endpoints, credits ([3932b60](https://github.com/arbfay/authpi/commit/3932b609116f059dff36a73f9a5378c21d69eb3d))
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
### Bug Fixes
|
|
25
|
+
|
|
26
|
+
* **generators:** action methods carry query params and real response types ([0f515ec](https://github.com/arbfay/authpi/commit/0f515ecc1ce737f62e2826ff3106b97c35a6ce2f))
|
|
27
|
+
|
|
28
|
+
## [0.4.0](https://github.com/arbfay/authpi/compare/authpi-admin-v0.3.1...authpi-admin-v0.4.0) (2026-06-11)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### ⚠ BREAKING CHANGES
|
|
32
|
+
|
|
33
|
+
* **admin-py:** `account_id` is now required for both auth modes. The api_key-without-account_id form never produced a successful request.
|
|
34
|
+
* **admin-py:** `api_key` changed from `str` to `tuple[str, str]`. The previous form never authenticated successfully, so no working integration is affected.
|
|
35
|
+
|
|
36
|
+
### Bug Fixes
|
|
37
|
+
|
|
38
|
+
* **admin-py:** default base_url to the production API ([4351942](https://github.com/arbfay/authpi/commit/4351942f3c40d75b24e079e1d83553e6f58a3303))
|
|
39
|
+
* **admin-py:** require account_id — the API has no accounts/me alias ([58cf3a2](https://github.com/arbfay/authpi/commit/58cf3a2a1af402f7b9e20ceadd84e6015957f000))
|
|
40
|
+
* **admin-py:** send API keys as HTTP Basic id:secret pair ([aee64f7](https://github.com/arbfay/authpi/commit/aee64f777abb18ecb4578c178cf55a4c72abb046))
|
|
41
|
+
|
|
42
|
+
## [0.3.1](https://github.com/arbfay/authpi/compare/authpi-admin-v0.3.0...authpi-admin-v0.3.1) (2026-06-10)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
### Bug Fixes
|
|
46
|
+
|
|
47
|
+
* **admin-py:** unwrap the single-resource data envelope in generated methods ([d63ee44](https://github.com/arbfay/authpi/commit/d63ee44d9db64e46dbfbfc5da66c4a1aaeacf609))
|
|
48
|
+
|
|
49
|
+
## [0.3.0](https://github.com/arbfay/authpi/compare/authpi-admin-v0.2.0...authpi-admin-v0.3.0) (2026-04-17)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
### Features
|
|
53
|
+
|
|
54
|
+
* **sdks:** set custom User-Agent for SDK identification ([#213](https://github.com/arbfay/authpi/issues/213)) ([dec716c](https://github.com/arbfay/authpi/commit/dec716c99d877e99496f45c69f3625341bb2bc2e))
|
|
55
|
+
|
|
56
|
+
## [0.2.0](https://github.com/arbfay/authpi/compare/authpi-admin-v0.1.0...authpi-admin-v0.2.0) (2026-04-13)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
### ⚠ BREAKING CHANGES
|
|
60
|
+
|
|
61
|
+
* All generated type interfaces now use snake_case field names. Consumers referencing camelCase fields (e.g., Client.issuerId) must update to snake_case (Client.issuer_id).
|
|
62
|
+
|
|
63
|
+
### Features
|
|
64
|
+
|
|
65
|
+
* Admin SDK code generator + Python SDK (authpi-admin) ([#186](https://github.com/arbfay/authpi/issues/186)) ([3eb5055](https://github.com/arbfay/authpi/commit/3eb505579f34a49769e0b01eb59f6d571ad74571))
|
|
66
|
+
* SDK convergence — extend @authpi/admin, migrate console, security fixes ([#197](https://github.com/arbfay/authpi/issues/197)) ([3d4896f](https://github.com/arbfay/authpi/commit/3d4896fa60381542d99a66e394809d128bf6bcf0))
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
### Bug Fixes
|
|
70
|
+
|
|
71
|
+
* **admin:** fix custom action paths, security hardening for TS and Python SDKs ([#189](https://github.com/arbfay/authpi/issues/189)) ([bf5e0b4](https://github.com/arbfay/authpi/commit/bf5e0b430a76173a1a1b4ff188095a96b337a74c))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: authpi-admin
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.0
|
|
4
4
|
Summary: Official Python Admin SDK for AuthPI Core API
|
|
5
5
|
Project-URL: Homepage, https://authpi.com
|
|
6
6
|
Project-URL: Documentation, https://docs.authpi.com/sdk/python/admin
|
|
@@ -47,7 +47,7 @@ pip install authpi-admin
|
|
|
47
47
|
```python
|
|
48
48
|
from authpi_admin import AuthPIAdmin
|
|
49
49
|
|
|
50
|
-
async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
50
|
+
async with AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx") as admin:
|
|
51
51
|
# List issuers
|
|
52
52
|
page = await admin.issuers.list(limit=10)
|
|
53
53
|
print(page.data)
|
|
@@ -70,8 +70,10 @@ async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
|
70
70
|
|
|
71
71
|
### API Key (default)
|
|
72
72
|
|
|
73
|
+
API keys are issued as an **id + secret pair** — both parts are shown once when you create the key in the dashboard. The SDK sends them as HTTP Basic credentials (`key_id:key_secret`):
|
|
74
|
+
|
|
73
75
|
```python
|
|
74
|
-
admin = AuthPIAdmin(api_key="key_xxx")
|
|
76
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx")
|
|
75
77
|
```
|
|
76
78
|
|
|
77
79
|
### Bearer Token
|
|
@@ -101,6 +103,24 @@ admin = AuthPIAdmin(
|
|
|
101
103
|
|
|
102
104
|
When `on_token_expired` is provided, the SDK calls it on 401 responses and retries the request with the new token. Concurrent 401s are deduplicated — only one refresh runs at a time.
|
|
103
105
|
|
|
106
|
+
### Account resolution
|
|
107
|
+
|
|
108
|
+
`account_id` is optional. When omitted, the SDK resolves it once via `GET /v1/me` on the first request and caches it — an API key always maps to exactly one account:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
async with AuthPIAdmin(api_key=("key_xxx", "your_key_secret")) as admin:
|
|
112
|
+
issuers = await admin.issuers.list() # resolves the account transparently
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
If the credential can act on zero or multiple accounts (possible with user bearer tokens), the SDK raises a `ConfigurationError` naming the choices — pass `account_id` explicitly in that case.
|
|
116
|
+
|
|
117
|
+
You can also ask directly who the API considers you to be:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
me = await admin.whoami()
|
|
121
|
+
# {"type": "api_key", "key_id": "key_...", "issuer_id": "i_...", "accounts": [{"account_id": ..., "org_id": ..., "scopes": [...]}]}
|
|
122
|
+
```
|
|
123
|
+
|
|
104
124
|
## Scoped Client Pattern
|
|
105
125
|
|
|
106
126
|
The SDK mirrors the API's resource hierarchy. Navigate with chained accessors:
|
|
@@ -119,6 +139,10 @@ await iss.clients.list()
|
|
|
119
139
|
await iss.organizations.list()
|
|
120
140
|
|
|
121
141
|
# User scope (nested under issuer)
|
|
142
|
+
org = admin.issuer("iss_xxx").organization("org_xxx")
|
|
143
|
+
await org.members.list()
|
|
144
|
+
await org.sso.add_domain(domain="acme.com")
|
|
145
|
+
|
|
122
146
|
usr = admin.issuer("iss_xxx").user("usr_xxx")
|
|
123
147
|
await usr.get()
|
|
124
148
|
await usr.sessions.list()
|
|
@@ -160,14 +184,15 @@ Read-only requests (GET, HEAD, OPTIONS) are automatically retried on 429, 502, 5
|
|
|
160
184
|
|
|
161
185
|
```python
|
|
162
186
|
# Default: retries enabled (3 attempts, 1s base delay, exponential backoff)
|
|
163
|
-
admin = AuthPIAdmin(api_key="key_xxx")
|
|
187
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx")
|
|
164
188
|
|
|
165
189
|
# Disable retries
|
|
166
|
-
admin = AuthPIAdmin(api_key="key_xxx", retries=False)
|
|
190
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), retries=False)
|
|
167
191
|
|
|
168
192
|
# Custom retry config
|
|
169
193
|
admin = AuthPIAdmin(
|
|
170
|
-
api_key="key_xxx",
|
|
194
|
+
api_key=("key_xxx", "your_key_secret"),
|
|
195
|
+
account_id="acc_xxx",
|
|
171
196
|
retries={"limit": 5, "delay": 0.5, "backoff": "linear"},
|
|
172
197
|
)
|
|
173
198
|
```
|
|
@@ -239,8 +264,9 @@ except AuthenticationError:
|
|
|
239
264
|
from authpi_admin import AuthPIAdmin
|
|
240
265
|
|
|
241
266
|
admin = AuthPIAdmin(
|
|
242
|
-
api_key="key_xxx",
|
|
243
|
-
|
|
267
|
+
api_key=("key_xxx", "your_key_secret"), # or access_token instead
|
|
268
|
+
account_id="acc_xxx", # optional — resolved via GET /v1/me when omitted
|
|
269
|
+
base_url="https://api.authpi.com", # default
|
|
244
270
|
timeout=30.0, # default, in seconds
|
|
245
271
|
default_headers={"X-Custom": "value"}, # optional extra headers
|
|
246
272
|
retries=True, # default (or False, or dict)
|
|
@@ -256,7 +282,7 @@ import httpx
|
|
|
256
282
|
from authpi_admin import AuthPIAdmin
|
|
257
283
|
|
|
258
284
|
async with httpx.AsyncClient(proxies="http://proxy:8080") as http:
|
|
259
|
-
admin = AuthPIAdmin(api_key="key_xxx", http_client=http)
|
|
285
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), http_client=http)
|
|
260
286
|
await admin.issuers.list()
|
|
261
287
|
```
|
|
262
288
|
|
|
@@ -265,7 +291,7 @@ async with httpx.AsyncClient(proxies="http://proxy:8080") as http:
|
|
|
265
291
|
The SDK supports async context managers for clean resource cleanup:
|
|
266
292
|
|
|
267
293
|
```python
|
|
268
|
-
async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
294
|
+
async with AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx") as admin:
|
|
269
295
|
await admin.issuers.list()
|
|
270
296
|
# httpx client is closed automatically
|
|
271
297
|
```
|
|
@@ -273,7 +299,7 @@ async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
|
273
299
|
Or close manually:
|
|
274
300
|
|
|
275
301
|
```python
|
|
276
|
-
admin = AuthPIAdmin(api_key="key_xxx")
|
|
302
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx")
|
|
277
303
|
try:
|
|
278
304
|
await admin.issuers.list()
|
|
279
305
|
finally:
|
|
@@ -15,7 +15,7 @@ pip install authpi-admin
|
|
|
15
15
|
```python
|
|
16
16
|
from authpi_admin import AuthPIAdmin
|
|
17
17
|
|
|
18
|
-
async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
18
|
+
async with AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx") as admin:
|
|
19
19
|
# List issuers
|
|
20
20
|
page = await admin.issuers.list(limit=10)
|
|
21
21
|
print(page.data)
|
|
@@ -38,8 +38,10 @@ async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
|
38
38
|
|
|
39
39
|
### API Key (default)
|
|
40
40
|
|
|
41
|
+
API keys are issued as an **id + secret pair** — both parts are shown once when you create the key in the dashboard. The SDK sends them as HTTP Basic credentials (`key_id:key_secret`):
|
|
42
|
+
|
|
41
43
|
```python
|
|
42
|
-
admin = AuthPIAdmin(api_key="key_xxx")
|
|
44
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx")
|
|
43
45
|
```
|
|
44
46
|
|
|
45
47
|
### Bearer Token
|
|
@@ -69,6 +71,24 @@ admin = AuthPIAdmin(
|
|
|
69
71
|
|
|
70
72
|
When `on_token_expired` is provided, the SDK calls it on 401 responses and retries the request with the new token. Concurrent 401s are deduplicated — only one refresh runs at a time.
|
|
71
73
|
|
|
74
|
+
### Account resolution
|
|
75
|
+
|
|
76
|
+
`account_id` is optional. When omitted, the SDK resolves it once via `GET /v1/me` on the first request and caches it — an API key always maps to exactly one account:
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
async with AuthPIAdmin(api_key=("key_xxx", "your_key_secret")) as admin:
|
|
80
|
+
issuers = await admin.issuers.list() # resolves the account transparently
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
If the credential can act on zero or multiple accounts (possible with user bearer tokens), the SDK raises a `ConfigurationError` naming the choices — pass `account_id` explicitly in that case.
|
|
84
|
+
|
|
85
|
+
You can also ask directly who the API considers you to be:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
me = await admin.whoami()
|
|
89
|
+
# {"type": "api_key", "key_id": "key_...", "issuer_id": "i_...", "accounts": [{"account_id": ..., "org_id": ..., "scopes": [...]}]}
|
|
90
|
+
```
|
|
91
|
+
|
|
72
92
|
## Scoped Client Pattern
|
|
73
93
|
|
|
74
94
|
The SDK mirrors the API's resource hierarchy. Navigate with chained accessors:
|
|
@@ -87,6 +107,10 @@ await iss.clients.list()
|
|
|
87
107
|
await iss.organizations.list()
|
|
88
108
|
|
|
89
109
|
# User scope (nested under issuer)
|
|
110
|
+
org = admin.issuer("iss_xxx").organization("org_xxx")
|
|
111
|
+
await org.members.list()
|
|
112
|
+
await org.sso.add_domain(domain="acme.com")
|
|
113
|
+
|
|
90
114
|
usr = admin.issuer("iss_xxx").user("usr_xxx")
|
|
91
115
|
await usr.get()
|
|
92
116
|
await usr.sessions.list()
|
|
@@ -128,14 +152,15 @@ Read-only requests (GET, HEAD, OPTIONS) are automatically retried on 429, 502, 5
|
|
|
128
152
|
|
|
129
153
|
```python
|
|
130
154
|
# Default: retries enabled (3 attempts, 1s base delay, exponential backoff)
|
|
131
|
-
admin = AuthPIAdmin(api_key="key_xxx")
|
|
155
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx")
|
|
132
156
|
|
|
133
157
|
# Disable retries
|
|
134
|
-
admin = AuthPIAdmin(api_key="key_xxx", retries=False)
|
|
158
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), retries=False)
|
|
135
159
|
|
|
136
160
|
# Custom retry config
|
|
137
161
|
admin = AuthPIAdmin(
|
|
138
|
-
api_key="key_xxx",
|
|
162
|
+
api_key=("key_xxx", "your_key_secret"),
|
|
163
|
+
account_id="acc_xxx",
|
|
139
164
|
retries={"limit": 5, "delay": 0.5, "backoff": "linear"},
|
|
140
165
|
)
|
|
141
166
|
```
|
|
@@ -207,8 +232,9 @@ except AuthenticationError:
|
|
|
207
232
|
from authpi_admin import AuthPIAdmin
|
|
208
233
|
|
|
209
234
|
admin = AuthPIAdmin(
|
|
210
|
-
api_key="key_xxx",
|
|
211
|
-
|
|
235
|
+
api_key=("key_xxx", "your_key_secret"), # or access_token instead
|
|
236
|
+
account_id="acc_xxx", # optional — resolved via GET /v1/me when omitted
|
|
237
|
+
base_url="https://api.authpi.com", # default
|
|
212
238
|
timeout=30.0, # default, in seconds
|
|
213
239
|
default_headers={"X-Custom": "value"}, # optional extra headers
|
|
214
240
|
retries=True, # default (or False, or dict)
|
|
@@ -224,7 +250,7 @@ import httpx
|
|
|
224
250
|
from authpi_admin import AuthPIAdmin
|
|
225
251
|
|
|
226
252
|
async with httpx.AsyncClient(proxies="http://proxy:8080") as http:
|
|
227
|
-
admin = AuthPIAdmin(api_key="key_xxx", http_client=http)
|
|
253
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), http_client=http)
|
|
228
254
|
await admin.issuers.list()
|
|
229
255
|
```
|
|
230
256
|
|
|
@@ -233,7 +259,7 @@ async with httpx.AsyncClient(proxies="http://proxy:8080") as http:
|
|
|
233
259
|
The SDK supports async context managers for clean resource cleanup:
|
|
234
260
|
|
|
235
261
|
```python
|
|
236
|
-
async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
262
|
+
async with AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx") as admin:
|
|
237
263
|
await admin.issuers.list()
|
|
238
264
|
# httpx client is closed automatically
|
|
239
265
|
```
|
|
@@ -241,7 +267,7 @@ async with AuthPIAdmin(api_key="key_xxx") as admin:
|
|
|
241
267
|
Or close manually:
|
|
242
268
|
|
|
243
269
|
```python
|
|
244
|
-
admin = AuthPIAdmin(api_key="key_xxx")
|
|
270
|
+
admin = AuthPIAdmin(api_key=("key_xxx", "your_key_secret"), account_id="acc_xxx")
|
|
245
271
|
try:
|
|
246
272
|
await admin.issuers.list()
|
|
247
273
|
finally:
|
|
@@ -2,23 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
from collections.abc import Awaitable, Callable
|
|
5
6
|
from functools import cached_property
|
|
6
|
-
from typing import TYPE_CHECKING, Any
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
7
8
|
|
|
8
9
|
import httpx
|
|
9
10
|
|
|
10
11
|
from authpi_admin.errors import ConfigurationError
|
|
11
|
-
from authpi_admin.
|
|
12
|
+
from authpi_admin.generated.resources.account_api_keys import AccountApiKeysResource
|
|
13
|
+
from authpi_admin.generated.resources.account_tokens import AccountTokensResource
|
|
12
14
|
from authpi_admin.generated.resources.accounts import AccountsResource
|
|
13
|
-
from authpi_admin.generated.resources.
|
|
15
|
+
from authpi_admin.generated.resources.credits import CreditsResource
|
|
14
16
|
from authpi_admin.generated.resources.domains import DomainsResource
|
|
15
17
|
from authpi_admin.generated.resources.events import EventsResource
|
|
16
18
|
from authpi_admin.generated.resources.issuers import IssuersResource
|
|
17
19
|
from authpi_admin.generated.resources.notes import NotesResource
|
|
18
|
-
from authpi_admin.generated.resources.tokens import TokensResource
|
|
19
20
|
from authpi_admin.generated.resources.webhooks import WebhooksResource
|
|
20
21
|
from authpi_admin.generated.scopes.issuer_scope import IssuerScope
|
|
21
22
|
from authpi_admin.generated.scopes.webhook_scope import WebhookScope
|
|
23
|
+
from authpi_admin.http_client import UNRESOLVED_ACCOUNT_ID, HttpClient
|
|
22
24
|
|
|
23
25
|
if TYPE_CHECKING:
|
|
24
26
|
from types import TracebackType
|
|
@@ -27,8 +29,11 @@ if TYPE_CHECKING:
|
|
|
27
29
|
class AuthPIAdmin:
|
|
28
30
|
"""Entry-point client for the AuthPI Core API.
|
|
29
31
|
|
|
30
|
-
Usage with API key:
|
|
31
|
-
async with AuthPIAdmin(
|
|
32
|
+
Usage with API key (id + secret pair, sent as HTTP Basic):
|
|
33
|
+
async with AuthPIAdmin(
|
|
34
|
+
api_key=("key_xxx", "your_key_secret"),
|
|
35
|
+
account_id="acc_xxx",
|
|
36
|
+
) as admin:
|
|
32
37
|
users = await admin.issuer("iss_xxx").users.list()
|
|
33
38
|
|
|
34
39
|
Usage with bearer token:
|
|
@@ -42,11 +47,11 @@ class AuthPIAdmin:
|
|
|
42
47
|
def __init__(
|
|
43
48
|
self,
|
|
44
49
|
*,
|
|
45
|
-
api_key: str | None = None,
|
|
50
|
+
api_key: tuple[str, str] | None = None,
|
|
46
51
|
access_token: str | None = None,
|
|
47
52
|
on_token_expired: Callable[[], Awaitable[dict[str, str]]] | None = None,
|
|
48
53
|
account_id: str | None = None,
|
|
49
|
-
base_url: str = "https://api.authpi.
|
|
54
|
+
base_url: str = "https://api.authpi.com",
|
|
50
55
|
http_client: httpx.AsyncClient | None = None,
|
|
51
56
|
timeout: float = 30.0,
|
|
52
57
|
default_headers: dict[str, str] | None = None,
|
|
@@ -56,10 +61,7 @@ class AuthPIAdmin:
|
|
|
56
61
|
raise ConfigurationError("Either api_key or access_token is required")
|
|
57
62
|
if api_key and access_token:
|
|
58
63
|
raise ConfigurationError("Provide either api_key or access_token, not both")
|
|
59
|
-
if access_token and not account_id:
|
|
60
|
-
raise ConfigurationError("account_id is required when using access_token")
|
|
61
64
|
|
|
62
|
-
resolved_account_id = account_id or "me"
|
|
63
65
|
self._http = HttpClient(
|
|
64
66
|
base_url=base_url,
|
|
65
67
|
api_key=api_key,
|
|
@@ -70,13 +72,23 @@ class AuthPIAdmin:
|
|
|
70
72
|
default_headers=default_headers,
|
|
71
73
|
retries=retries,
|
|
72
74
|
)
|
|
73
|
-
|
|
75
|
+
# Without an explicit account_id, paths carry a placeholder that the
|
|
76
|
+
# HttpClient substitutes at request time after resolving GET /v1/me once.
|
|
77
|
+
self._base_path = f"/v1/accounts/{account_id or UNRESOLVED_ACCOUNT_ID}"
|
|
74
78
|
|
|
75
79
|
@property
|
|
76
80
|
def http(self) -> HttpClient:
|
|
77
81
|
"""The underlying HTTP client (for advanced use)."""
|
|
78
82
|
return self._http
|
|
79
83
|
|
|
84
|
+
async def whoami(self) -> dict[str, Any]:
|
|
85
|
+
"""Who does the Core API consider this credential to be?
|
|
86
|
+
|
|
87
|
+
Returns the credential type and the verified accounts it can act on
|
|
88
|
+
(``GET /v1/me``).
|
|
89
|
+
"""
|
|
90
|
+
return await self._http.whoami()
|
|
91
|
+
|
|
80
92
|
# --- Account-level resource accessors ---
|
|
81
93
|
|
|
82
94
|
@cached_property
|
|
@@ -85,9 +97,14 @@ class AuthPIAdmin:
|
|
|
85
97
|
return AccountsResource(self._http, "/v1/accounts")
|
|
86
98
|
|
|
87
99
|
@cached_property
|
|
88
|
-
def api_keys(self) ->
|
|
100
|
+
def api_keys(self) -> AccountApiKeysResource:
|
|
89
101
|
"""Manage account-level API keys."""
|
|
90
|
-
return
|
|
102
|
+
return AccountApiKeysResource(self._http, f"{self._base_path}/api-keys")
|
|
103
|
+
|
|
104
|
+
@cached_property
|
|
105
|
+
def credits(self) -> CreditsResource:
|
|
106
|
+
"""Account credit balance, ledger, and top-ups."""
|
|
107
|
+
return CreditsResource(self._http, f"{self._base_path}/credits")
|
|
91
108
|
|
|
92
109
|
@cached_property
|
|
93
110
|
def domains(self) -> DomainsResource:
|
|
@@ -110,9 +127,9 @@ class AuthPIAdmin:
|
|
|
110
127
|
return NotesResource(self._http, f"{self._base_path}/notes")
|
|
111
128
|
|
|
112
129
|
@cached_property
|
|
113
|
-
def tokens(self) ->
|
|
130
|
+
def tokens(self) -> AccountTokensResource:
|
|
114
131
|
"""Manage account-level tokens."""
|
|
115
|
-
return
|
|
132
|
+
return AccountTokensResource(self._http, f"{self._base_path}/tokens")
|
|
116
133
|
|
|
117
134
|
@cached_property
|
|
118
135
|
def webhooks(self) -> WebhooksResource:
|
|
@@ -2,27 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
from .models import * # noqa: F401,F403
|
|
4
4
|
from .resources.accounts import AccountsResource # noqa: F401
|
|
5
|
-
from .resources.
|
|
5
|
+
from .resources.account_api_keys import AccountApiKeysResource # noqa: F401
|
|
6
|
+
from .resources.credits import CreditsResource # noqa: F401
|
|
6
7
|
from .resources.domains import DomainsResource # noqa: F401
|
|
7
8
|
from .resources.events import EventsResource # noqa: F401
|
|
8
9
|
from .resources.issuers import IssuersResource # noqa: F401
|
|
9
10
|
from .resources.agents import AgentsResource # noqa: F401
|
|
10
|
-
from .resources.
|
|
11
|
+
from .resources.agent_verifiers import AgentVerifiersResource # noqa: F401
|
|
11
12
|
from .resources.approvals import ApprovalsResource # noqa: F401
|
|
12
13
|
from .resources.auth_methods import AuthMethodsResource # noqa: F401
|
|
13
14
|
from .resources.clients import ClientsResource # noqa: F401
|
|
14
15
|
from .resources.organizations import OrganizationsResource # noqa: F401
|
|
16
|
+
from .resources.organization_api_keys import OrganizationApiKeysResource # noqa: F401
|
|
15
17
|
from .resources.groups import GroupsResource # noqa: F401
|
|
16
18
|
from .resources.invitations import InvitationsResource # noqa: F401
|
|
17
19
|
from .resources.members import MembersResource # noqa: F401
|
|
18
|
-
from .resources.
|
|
20
|
+
from .resources.sso import SsoResource # noqa: F401
|
|
21
|
+
from .resources.issuer_sessions import IssuerSessionsResource # noqa: F401
|
|
22
|
+
from .resources.issuer_tokens import IssuerTokensResource # noqa: F401
|
|
19
23
|
from .resources.users import UsersResource # noqa: F401
|
|
20
|
-
from .resources.
|
|
24
|
+
from .resources.user_sessions import UserSessionsResource # noqa: F401
|
|
25
|
+
from .resources.user_tokens import UserTokensResource # noqa: F401
|
|
21
26
|
from .resources.trusted_devices import TrustedDevicesResource # noqa: F401
|
|
27
|
+
from .resources.user_verifiers import UserVerifiersResource # noqa: F401
|
|
22
28
|
from .resources.notes import NotesResource # noqa: F401
|
|
29
|
+
from .resources.payment_methods import PaymentMethodsResource # noqa: F401
|
|
30
|
+
from .resources.account_tokens import AccountTokensResource # noqa: F401
|
|
23
31
|
from .resources.webhooks import WebhooksResource # noqa: F401
|
|
24
32
|
from .resources.deliveries import DeliveriesResource # noqa: F401
|
|
25
33
|
from .scopes.issuer_scope import IssuerScope # noqa: F401
|
|
26
34
|
from .scopes.agent_scope import AgentScope # noqa: F401
|
|
35
|
+
from .scopes.organization_scope import OrganizationScope # noqa: F401
|
|
27
36
|
from .scopes.user_scope import UserScope # noqa: F401
|
|
28
37
|
from .scopes.webhook_scope import WebhookScope # noqa: F401
|