torii-backend 0.0.2__py3-none-any.whl
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.
- torii_backend/__init__.py +57 -0
- torii_backend/client.py +307 -0
- torii_backend/errors.py +42 -0
- torii_backend/fastapi.py +58 -0
- torii_backend/generated/__init__.py +64 -0
- torii_backend/generated/api/__init__.py +6 -0
- torii_backend/generated/api/server_sessions_api.py +853 -0
- torii_backend/generated/api/server_users_api.py +2017 -0
- torii_backend/generated/api_client.py +804 -0
- torii_backend/generated/api_response.py +21 -0
- torii_backend/generated/configuration.py +596 -0
- torii_backend/generated/exceptions.py +218 -0
- torii_backend/generated/models/__init__.py +23 -0
- torii_backend/generated/models/create_user_request.py +129 -0
- torii_backend/generated/models/cursor_page_response_user_response.py +106 -0
- torii_backend/generated/models/problem_detail.py +98 -0
- torii_backend/generated/models/server_user_search_request.py +128 -0
- torii_backend/generated/models/update_user_request.py +132 -0
- torii_backend/generated/models/user_response.py +164 -0
- torii_backend/generated/models/user_session_response.py +114 -0
- torii_backend/generated/py.typed +0 -0
- torii_backend/generated/rest.py +263 -0
- torii_backend/py.typed +0 -0
- torii_backend/types.py +44 -0
- torii_backend/verify.py +156 -0
- torii_backend-0.0.2.dist-info/METADATA +143 -0
- torii_backend-0.0.2.dist-info/RECORD +29 -0
- torii_backend-0.0.2.dist-info/WHEEL +4 -0
- torii_backend-0.0.2.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""torii-backend — backend SDK for torii.
|
|
2
|
+
|
|
3
|
+
Verify JWTs networklessly, call ``/api/server/v1/**`` with a secret key,
|
|
4
|
+
and (soon) verify outbound webhook signatures. Framework-agnostic; an
|
|
5
|
+
optional FastAPI dependency adapter lives in ``torii_backend.fastapi``.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from torii_backend.client import ToriiClient, create_torii_client
|
|
9
|
+
from torii_backend.errors import ToriiApiError, ToriiAuthError
|
|
10
|
+
|
|
11
|
+
# Generated data types — re-exported under stable Torii* aliases so the
|
|
12
|
+
# public surface is independent of the generator's naming.
|
|
13
|
+
from torii_backend.generated.models import (
|
|
14
|
+
CreateUserRequest as ToriiCreateUserInput,
|
|
15
|
+
)
|
|
16
|
+
from torii_backend.generated.models import (
|
|
17
|
+
CursorPageResponseUserResponse as ToriiCursorPageUser,
|
|
18
|
+
)
|
|
19
|
+
from torii_backend.generated.models import (
|
|
20
|
+
ProblemDetail as ToriiProblemDetail,
|
|
21
|
+
)
|
|
22
|
+
from torii_backend.generated.models import (
|
|
23
|
+
UpdateUserRequest as ToriiUpdateUserInput,
|
|
24
|
+
)
|
|
25
|
+
from torii_backend.generated.models import (
|
|
26
|
+
UserResponse as ToriiUser,
|
|
27
|
+
)
|
|
28
|
+
from torii_backend.generated.models import (
|
|
29
|
+
UserSessionResponse as ToriiSession,
|
|
30
|
+
)
|
|
31
|
+
from torii_backend.types import ToriiAuth
|
|
32
|
+
from torii_backend.verify import (
|
|
33
|
+
authenticate_request,
|
|
34
|
+
clear_jwks_cache_for_tests,
|
|
35
|
+
verify_token,
|
|
36
|
+
verify_webhook,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"ToriiApiError",
|
|
41
|
+
"ToriiAuth",
|
|
42
|
+
"ToriiAuthError",
|
|
43
|
+
"ToriiClient",
|
|
44
|
+
"ToriiCreateUserInput",
|
|
45
|
+
"ToriiCursorPageUser",
|
|
46
|
+
"ToriiProblemDetail",
|
|
47
|
+
"ToriiSession",
|
|
48
|
+
"ToriiUpdateUserInput",
|
|
49
|
+
"ToriiUser",
|
|
50
|
+
"authenticate_request",
|
|
51
|
+
"clear_jwks_cache_for_tests",
|
|
52
|
+
"create_torii_client",
|
|
53
|
+
"verify_token",
|
|
54
|
+
"verify_webhook",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
__version__ = "0.0.1"
|
torii_backend/client.py
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
"""ToriiClient — entry point for the REST surface.
|
|
2
|
+
|
|
3
|
+
Wraps the openapi-generator output under ``torii_backend.generated``
|
|
4
|
+
behind a thin, hand-written facade so callers see ergonomic methods
|
|
5
|
+
instead of the generator's verbose ``_request_timeout``/``_headers``/...
|
|
6
|
+
parameter sprawl.
|
|
7
|
+
|
|
8
|
+
Types and endpoints come from the OpenAPI spec via ``openapi-generator``;
|
|
9
|
+
only the wrapper + auth helpers are hand-written. When the spec grows,
|
|
10
|
+
regenerate and add a one-line wrapper method per new endpoint.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from datetime import date, datetime
|
|
16
|
+
from typing import Any
|
|
17
|
+
from uuid import UUID
|
|
18
|
+
|
|
19
|
+
from torii_backend.errors import ToriiApiError
|
|
20
|
+
from torii_backend.generated import (
|
|
21
|
+
ApiClient,
|
|
22
|
+
ApiException,
|
|
23
|
+
Configuration,
|
|
24
|
+
ServerSessionsApi,
|
|
25
|
+
ServerUsersApi,
|
|
26
|
+
)
|
|
27
|
+
from torii_backend.generated.models import (
|
|
28
|
+
CreateUserRequest,
|
|
29
|
+
CursorPageResponseUserResponse,
|
|
30
|
+
ServerUserSearchRequest,
|
|
31
|
+
UpdateUserRequest,
|
|
32
|
+
UserResponse,
|
|
33
|
+
UserSessionResponse,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
# Sentinel to distinguish "argument not passed" from "explicit None" in the
|
|
37
|
+
# keyword-arg flavour of ``users.update`` / ``users.create``. We must NOT use
|
|
38
|
+
# ``None`` for this purpose because PATCH semantics require ``None`` to mean
|
|
39
|
+
# "clear this field on the server". See README "PATCH semantics".
|
|
40
|
+
_UNSET: Any = object()
|
|
41
|
+
|
|
42
|
+
DEFAULT_API_URL = "https://api.torii.so"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class UsersClient:
|
|
46
|
+
def __init__(self, api: ServerUsersApi) -> None:
|
|
47
|
+
self._api = api
|
|
48
|
+
|
|
49
|
+
def list(
|
|
50
|
+
self,
|
|
51
|
+
*,
|
|
52
|
+
limit: int | None = None,
|
|
53
|
+
cursor: str | UUID | None = None,
|
|
54
|
+
name: str | None = None,
|
|
55
|
+
email: str | None = None,
|
|
56
|
+
statuses: list[str] | None = None,
|
|
57
|
+
created_after: str | datetime | None = None,
|
|
58
|
+
created_before: str | datetime | None = None,
|
|
59
|
+
) -> CursorPageResponseUserResponse:
|
|
60
|
+
"""Search users. Server-side cursor-paginated; loop with the
|
|
61
|
+
returned ``next_cursor`` until ``has_more`` is False."""
|
|
62
|
+
search = ServerUserSearchRequest.from_dict(
|
|
63
|
+
{
|
|
64
|
+
"name": name,
|
|
65
|
+
"email": email,
|
|
66
|
+
"statuses": statuses,
|
|
67
|
+
"createdAfter": created_after,
|
|
68
|
+
"createdBefore": created_before,
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
with _translate_api_error():
|
|
72
|
+
return self._api.search_users(
|
|
73
|
+
limit=limit,
|
|
74
|
+
cursor=_coerce_uuid(cursor),
|
|
75
|
+
server_user_search_request=search,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def get(self, user_id: str | UUID) -> UserResponse:
|
|
79
|
+
with _translate_api_error():
|
|
80
|
+
return self._api.get_user(_coerce_uuid(user_id))
|
|
81
|
+
|
|
82
|
+
def create(
|
|
83
|
+
self,
|
|
84
|
+
input: CreateUserRequest | dict[str, Any] | None = None,
|
|
85
|
+
*,
|
|
86
|
+
email: str | None = _UNSET,
|
|
87
|
+
password: str | None = _UNSET,
|
|
88
|
+
name: str | None = _UNSET,
|
|
89
|
+
phone: str | None = _UNSET,
|
|
90
|
+
address: str | None = _UNSET,
|
|
91
|
+
date_of_birth: str | date | None = _UNSET,
|
|
92
|
+
) -> UserResponse:
|
|
93
|
+
"""Create a user.
|
|
94
|
+
|
|
95
|
+
Two call shapes:
|
|
96
|
+
* Pass a ``CreateUserRequest`` / dict positionally; OR
|
|
97
|
+
* Pass keyword args directly (``email=...``, ``name=...``, etc.).
|
|
98
|
+
"""
|
|
99
|
+
if input is not None:
|
|
100
|
+
body = (
|
|
101
|
+
input
|
|
102
|
+
if isinstance(input, CreateUserRequest)
|
|
103
|
+
else CreateUserRequest.from_dict(input)
|
|
104
|
+
)
|
|
105
|
+
else:
|
|
106
|
+
kwargs = {
|
|
107
|
+
"email": email,
|
|
108
|
+
"password": password,
|
|
109
|
+
"name": name,
|
|
110
|
+
"phone": phone,
|
|
111
|
+
"address": address,
|
|
112
|
+
"dateOfBirth": date_of_birth,
|
|
113
|
+
}
|
|
114
|
+
body = CreateUserRequest.model_validate(
|
|
115
|
+
{k: v for k, v in kwargs.items() if v is not _UNSET}
|
|
116
|
+
)
|
|
117
|
+
with _translate_api_error():
|
|
118
|
+
return self._api.create_user(body)
|
|
119
|
+
|
|
120
|
+
def update(
|
|
121
|
+
self,
|
|
122
|
+
user_id: str | UUID,
|
|
123
|
+
input: UpdateUserRequest | dict[str, Any] | None = None,
|
|
124
|
+
*,
|
|
125
|
+
name: str | None = _UNSET,
|
|
126
|
+
phone: str | None = _UNSET,
|
|
127
|
+
locale: str | None = _UNSET,
|
|
128
|
+
address: str | None = _UNSET,
|
|
129
|
+
date_of_birth: str | date | None = _UNSET,
|
|
130
|
+
) -> UserResponse:
|
|
131
|
+
"""Patch a user.
|
|
132
|
+
|
|
133
|
+
Tri-state PATCH semantics — only fields the caller explicitly set
|
|
134
|
+
are sent to the server:
|
|
135
|
+
* not passed → omitted from the JSON body → server leaves alone
|
|
136
|
+
* ``None`` → emitted as ``null`` → server clears
|
|
137
|
+
* value → emitted with value → server updates
|
|
138
|
+
|
|
139
|
+
Two call shapes:
|
|
140
|
+
* Pass an ``UpdateUserRequest`` / dict positionally; the request
|
|
141
|
+
model's ``model_fields_set`` drives which keys are sent; OR
|
|
142
|
+
* Pass keyword args directly (``name="Ada"``, ``phone=None``, ...).
|
|
143
|
+
Only the kwargs you pass appear on the wire.
|
|
144
|
+
"""
|
|
145
|
+
if input is not None:
|
|
146
|
+
model = (
|
|
147
|
+
input
|
|
148
|
+
if isinstance(input, UpdateUserRequest)
|
|
149
|
+
else UpdateUserRequest.model_validate(input)
|
|
150
|
+
)
|
|
151
|
+
else:
|
|
152
|
+
kwargs = {
|
|
153
|
+
"name": name,
|
|
154
|
+
"phone": phone,
|
|
155
|
+
"locale": locale,
|
|
156
|
+
"address": address,
|
|
157
|
+
"dateOfBirth": date_of_birth,
|
|
158
|
+
}
|
|
159
|
+
model = UpdateUserRequest.model_validate(
|
|
160
|
+
{k: v for k, v in kwargs.items() if v is not _UNSET}
|
|
161
|
+
)
|
|
162
|
+
# Build the wire body from the model's *explicitly set* fields only.
|
|
163
|
+
# We must NOT pass the model through the generated ``update_user`` —
|
|
164
|
+
# it's wrapped in pydantic's ``@validate_call`` which would re-coerce
|
|
165
|
+
# the dict back into an ``UpdateUserRequest`` and then serialize via
|
|
166
|
+
# ``to_dict()`` (which uses ``exclude_none=True``). That collapses
|
|
167
|
+
# ``phone=None`` ("clear this field") into "omit", breaking tri-state.
|
|
168
|
+
# Drop down to the serializer + ``call_api`` directly so the dict we
|
|
169
|
+
# built survives untouched.
|
|
170
|
+
body = model.model_dump(exclude_unset=True, by_alias=True)
|
|
171
|
+
with _translate_api_error():
|
|
172
|
+
return self._patch_user(_coerce_uuid(user_id), body)
|
|
173
|
+
|
|
174
|
+
def _patch_user(self, user_id: Any, body: dict[str, Any]) -> UserResponse:
|
|
175
|
+
params = self._api._update_user_serialize(
|
|
176
|
+
user_id=user_id,
|
|
177
|
+
update_user_request=body,
|
|
178
|
+
_request_auth=None,
|
|
179
|
+
_content_type=None,
|
|
180
|
+
_headers=None,
|
|
181
|
+
_host_index=0,
|
|
182
|
+
)
|
|
183
|
+
response = self._api.api_client.call_api(*params)
|
|
184
|
+
response.read()
|
|
185
|
+
return self._api.api_client.response_deserialize(
|
|
186
|
+
response_data=response,
|
|
187
|
+
response_types_map={"200": "UserResponse"},
|
|
188
|
+
).data
|
|
189
|
+
|
|
190
|
+
def delete(self, user_id: str | UUID) -> None:
|
|
191
|
+
with _translate_api_error():
|
|
192
|
+
self._api.delete_user(_coerce_uuid(user_id))
|
|
193
|
+
|
|
194
|
+
def ban(self, user_id: str | UUID) -> UserResponse:
|
|
195
|
+
with _translate_api_error():
|
|
196
|
+
return self._api.ban_user(_coerce_uuid(user_id))
|
|
197
|
+
|
|
198
|
+
def unban(self, user_id: str | UUID) -> UserResponse:
|
|
199
|
+
with _translate_api_error():
|
|
200
|
+
return self._api.unban_user(_coerce_uuid(user_id))
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class SessionsClient:
|
|
204
|
+
def __init__(self, api: ServerSessionsApi) -> None:
|
|
205
|
+
self._api = api
|
|
206
|
+
|
|
207
|
+
def list_for_user(self, user_id: str | UUID) -> list[UserSessionResponse]:
|
|
208
|
+
with _translate_api_error():
|
|
209
|
+
return self._api.list_sessions(_coerce_uuid(user_id))
|
|
210
|
+
|
|
211
|
+
def revoke_all_for_user(self, user_id: str | UUID) -> None:
|
|
212
|
+
with _translate_api_error():
|
|
213
|
+
self._api.revoke_all_sessions(_coerce_uuid(user_id))
|
|
214
|
+
|
|
215
|
+
def revoke(self, user_id: str | UUID, session_id: str | UUID) -> None:
|
|
216
|
+
with _translate_api_error():
|
|
217
|
+
self._api.revoke_session(_coerce_uuid(user_id), _coerce_uuid(session_id))
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class ToriiClient:
|
|
221
|
+
"""Construct via :func:`create_torii_client`."""
|
|
222
|
+
|
|
223
|
+
def __init__(self, api_client: ApiClient) -> None:
|
|
224
|
+
self._api_client = api_client
|
|
225
|
+
self.users = UsersClient(ServerUsersApi(api_client))
|
|
226
|
+
self.sessions = SessionsClient(ServerSessionsApi(api_client))
|
|
227
|
+
|
|
228
|
+
def close(self) -> None:
|
|
229
|
+
self._api_client.close()
|
|
230
|
+
|
|
231
|
+
def __enter__(self) -> ToriiClient:
|
|
232
|
+
return self
|
|
233
|
+
|
|
234
|
+
def __exit__(self, *_exc: Any) -> None:
|
|
235
|
+
self.close()
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def create_torii_client(
|
|
239
|
+
*,
|
|
240
|
+
secret_key: str,
|
|
241
|
+
api_url: str | None = None,
|
|
242
|
+
) -> ToriiClient:
|
|
243
|
+
"""Build a torii backend client.
|
|
244
|
+
|
|
245
|
+
Example::
|
|
246
|
+
|
|
247
|
+
torii = create_torii_client(secret_key=os.environ["TORII_SECRET_KEY"])
|
|
248
|
+
user = torii.users.get("user_abc")
|
|
249
|
+
|
|
250
|
+
``api_url`` defaults to ``https://api.torii.so``. Override for staging
|
|
251
|
+
or self-hosted.
|
|
252
|
+
"""
|
|
253
|
+
if not secret_key:
|
|
254
|
+
raise ValueError("create_torii_client: secret_key is required")
|
|
255
|
+
config = Configuration(host=(api_url or DEFAULT_API_URL).rstrip("/"))
|
|
256
|
+
api_client = ApiClient(configuration=config)
|
|
257
|
+
# Spec doesn't declare a securityScheme, so authorise via a default
|
|
258
|
+
# header on every request rather than the ``access_token`` config slot.
|
|
259
|
+
api_client.set_default_header("Authorization", f"Bearer {secret_key}")
|
|
260
|
+
return ToriiClient(api_client)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _coerce_uuid(value: str | UUID | None) -> Any:
|
|
264
|
+
"""Generated methods accept UUID for path params. We let callers pass
|
|
265
|
+
either ``str`` or ``UUID`` and coerce here."""
|
|
266
|
+
if value is None:
|
|
267
|
+
return None
|
|
268
|
+
if isinstance(value, UUID):
|
|
269
|
+
return value
|
|
270
|
+
return UUID(value)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class _translate_api_error:
|
|
274
|
+
"""Context manager: re-raise the generator's ``ApiException`` as our
|
|
275
|
+
stable ``ToriiApiError`` so callers don't depend on generator
|
|
276
|
+
internals to catch failures."""
|
|
277
|
+
|
|
278
|
+
def __enter__(self) -> _translate_api_error:
|
|
279
|
+
return self
|
|
280
|
+
|
|
281
|
+
def __exit__(self, exc_type, exc, _tb) -> bool:
|
|
282
|
+
if exc is None or not isinstance(exc, ApiException):
|
|
283
|
+
return False
|
|
284
|
+
body: Any = exc.body
|
|
285
|
+
if isinstance(body, bytes):
|
|
286
|
+
try:
|
|
287
|
+
body = body.decode("utf-8")
|
|
288
|
+
except UnicodeDecodeError:
|
|
289
|
+
body = None
|
|
290
|
+
if isinstance(body, str):
|
|
291
|
+
try:
|
|
292
|
+
import json
|
|
293
|
+
|
|
294
|
+
body = json.loads(body)
|
|
295
|
+
except ValueError:
|
|
296
|
+
pass
|
|
297
|
+
message = _extract_message(body) or f"torii {exc.status} {exc.reason or ''}".strip()
|
|
298
|
+
raise ToriiApiError(message, exc.status or 0, body) from exc
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _extract_message(body: Any) -> str | None:
|
|
302
|
+
if isinstance(body, dict):
|
|
303
|
+
for key in ("detail", "title", "message"):
|
|
304
|
+
value = body.get(key)
|
|
305
|
+
if isinstance(value, str):
|
|
306
|
+
return value
|
|
307
|
+
return None
|
torii_backend/errors.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Error types raised by torii_backend."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ToriiApiError(Exception):
|
|
9
|
+
"""Raised when ``/api/server/v1/**`` responds non-2xx.
|
|
10
|
+
|
|
11
|
+
Inspect ``status``, ``code`` (from the RFC 7807 error body if present),
|
|
12
|
+
and ``support_id`` (echoed from the server's correlation id) for
|
|
13
|
+
diagnostics. ``body`` contains the raw parsed response.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
message: str,
|
|
19
|
+
status: int,
|
|
20
|
+
body: Any = None,
|
|
21
|
+
):
|
|
22
|
+
super().__init__(message)
|
|
23
|
+
self.status = status
|
|
24
|
+
self.body = body
|
|
25
|
+
self.code: str | None = None
|
|
26
|
+
self.support_id: str | None = None
|
|
27
|
+
if isinstance(body, dict):
|
|
28
|
+
code = body.get("code")
|
|
29
|
+
support_id = body.get("supportId") or body.get("support_id")
|
|
30
|
+
if isinstance(code, str):
|
|
31
|
+
self.code = code
|
|
32
|
+
if isinstance(support_id, str):
|
|
33
|
+
self.support_id = support_id
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class ToriiAuthError(Exception):
|
|
37
|
+
"""Raised by ``verify_token`` / ``authenticate_request`` when a token
|
|
38
|
+
cannot be verified (bad signature, wrong issuer, missing claims, ...)."""
|
|
39
|
+
|
|
40
|
+
def __init__(self, message: str, cause: BaseException | None = None):
|
|
41
|
+
super().__init__(message)
|
|
42
|
+
self.cause = cause
|
torii_backend/fastapi.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""FastAPI dependency adapter.
|
|
2
|
+
|
|
3
|
+
``fastapi`` is an optional install — depend on ``torii-backend[fastapi]``
|
|
4
|
+
to pull it in. Importing this module without FastAPI raises a clear
|
|
5
|
+
error so the dependency requirement is obvious.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Callable
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
from fastapi import HTTPException, Request
|
|
14
|
+
except ImportError as e: # pragma: no cover - import guard
|
|
15
|
+
raise ImportError(
|
|
16
|
+
"torii_backend.fastapi requires fastapi. Install with `pip install torii-backend[fastapi]`."
|
|
17
|
+
) from e
|
|
18
|
+
|
|
19
|
+
from torii_backend.errors import ToriiAuthError
|
|
20
|
+
from torii_backend.types import ToriiAuth
|
|
21
|
+
from torii_backend.verify import authenticate_request
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def require_auth(
|
|
25
|
+
*,
|
|
26
|
+
issuer: str,
|
|
27
|
+
audience: str | list[str] | None = None,
|
|
28
|
+
leeway: float = 30.0,
|
|
29
|
+
) -> Callable[[Request], ToriiAuth]:
|
|
30
|
+
"""Return a FastAPI dependency that authenticates the request.
|
|
31
|
+
|
|
32
|
+
Example::
|
|
33
|
+
|
|
34
|
+
from fastapi import Depends, FastAPI
|
|
35
|
+
from torii_backend.fastapi import require_auth
|
|
36
|
+
|
|
37
|
+
app = FastAPI()
|
|
38
|
+
|
|
39
|
+
@app.get("/me")
|
|
40
|
+
def me(auth = Depends(require_auth(issuer="https://acme.torii.so"))):
|
|
41
|
+
return {"user_id": auth.user_id}
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def dependency(request: Request) -> ToriiAuth:
|
|
45
|
+
try:
|
|
46
|
+
return authenticate_request(
|
|
47
|
+
request.headers,
|
|
48
|
+
issuer=issuer,
|
|
49
|
+
audience=audience,
|
|
50
|
+
leeway=leeway,
|
|
51
|
+
)
|
|
52
|
+
except ToriiAuthError as exc:
|
|
53
|
+
raise HTTPException(
|
|
54
|
+
status_code=401,
|
|
55
|
+
detail={"code": "authentication_failed", "message": str(exc)},
|
|
56
|
+
) from exc
|
|
57
|
+
|
|
58
|
+
return dependency
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
# flake8: noqa
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
OpenAPI definition
|
|
7
|
+
|
|
8
|
+
No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
|
9
|
+
|
|
10
|
+
The version of the OpenAPI document: v0
|
|
11
|
+
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
|
12
|
+
|
|
13
|
+
Do not edit the class manually.
|
|
14
|
+
""" # noqa: E501
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
__version__ = "1.0.0"
|
|
18
|
+
|
|
19
|
+
# Define package exports
|
|
20
|
+
__all__ = [
|
|
21
|
+
"ServerSessionsApi",
|
|
22
|
+
"ServerUsersApi",
|
|
23
|
+
"ApiResponse",
|
|
24
|
+
"ApiClient",
|
|
25
|
+
"Configuration",
|
|
26
|
+
"OpenApiException",
|
|
27
|
+
"ApiTypeError",
|
|
28
|
+
"ApiValueError",
|
|
29
|
+
"ApiKeyError",
|
|
30
|
+
"ApiAttributeError",
|
|
31
|
+
"ApiException",
|
|
32
|
+
"CreateUserRequest",
|
|
33
|
+
"CursorPageResponseUserResponse",
|
|
34
|
+
"ProblemDetail",
|
|
35
|
+
"ServerUserSearchRequest",
|
|
36
|
+
"UpdateUserRequest",
|
|
37
|
+
"UserResponse",
|
|
38
|
+
"UserSessionResponse",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
# import apis into sdk package
|
|
42
|
+
from torii_backend.generated.api.server_sessions_api import ServerSessionsApi as ServerSessionsApi
|
|
43
|
+
from torii_backend.generated.api.server_users_api import ServerUsersApi as ServerUsersApi
|
|
44
|
+
|
|
45
|
+
# import ApiClient
|
|
46
|
+
from torii_backend.generated.api_response import ApiResponse as ApiResponse
|
|
47
|
+
from torii_backend.generated.api_client import ApiClient as ApiClient
|
|
48
|
+
from torii_backend.generated.configuration import Configuration as Configuration
|
|
49
|
+
from torii_backend.generated.exceptions import OpenApiException as OpenApiException
|
|
50
|
+
from torii_backend.generated.exceptions import ApiTypeError as ApiTypeError
|
|
51
|
+
from torii_backend.generated.exceptions import ApiValueError as ApiValueError
|
|
52
|
+
from torii_backend.generated.exceptions import ApiKeyError as ApiKeyError
|
|
53
|
+
from torii_backend.generated.exceptions import ApiAttributeError as ApiAttributeError
|
|
54
|
+
from torii_backend.generated.exceptions import ApiException as ApiException
|
|
55
|
+
|
|
56
|
+
# import models into sdk package
|
|
57
|
+
from torii_backend.generated.models.create_user_request import CreateUserRequest as CreateUserRequest
|
|
58
|
+
from torii_backend.generated.models.cursor_page_response_user_response import CursorPageResponseUserResponse as CursorPageResponseUserResponse
|
|
59
|
+
from torii_backend.generated.models.problem_detail import ProblemDetail as ProblemDetail
|
|
60
|
+
from torii_backend.generated.models.server_user_search_request import ServerUserSearchRequest as ServerUserSearchRequest
|
|
61
|
+
from torii_backend.generated.models.update_user_request import UpdateUserRequest as UpdateUserRequest
|
|
62
|
+
from torii_backend.generated.models.user_response import UserResponse as UserResponse
|
|
63
|
+
from torii_backend.generated.models.user_session_response import UserSessionResponse as UserSessionResponse
|
|
64
|
+
|