fasthtml-auth 0.1.0__tar.gz → 0.1.2__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.
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/CHANGELOG.md +26 -1
- {fasthtml_auth-0.1.0/fasthtml_auth.egg-info → fasthtml_auth-0.1.2}/PKG-INFO +1 -1
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/examples/basic_app.py +5 -5
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/fasthtml_auth/__init__.py +1 -1
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/fasthtml_auth/forms.py +8 -6
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/fasthtml_auth/middleware.py +25 -1
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/fasthtml_auth/routes.py +26 -3
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2/fasthtml_auth.egg-info}/PKG-INFO +1 -1
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/pyproject.toml +1 -1
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/LICENSE +0 -0
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/MANIFEST.in +0 -0
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/README.md +0 -0
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/fasthtml_auth/database.py +0 -0
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/fasthtml_auth/init.py +0 -0
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/fasthtml_auth/manager.py +0 -0
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/fasthtml_auth/models.py +0 -0
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/fasthtml_auth/repository.py +0 -0
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/fasthtml_auth/utils.py +0 -0
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/fasthtml_auth.egg-info/SOURCES.txt +0 -0
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/fasthtml_auth.egg-info/dependency_links.txt +0 -0
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/fasthtml_auth.egg-info/requires.txt +0 -0
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/fasthtml_auth.egg-info/top_level.txt +0 -0
- {fasthtml_auth-0.1.0 → fasthtml_auth-0.1.2}/setup.cfg +0 -0
@@ -5,7 +5,32 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
-
## [0.1.
|
8
|
+
## [0.1.2] - 2025-09-03
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Implemented "Remember me" functionality with extended session duration
|
12
|
+
- Added session expiry handling for persistent logins
|
13
|
+
- Server-side validation for "Accept Terms" checkbox in registration
|
14
|
+
- Clear error message when terms are not accepted
|
15
|
+
|
16
|
+
|
17
|
+
### Fixed
|
18
|
+
- "Remember me" checkbox now actually remembers users for 30 days
|
19
|
+
- "Checkbox for accept terms in the register form has been corrected so that it displays correctly
|
20
|
+
- Registration now properly validates terms acceptance
|
21
|
+
- Sorted out some styling issues with the admin forms in basic_app.py
|
22
|
+
|
23
|
+
|
24
|
+
## [0.1.1] - 2025-09-09
|
25
|
+
|
26
|
+
### Fixed
|
27
|
+
- Fixed `require_role` decorator to work with functions that don't accept *args, **kwargs
|
28
|
+
- Added automatic parameter inspection to handle both single-parameter and multi-parameter route functions
|
29
|
+
|
30
|
+
### Changed
|
31
|
+
- Improved decorator compatibility with different FastHTML route function signatures
|
32
|
+
|
33
|
+
## [0.1.0] - 2025-09-03
|
9
34
|
|
10
35
|
### Added
|
11
36
|
- Initial release of FastHTML-Auth
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: fasthtml-auth
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.2
|
4
4
|
Summary: Complete authentication system for FastHTML applications with beautiful UI, role-based access control, and session management
|
5
5
|
Author-email: John Richmond <confusedjohn46@gmail.com>
|
6
6
|
Maintainer-email: John Richmond <confusedjohn46@gmail.com>
|
@@ -117,13 +117,13 @@ def admin_panel(req):
|
|
117
117
|
" ",
|
118
118
|
Button("System Settings", cls=ButtonT.secondary, disabled=True),
|
119
119
|
" ",
|
120
|
-
Button("View Logs", cls=ButtonT.
|
120
|
+
Button("View Logs", cls=ButtonT.secondary, disabled=True),
|
121
121
|
cls="mt-4"
|
122
122
|
),
|
123
123
|
P("(These buttons are disabled in demo)", cls="text-sm text-muted-foreground mt-2")
|
124
124
|
)
|
125
125
|
),
|
126
|
-
cls=ContainerT.
|
126
|
+
cls=ContainerT.lg
|
127
127
|
)
|
128
128
|
|
129
129
|
# Test manager route
|
@@ -151,7 +151,7 @@ def manager_view(req, *args, **kwargs):
|
|
151
151
|
P("(These buttons are disabled in demo)", cls="text-sm text-muted-foreground mt-2")
|
152
152
|
)
|
153
153
|
),
|
154
|
-
cls=ContainerT.
|
154
|
+
cls=ContainerT.lg
|
155
155
|
)
|
156
156
|
|
157
157
|
# Public route for testing
|
@@ -180,7 +180,7 @@ def about():
|
|
180
180
|
)
|
181
181
|
)
|
182
182
|
),
|
183
|
-
cls=ContainerT.
|
183
|
+
cls=ContainerT.lg
|
184
184
|
)
|
185
185
|
|
186
186
|
# Contact page - another public route
|
@@ -199,7 +199,7 @@ def contact():
|
|
199
199
|
P("Phone: (555) 123-4567")
|
200
200
|
)
|
201
201
|
),
|
202
|
-
cls=ContainerT.
|
202
|
+
cls=ContainerT.lg
|
203
203
|
)
|
204
204
|
|
205
205
|
if __name__ == "__main__":
|
@@ -42,9 +42,9 @@ def create_login_form(error=None, action="/auth/login", redirect_to="/"):
|
|
42
42
|
|
43
43
|
Div(
|
44
44
|
Label(
|
45
|
-
|
46
|
-
" Remember me",
|
47
|
-
cls="flex items-center text-sm"
|
45
|
+
CheckboxX(name="remember_me", selected=False),
|
46
|
+
Span(" Remember me", cls="ml-2"),
|
47
|
+
cls="flex items-center text-sm cursor-pointer"
|
48
48
|
),
|
49
49
|
cls="mb-4"
|
50
50
|
),
|
@@ -87,6 +87,8 @@ def create_register_form(error=None, action="/auth/register"):
|
|
87
87
|
error_message = "Please enter a valid email address."
|
88
88
|
elif error == 'creation_failed':
|
89
89
|
error_message = "Failed to create account. Please try again."
|
90
|
+
elif error == 'terms_required': # NEW ERROR MESSAGE
|
91
|
+
error_message = "You must accept the Terms and Conditions to register."
|
90
92
|
|
91
93
|
return DivCentered(
|
92
94
|
Card(
|
@@ -139,9 +141,9 @@ def create_register_form(error=None, action="/auth/register"):
|
|
139
141
|
|
140
142
|
Div(
|
141
143
|
Label(
|
142
|
-
|
143
|
-
" I accept the Terms and Conditions",
|
144
|
-
cls="flex items-center text-sm"
|
144
|
+
CheckboxX(name="accept_terms", selected=False, required=True),
|
145
|
+
Span(" I accept the Terms and Conditions", cls="ml-2"),
|
146
|
+
cls="flex items-center text-sm cursor-pointer"
|
145
147
|
),
|
146
148
|
cls="mb-4"
|
147
149
|
),
|
@@ -1,4 +1,5 @@
|
|
1
1
|
from fasthtml.common import *
|
2
|
+
import inspect
|
2
3
|
from typing import Optional, List
|
3
4
|
|
4
5
|
class AuthBeforeware:
|
@@ -34,6 +35,23 @@ class AuthBeforeware:
|
|
34
35
|
"""Check authentication prior to check"""
|
35
36
|
auth_username = sess.get('auth')
|
36
37
|
|
38
|
+
# If no session auth, check for remember me cookie
|
39
|
+
if not auth_username:
|
40
|
+
remember_user = req.cookies.get('remember_user')
|
41
|
+
if remember_user:
|
42
|
+
# Verify user still exists and is active
|
43
|
+
user = self.auth_manager.get_user(remember_user)
|
44
|
+
if user and user.active:
|
45
|
+
# Restore session from remember me cookie
|
46
|
+
sess['auth'] = user.username
|
47
|
+
sess['user_id'] = user.id
|
48
|
+
sess['role'] = user.role
|
49
|
+
sess['remember_me'] = True
|
50
|
+
auth_username = user.username
|
51
|
+
else:
|
52
|
+
# Invalid remember me cookie, continue to redirect
|
53
|
+
pass
|
54
|
+
|
37
55
|
if not auth_username:
|
38
56
|
# No auth, redirect to login
|
39
57
|
return RedirectResponse(self.login_path, status_code=303)
|
@@ -98,7 +116,13 @@ class AuthBeforeware:
|
|
98
116
|
user = req.scope.get('user')
|
99
117
|
if not user or user.role not in allowed_roles:
|
100
118
|
return Response("Forbidden", status_code=403)
|
101
|
-
|
119
|
+
|
120
|
+
# Check if function accepts extra args
|
121
|
+
sig = inspect.signature(func)
|
122
|
+
if len(sig.parameters) == 1: # Only takes req
|
123
|
+
return func(req)
|
124
|
+
else:
|
125
|
+
return func(req, *args, **kwargs)
|
102
126
|
return wrapper
|
103
127
|
return decorator
|
104
128
|
|
@@ -46,18 +46,37 @@ class AuthRoutes:
|
|
46
46
|
form = await req.form()
|
47
47
|
username = form.get('username', '').strip()
|
48
48
|
password = form.get('password', '')
|
49
|
+
remember_me = form.get('remember_me') == 'on'
|
49
50
|
|
50
51
|
# Authenticate
|
51
52
|
user = self.auth.user_repo.authenticate(username, password)
|
53
|
+
|
52
54
|
if user:
|
53
55
|
# Set session
|
54
56
|
sess['auth'] = user.username
|
55
57
|
sess['user_id'] = user.id
|
56
58
|
sess['role'] = user.role
|
57
|
-
|
58
|
-
# Redirect to next URL or default
|
59
|
+
|
59
60
|
redirect_url = form.get('redirect_to', '/')
|
60
|
-
|
61
|
+
response = RedirectResponse(redirect_url, status_code=303)
|
62
|
+
|
63
|
+
if remember_me:
|
64
|
+
# Set a long-lived cookie (30 days)
|
65
|
+
response.set_cookie(
|
66
|
+
key='remember_user',
|
67
|
+
value=user.username,
|
68
|
+
max_age=30*24*60*60, # 30 days in seconds
|
69
|
+
httponly=True,
|
70
|
+
samesite='strict'
|
71
|
+
)
|
72
|
+
sess['remember_me'] = True
|
73
|
+
else:
|
74
|
+
# Remove remember me cookie if it exists
|
75
|
+
response.delete_cookie('remember_user')
|
76
|
+
sess.pop('remember_me', None)
|
77
|
+
|
78
|
+
return response
|
79
|
+
|
61
80
|
# On failure, preserve the redirect_to parameter
|
62
81
|
redirect_to = form.get('redirect_to', '/')
|
63
82
|
error_url = f"{prefix}/login?error=invalid"
|
@@ -92,6 +111,10 @@ class AuthRoutes:
|
|
92
111
|
email = form.get('email', '').strip()
|
93
112
|
password = form.get('password', '')
|
94
113
|
confirm = form.get('confirm_password', '')
|
114
|
+
accept_terms = form.get('accept_terms') == 'on'
|
115
|
+
|
116
|
+
if not accept_terms:
|
117
|
+
return RedirectResponse(f"{prefix}/register?error=terms_required", status_code=303)
|
95
118
|
|
96
119
|
# Validation
|
97
120
|
if password != confirm:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: fasthtml-auth
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.2
|
4
4
|
Summary: Complete authentication system for FastHTML applications with beautiful UI, role-based access control, and session management
|
5
5
|
Author-email: John Richmond <confusedjohn46@gmail.com>
|
6
6
|
Maintainer-email: John Richmond <confusedjohn46@gmail.com>
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
4
4
|
|
5
5
|
[project]
|
6
6
|
name = "fasthtml-auth"
|
7
|
-
version = "0.1.
|
7
|
+
version = "0.1.2"
|
8
8
|
description = "Complete authentication system for FastHTML applications with beautiful UI, role-based access control, and session management"
|
9
9
|
readme = "README.md"
|
10
10
|
license = {file = "LICENSE"}
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|