fasthtml-auth 0.1.0__py3-none-any.whl → 0.1.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.
- fasthtml_auth/__init__.py +1 -1
- fasthtml_auth/forms.py +8 -6
- fasthtml_auth/middleware.py +25 -1
- fasthtml_auth/routes.py +26 -3
- {fasthtml_auth-0.1.0.dist-info → fasthtml_auth-0.1.2.dist-info}/METADATA +1 -1
- fasthtml_auth-0.1.2.dist-info/RECORD +15 -0
- fasthtml_auth-0.1.0.dist-info/RECORD +0 -15
- {fasthtml_auth-0.1.0.dist-info → fasthtml_auth-0.1.2.dist-info}/WHEEL +0 -0
- {fasthtml_auth-0.1.0.dist-info → fasthtml_auth-0.1.2.dist-info}/licenses/LICENSE +0 -0
- {fasthtml_auth-0.1.0.dist-info → fasthtml_auth-0.1.2.dist-info}/top_level.txt +0 -0
fasthtml_auth/__init__.py
CHANGED
fasthtml_auth/forms.py
CHANGED
@@ -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
|
),
|
fasthtml_auth/middleware.py
CHANGED
@@ -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
|
|
fasthtml_auth/routes.py
CHANGED
@@ -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>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
fasthtml_auth/__init__.py,sha256=HvOxBGpyrUEUWgh36E_niD3srCB01FXy0JpET7cHVB8,780
|
2
|
+
fasthtml_auth/database.py,sha256=xVSqZD8-zA1qq166LsQMQvxMkMaJhLfIv1QIXX0Bgvc,1079
|
3
|
+
fasthtml_auth/forms.py,sha256=CNgDfU-kTzubefx650nC-654FAgrdvVJLhPmpRRCoAk,15372
|
4
|
+
fasthtml_auth/init.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
fasthtml_auth/manager.py,sha256=1sI6S4HLji9FdZNxPCp-73wsihkyaNvCG_DzibfazDk,2663
|
6
|
+
fasthtml_auth/middleware.py,sha256=kStT_UEPVbEpp5XIWf2W_iNUEiZ8nz-MJHhXBND-fAI,4425
|
7
|
+
fasthtml_auth/models.py,sha256=WMZuvW9ikB9y8Zql4Q68ItvzVYxNkGuLWsR5gnPp86w,2323
|
8
|
+
fasthtml_auth/repository.py,sha256=27j7ps4RurezPKUjGBMhUGQBofRER2GeSFgSe7FPjic,4395
|
9
|
+
fasthtml_auth/routes.py,sha256=muNa7TnUumgDNX1rIkByC1-jhm0KzRWX5_CGjlqVdeU,9892
|
10
|
+
fasthtml_auth/utils.py,sha256=NsdmkcxVLfahe_BVc8GqawadwJgilX6NbZt-gMdiibk,1451
|
11
|
+
fasthtml_auth-0.1.2.dist-info/licenses/LICENSE,sha256=EnPW65jfIuV5msUjRTyzltVV2Aqo3HcIyM6z3hUloXQ,1064
|
12
|
+
fasthtml_auth-0.1.2.dist-info/METADATA,sha256=XW2H6do3KgzLbWYwQEoLCL8ZVlqWN7nt4nYHzGbhHF8,12395
|
13
|
+
fasthtml_auth-0.1.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
14
|
+
fasthtml_auth-0.1.2.dist-info/top_level.txt,sha256=uG6b_v5AONQHyoez8FVziIAf9ORW0Ri_mUaU0yNwMWk,14
|
15
|
+
fasthtml_auth-0.1.2.dist-info/RECORD,,
|
@@ -1,15 +0,0 @@
|
|
1
|
-
fasthtml_auth/__init__.py,sha256=6y-RXppiFdNaMAMbWNUxaEZmapDe_gGQfgRW69wINH8,780
|
2
|
-
fasthtml_auth/database.py,sha256=xVSqZD8-zA1qq166LsQMQvxMkMaJhLfIv1QIXX0Bgvc,1079
|
3
|
-
fasthtml_auth/forms.py,sha256=zlgaq5cmHQspjwyz5e48yuC03JL5D4dSm_6gOKnBnP0,15163
|
4
|
-
fasthtml_auth/init.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
fasthtml_auth/manager.py,sha256=1sI6S4HLji9FdZNxPCp-73wsihkyaNvCG_DzibfazDk,2663
|
6
|
-
fasthtml_auth/middleware.py,sha256=CplYcPjzkTRcVGXr19ClDG3inoaxHGK5ZmQUWjboCOc,3341
|
7
|
-
fasthtml_auth/models.py,sha256=WMZuvW9ikB9y8Zql4Q68ItvzVYxNkGuLWsR5gnPp86w,2323
|
8
|
-
fasthtml_auth/repository.py,sha256=27j7ps4RurezPKUjGBMhUGQBofRER2GeSFgSe7FPjic,4395
|
9
|
-
fasthtml_auth/routes.py,sha256=6NBLxppYi53SfUjyMnXLb3gzl9GlswqdTZgEg36-FGc,9025
|
10
|
-
fasthtml_auth/utils.py,sha256=NsdmkcxVLfahe_BVc8GqawadwJgilX6NbZt-gMdiibk,1451
|
11
|
-
fasthtml_auth-0.1.0.dist-info/licenses/LICENSE,sha256=EnPW65jfIuV5msUjRTyzltVV2Aqo3HcIyM6z3hUloXQ,1064
|
12
|
-
fasthtml_auth-0.1.0.dist-info/METADATA,sha256=aeBCysMLiSnnlFz3myExT9FH2vMuuW1uFJ7ANMnvKiU,12395
|
13
|
-
fasthtml_auth-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
14
|
-
fasthtml_auth-0.1.0.dist-info/top_level.txt,sha256=uG6b_v5AONQHyoez8FVziIAf9ORW0Ri_mUaU0yNwMWk,14
|
15
|
-
fasthtml_auth-0.1.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|