simple-module-auth 0.0.1__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.
auth/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Auth module — shared contracts (UserContext, deps)."""
@@ -0,0 +1,5 @@
1
+ """Auth contracts — public types for other modules."""
2
+
3
+ from auth.contracts.schemas import UserContext
4
+
5
+ __all__ = ["UserContext"]
@@ -0,0 +1,75 @@
1
+ """Auth data types shared with other modules."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import TYPE_CHECKING, Any
7
+
8
+ if TYPE_CHECKING:
9
+ # Runtime import would be circular: auth -> users -> auth.
10
+ # Only imported for type-hints, never at runtime.
11
+ from users.models import User
12
+
13
+
14
+ @dataclass
15
+ class UserContext:
16
+ """Authenticated user information for downstream handlers."""
17
+
18
+ id: str
19
+ email: str
20
+ name: str
21
+ roles: list[str] = field(default_factory=list)
22
+ tenant_id: str | None = None
23
+
24
+ @classmethod
25
+ def from_user(cls, user: User | Any) -> UserContext:
26
+ """Build a UserContext from a users.models.User with eagerly-loaded roles.
27
+
28
+ Duck-typed to avoid importing users.models at runtime — any object
29
+ exposing .id, .email, .full_name, .roles[*].name, .tenant_id works.
30
+ The caller is responsible for eager-loading roles (selectinload).
31
+ """
32
+ return cls(
33
+ id=str(user.id),
34
+ email=user.email,
35
+ name=user.full_name or user.email,
36
+ roles=[r.name for r in user.roles],
37
+ tenant_id=user.tenant_id,
38
+ )
39
+
40
+ def to_session_dict(self) -> dict[str, Any]:
41
+ """Serialize to a JSON-safe dict for the signed session cookie.
42
+
43
+ The inverse of :meth:`from_session_dict`. Adding a new field to
44
+ ``UserContext`` requires updating both methods here — not the
45
+ AuthMiddleware cache helper, which stays schema-agnostic."""
46
+ return {
47
+ "id": self.id,
48
+ "email": self.email,
49
+ "name": self.name,
50
+ "roles": list(self.roles),
51
+ "tenant_id": self.tenant_id,
52
+ }
53
+
54
+ @classmethod
55
+ def from_session_dict(cls, payload: Any) -> UserContext | None:
56
+ """Rebuild from :meth:`to_session_dict` output. Returns ``None`` on any
57
+ shape mismatch so callers can fall through to a fresh DB load."""
58
+ if not isinstance(payload, dict):
59
+ return None
60
+ try:
61
+ return cls(
62
+ id=str(payload["id"]),
63
+ email=str(payload["email"]),
64
+ name=str(payload["name"]),
65
+ roles=list(payload.get("roles") or []),
66
+ tenant_id=payload.get("tenant_id"),
67
+ )
68
+ except (KeyError, TypeError, ValueError):
69
+ return None
70
+
71
+ def has_role(self, role: str) -> bool:
72
+ return role in self.roles
73
+
74
+ def has_any_role(self, roles: list[str]) -> bool:
75
+ return bool(set(self.roles) & set(roles))
auth/deps.py ADDED
@@ -0,0 +1,60 @@
1
+ """FastAPI auth dependencies — get_current_user, require_permission."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Annotated
6
+
7
+ from fastapi import Depends, HTTPException, Request
8
+ from simple_module_hosting.i18n_deps import TranslatorDep
9
+
10
+ from auth.contracts.schemas import UserContext
11
+
12
+ _ADMIN_ROLE = "admin"
13
+
14
+
15
+ async def get_current_user(request: Request, t: TranslatorDep) -> UserContext:
16
+ """Extract the authenticated user from request state.
17
+
18
+ The auth middleware must set ``request.state.user`` before this runs.
19
+ """
20
+ user = getattr(request.state, "user", None)
21
+ if user is None:
22
+ raise HTTPException(status_code=401, detail=t.t("auth.errors.not_authenticated"))
23
+ return user
24
+
25
+
26
+ CurrentUser = Annotated[UserContext, Depends(get_current_user)]
27
+
28
+
29
+ def require_permission(*permissions: str):
30
+ """Create a dependency that checks if the user has required permissions.
31
+
32
+ Usage::
33
+
34
+ @router.post("/", dependencies=[Depends(require_permission("products.create"))])
35
+ async def create_product(...): ...
36
+ """
37
+
38
+ async def check(
39
+ request: Request,
40
+ t: TranslatorDep,
41
+ user: UserContext = Depends(get_current_user),
42
+ ):
43
+ # Admin role bypasses permission checks
44
+ if _ADMIN_ROLE in user.roles:
45
+ return
46
+
47
+ # Get permission registry from app state
48
+ perm_registry = request.app.state.sm.permissions
49
+ user_perms = perm_registry.get_permissions_for_roles(user.roles)
50
+
51
+ if not any(p in user_perms for p in permissions):
52
+ raise HTTPException(
53
+ status_code=403,
54
+ detail=t.t(
55
+ "auth.errors.missing_permission",
56
+ permissions=", ".join(permissions),
57
+ ),
58
+ )
59
+
60
+ return Depends(check)
auth/locales/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "errors": {
3
+ "not_authenticated": "Not authenticated",
4
+ "missing_permission": "Missing required permission: {permissions}"
5
+ }
6
+ }
auth/locales/es.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "errors": {
3
+ "not_authenticated": "No autenticado",
4
+ "missing_permission": "Falta el permiso requerido: {permissions}"
5
+ }
6
+ }
auth/module.py ADDED
@@ -0,0 +1,26 @@
1
+ """Auth module — shared contracts (UserContext, deps).
2
+
3
+ Intentionally minimal: this module owns the PUBLIC interface (UserContext,
4
+ get_current_user, CurrentUser, require_permission) that every other module
5
+ imports. Keeping it stable prevents churn when auth internals change.
6
+
7
+ All authentication logic (middleware, login, signup, OAuth) lives in the
8
+ users module.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import importlib.resources
14
+ from pathlib import Path
15
+
16
+ from simple_module_core.module import ModuleBase, ModuleMeta
17
+
18
+
19
+ class AuthModule(ModuleBase):
20
+ meta = ModuleMeta(
21
+ name="Auth",
22
+ route_prefix="/auth",
23
+ )
24
+
25
+ def locale_dirs(self) -> dict[str, Path]:
26
+ return {"auth": Path(str(importlib.resources.files(__package__) / "locales"))}
auth/package.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "name": "@simple-module-py/auth",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "Frontend assets for the Auth module",
6
+ "peerDependencies": {
7
+ "react": "^19.0.0",
8
+ "react-dom": "^19.0.0",
9
+ "@inertiajs/react": "^2.0.0",
10
+ "@simple-module-py/ui": "*"
11
+ },
12
+ "devDependencies": {
13
+ "@simple-module-py/tsconfig": "*"
14
+ },
15
+ "dependencies": {}
16
+ }
auth/py.typed ADDED
File without changes
@@ -0,0 +1,71 @@
1
+ Metadata-Version: 2.4
2
+ Name: simple_module_auth
3
+ Version: 0.0.1
4
+ Summary: Session-cookie authentication primitives — middleware, login/logout, redirect helpers for simple_module
5
+ Project-URL: Homepage, https://github.com/antosubash/simple_module_python
6
+ Project-URL: Repository, https://github.com/antosubash/simple_module_python
7
+ Project-URL: Issues, https://github.com/antosubash/simple_module_python/issues
8
+ Project-URL: Changelog, https://github.com/antosubash/simple_module_python/blob/main/CHANGELOG.md
9
+ Author-email: Anto Subash <antosubash@live.com>
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: authentication,cookie,fastapi,session,simple-module
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Framework :: FastAPI
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Internet :: WWW/HTTP
21
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
22
+ Classifier: Typing :: Typed
23
+ Requires-Python: >=3.12
24
+ Requires-Dist: itsdangerous>=2.2
25
+ Requires-Dist: simple-module-core==0.0.1
26
+ Requires-Dist: simple-module-db==0.0.1
27
+ Description-Content-Type: text/markdown
28
+
29
+ # simple_module_auth
30
+
31
+ Session-cookie authentication primitives for [simple_module](https://github.com/antosubash/simple_module_python) apps. Provides the `SessionMiddleware` wiring, login/logout helpers, and login-redirect handling used by the `simple_module_users` module.
32
+
33
+ **Heads up:** for most apps you don't install this directly — `simple_module_users` pulls it in and builds the email+password auth flow on top of these primitives.
34
+
35
+ ## Install
36
+
37
+ ```bash
38
+ pip install simple_module_auth
39
+ ```
40
+
41
+ ## What it provides
42
+
43
+ - Starlette `SessionMiddleware` configuration reading `SM_SECRET_KEY` and `SM_SESSION_COOKIE_*` env vars.
44
+ - `current_user_id` FastAPI dependency reading the signed session cookie.
45
+ - Redirect-to-login helpers for unauthenticated requests on Inertia routes.
46
+ - Login-required decorator / dependency for protecting routes without pulling in the heavier `simple_module_users` package.
47
+
48
+ ## Usage
49
+
50
+ ```python
51
+ from fastapi import APIRouter, Depends
52
+ from simple_module_auth import require_login
53
+
54
+ router = APIRouter()
55
+
56
+
57
+ @router.get("/me")
58
+ async def me(user_id: int = Depends(require_login)):
59
+ return {"user_id": user_id}
60
+ ```
61
+
62
+ Routes that need more than just "logged in" (e.g. role/permission checks) should use `simple_module_permissions` instead.
63
+
64
+ ## Depends on
65
+
66
+ - `simple_module_core`, `simple_module_db`
67
+ - `itsdangerous`
68
+
69
+ ## License
70
+
71
+ MIT — see [LICENSE](https://github.com/antosubash/simple_module_python/blob/main/LICENSE).
@@ -0,0 +1,14 @@
1
+ auth/__init__.py,sha256=KNm7tP262uKG-dHYqVa_oIH_gSObAPH-vFL6hGGYsAw,60
2
+ auth/deps.py,sha256=ouFGjKZd9IOQo5oxLQ9UBSTkzgt_zoOhiEjOpNI_vXs,1805
3
+ auth/module.py,sha256=huMTDc_IeS8eiRQhnu-mvPcXY7xQ52GcdmqWdUFNJEA,767
4
+ auth/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ auth/contracts/__init__.py,sha256=7vR0kgDkcs9HDP7Gcrfmq8UCuWniErIxLUPyRnz-qlU,132
6
+ auth/contracts/schemas.py,sha256=Fi1-xqTaoWv3EEM9Oz-6Qst3PqVBrF7gp-_iXW1jjgo,2562
7
+ auth/locales/en.json,sha256=3a-d-xk1u4wUC6CZFWl3UhvTu7CTjZ0st9K_05FbY_o,139
8
+ auth/locales/es.json,sha256=9oeoOjEHrbkkgWSks4xSl4E1ePuEl-DHPsmjfntb9OM,135
9
+ auth/package.json,sha256=rSy_-0JLlNthQ25ru8_yk1b1-uBmki0wNiP426eWt_4,371
10
+ simple_module_auth-0.0.1.dist-info/METADATA,sha256=q4cnkxA4G7IB_B7Gqr4iqn2hkWjcQ9x7sk_nC2CEfZc,2748
11
+ simple_module_auth-0.0.1.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
12
+ simple_module_auth-0.0.1.dist-info/entry_points.txt,sha256=LpawiTLI4tijzuCnX4Judo1zha8cp87_Mx3_24qg4ZA,46
13
+ simple_module_auth-0.0.1.dist-info/licenses/LICENSE,sha256=Yn66lhLklsF5p7pa85_ksQrJ79Q-FgOaUAHevLBjer4,1068
14
+ simple_module_auth-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [simple_module]
2
+ auth = auth.module:AuthModule
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Anto Subash
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.