plain.auth 0.15.0__tar.gz → 0.16.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.
@@ -0,0 +1,183 @@
1
+ Metadata-Version: 2.4
2
+ Name: plain.auth
3
+ Version: 0.16.0
4
+ Summary: Add users to your app and decide what they can access.
5
+ Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.11
8
+ Requires-Dist: plain-models<1.0.0
9
+ Requires-Dist: plain-sessions<1.0.0
10
+ Requires-Dist: plain<1.0.0
11
+ Description-Content-Type: text/markdown
12
+
13
+ # plain.auth
14
+
15
+ **Add users to your app and decide what they can access.**
16
+
17
+ - [Overview](#overview)
18
+ - [Authentication setup](#authentication-setup)
19
+ - [Settings configuration](#settings-configuration)
20
+ - [Creating a user model](#creating-a-user-model)
21
+ - [Login views](#login-views)
22
+ - [Checking if a user is logged in](#checking-if-a-user-is-logged-in)
23
+ - [Restricting views](#restricting-views)
24
+ - [Installation](#installation)
25
+
26
+ ## Overview
27
+
28
+ The `plain.auth` package provides user authentication and authorization for Plain applications. Here's a basic example of checking if a user is logged in:
29
+
30
+ ```python
31
+ # In a view
32
+ if request.user:
33
+ print(f"Hello, {request.user.email}!")
34
+ else:
35
+ print("You are not logged in.")
36
+ ```
37
+
38
+ And restricting a view to logged-in users:
39
+
40
+ ```python
41
+ from plain.auth.views import AuthViewMixin
42
+ from plain.views import View
43
+
44
+ class ProfileView(AuthViewMixin, View):
45
+ login_required = True
46
+
47
+ def get(self):
48
+ return f"Welcome, {self.request.user.email}!"
49
+ ```
50
+
51
+ ## Authentication setup
52
+
53
+ ### Settings configuration
54
+
55
+ Configure your authentication settings in `app/settings.py`:
56
+
57
+ ```python
58
+ INSTALLED_PACKAGES = [
59
+ # ...
60
+ "plain.auth",
61
+ "plain.sessions",
62
+ "plain.passwords", # Or another auth method
63
+ ]
64
+
65
+ MIDDLEWARE = [
66
+ "plain.sessions.middleware.SessionMiddleware",
67
+ "plain.auth.middleware.AuthenticationMiddleware",
68
+ ]
69
+
70
+ AUTH_USER_MODEL = "users.User"
71
+ AUTH_LOGIN_URL = "login"
72
+ ```
73
+
74
+ ### Creating a user model
75
+
76
+ Create your own user model using `plain create users` or manually:
77
+
78
+ ```python
79
+ # app/users/models.py
80
+ from plain import models
81
+ from plain.passwords.models import PasswordField
82
+
83
+
84
+ class User(models.Model):
85
+ email = models.EmailField()
86
+ password = PasswordField()
87
+ is_admin = models.BooleanField(default=False)
88
+ created_at = models.DateTimeField(auto_now_add=True)
89
+
90
+ def __str__(self):
91
+ return self.email
92
+ ```
93
+
94
+ ### Login views
95
+
96
+ To log users in, you'll need to pair this package with an authentication method:
97
+
98
+ - `plain-passwords` - Username/password authentication
99
+ - `plain-oauth` - OAuth provider authentication
100
+ - `plain-passkeys` (TBD) - WebAuthn/passkey authentication
101
+ - `plain-passlinks` (TBD) - Magic link authentication
102
+
103
+ Example with password authentication:
104
+
105
+ ```python
106
+ # app/urls.py
107
+ from plain.auth.views import LogoutView
108
+ from plain.urls import path
109
+ from plain.passwords.views import PasswordLoginView
110
+
111
+
112
+ class LoginView(PasswordLoginView):
113
+ template_name = "login.html"
114
+
115
+
116
+ urlpatterns = [
117
+ path("logout/", LogoutView, name="logout"),
118
+ path("login/", LoginView, name="login"),
119
+ ]
120
+ ```
121
+
122
+ ## Checking if a user is logged in
123
+
124
+ A `request.user` will either be `None` or point to an instance of your `AUTH_USER_MODEL`.
125
+
126
+ In templates:
127
+
128
+ ```html
129
+ {% if request.user %}
130
+ <p>Hello, {{ request.user.email }}!</p>
131
+ {% else %}
132
+ <p>You are not logged in.</p>
133
+ {% endif %}
134
+ ```
135
+
136
+ In Python code:
137
+
138
+ ```python
139
+ if request.user:
140
+ print(f"Hello, {request.user.email}!")
141
+ else:
142
+ print("You are not logged in.")
143
+ ```
144
+
145
+ ## Restricting views
146
+
147
+ Use the [`AuthViewMixin`](./views.py#AuthViewMixin) to restrict views to logged-in users, admin users, or custom logic:
148
+
149
+ ```python
150
+ from plain.auth.views import AuthViewMixin
151
+ from plain.exceptions import PermissionDenied
152
+ from plain.views import View
153
+
154
+
155
+ class LoggedInView(AuthViewMixin, View):
156
+ login_required = True
157
+
158
+
159
+ class AdminOnlyView(AuthViewMixin, View):
160
+ login_required = True
161
+ admin_required = True
162
+
163
+
164
+ class CustomPermissionView(AuthViewMixin, View):
165
+ def check_auth(self):
166
+ super().check_auth()
167
+ if not self.request.user.is_special:
168
+ raise PermissionDenied("You're not special!")
169
+ ```
170
+
171
+ The [`AuthViewMixin`](./views.py#AuthViewMixin) provides:
172
+
173
+ - `login_required` - Requires a logged-in user
174
+ - `admin_required` - Requires `user.is_admin` to be True
175
+ - `check_auth()` - Override for custom authorization logic
176
+
177
+ ## Installation
178
+
179
+ Install the `plain.auth` package from [PyPI](https://pypi.org/project/plain.auth/):
180
+
181
+ ```bash
182
+ uv add plain.auth
183
+ ```
@@ -1,5 +1,16 @@
1
1
  # plain-auth changelog
2
2
 
3
+ ## [0.16.0](https://github.com/dropseed/plain/releases/plain-auth@0.16.0) (2025-08-19)
4
+
5
+ ### What's changed
6
+
7
+ - Removed automatic CSRF token rotation on login as part of CSRF system refactor using Sec-Fetch-Site headers ([9551508](https://github.com/dropseed/plain/commit/955150800c))
8
+ - Updated README with improved documentation, examples, and better package description ([4ebecd1](https://github.com/dropseed/plain/commit/4ebecd1856))
9
+
10
+ ### Upgrade instructions
11
+
12
+ - No changes required
13
+
3
14
  ## [0.15.0](https://github.com/dropseed/plain/releases/plain-auth@0.15.0) (2025-07-22)
4
15
 
5
16
  ### What's changed
@@ -0,0 +1,171 @@
1
+ # plain.auth
2
+
3
+ **Add users to your app and decide what they can access.**
4
+
5
+ - [Overview](#overview)
6
+ - [Authentication setup](#authentication-setup)
7
+ - [Settings configuration](#settings-configuration)
8
+ - [Creating a user model](#creating-a-user-model)
9
+ - [Login views](#login-views)
10
+ - [Checking if a user is logged in](#checking-if-a-user-is-logged-in)
11
+ - [Restricting views](#restricting-views)
12
+ - [Installation](#installation)
13
+
14
+ ## Overview
15
+
16
+ The `plain.auth` package provides user authentication and authorization for Plain applications. Here's a basic example of checking if a user is logged in:
17
+
18
+ ```python
19
+ # In a view
20
+ if request.user:
21
+ print(f"Hello, {request.user.email}!")
22
+ else:
23
+ print("You are not logged in.")
24
+ ```
25
+
26
+ And restricting a view to logged-in users:
27
+
28
+ ```python
29
+ from plain.auth.views import AuthViewMixin
30
+ from plain.views import View
31
+
32
+ class ProfileView(AuthViewMixin, View):
33
+ login_required = True
34
+
35
+ def get(self):
36
+ return f"Welcome, {self.request.user.email}!"
37
+ ```
38
+
39
+ ## Authentication setup
40
+
41
+ ### Settings configuration
42
+
43
+ Configure your authentication settings in `app/settings.py`:
44
+
45
+ ```python
46
+ INSTALLED_PACKAGES = [
47
+ # ...
48
+ "plain.auth",
49
+ "plain.sessions",
50
+ "plain.passwords", # Or another auth method
51
+ ]
52
+
53
+ MIDDLEWARE = [
54
+ "plain.sessions.middleware.SessionMiddleware",
55
+ "plain.auth.middleware.AuthenticationMiddleware",
56
+ ]
57
+
58
+ AUTH_USER_MODEL = "users.User"
59
+ AUTH_LOGIN_URL = "login"
60
+ ```
61
+
62
+ ### Creating a user model
63
+
64
+ Create your own user model using `plain create users` or manually:
65
+
66
+ ```python
67
+ # app/users/models.py
68
+ from plain import models
69
+ from plain.passwords.models import PasswordField
70
+
71
+
72
+ class User(models.Model):
73
+ email = models.EmailField()
74
+ password = PasswordField()
75
+ is_admin = models.BooleanField(default=False)
76
+ created_at = models.DateTimeField(auto_now_add=True)
77
+
78
+ def __str__(self):
79
+ return self.email
80
+ ```
81
+
82
+ ### Login views
83
+
84
+ To log users in, you'll need to pair this package with an authentication method:
85
+
86
+ - `plain-passwords` - Username/password authentication
87
+ - `plain-oauth` - OAuth provider authentication
88
+ - `plain-passkeys` (TBD) - WebAuthn/passkey authentication
89
+ - `plain-passlinks` (TBD) - Magic link authentication
90
+
91
+ Example with password authentication:
92
+
93
+ ```python
94
+ # app/urls.py
95
+ from plain.auth.views import LogoutView
96
+ from plain.urls import path
97
+ from plain.passwords.views import PasswordLoginView
98
+
99
+
100
+ class LoginView(PasswordLoginView):
101
+ template_name = "login.html"
102
+
103
+
104
+ urlpatterns = [
105
+ path("logout/", LogoutView, name="logout"),
106
+ path("login/", LoginView, name="login"),
107
+ ]
108
+ ```
109
+
110
+ ## Checking if a user is logged in
111
+
112
+ A `request.user` will either be `None` or point to an instance of your `AUTH_USER_MODEL`.
113
+
114
+ In templates:
115
+
116
+ ```html
117
+ {% if request.user %}
118
+ <p>Hello, {{ request.user.email }}!</p>
119
+ {% else %}
120
+ <p>You are not logged in.</p>
121
+ {% endif %}
122
+ ```
123
+
124
+ In Python code:
125
+
126
+ ```python
127
+ if request.user:
128
+ print(f"Hello, {request.user.email}!")
129
+ else:
130
+ print("You are not logged in.")
131
+ ```
132
+
133
+ ## Restricting views
134
+
135
+ Use the [`AuthViewMixin`](./views.py#AuthViewMixin) to restrict views to logged-in users, admin users, or custom logic:
136
+
137
+ ```python
138
+ from plain.auth.views import AuthViewMixin
139
+ from plain.exceptions import PermissionDenied
140
+ from plain.views import View
141
+
142
+
143
+ class LoggedInView(AuthViewMixin, View):
144
+ login_required = True
145
+
146
+
147
+ class AdminOnlyView(AuthViewMixin, View):
148
+ login_required = True
149
+ admin_required = True
150
+
151
+
152
+ class CustomPermissionView(AuthViewMixin, View):
153
+ def check_auth(self):
154
+ super().check_auth()
155
+ if not self.request.user.is_special:
156
+ raise PermissionDenied("You're not special!")
157
+ ```
158
+
159
+ The [`AuthViewMixin`](./views.py#AuthViewMixin) provides:
160
+
161
+ - `login_required` - Requires a logged-in user
162
+ - `admin_required` - Requires `user.is_admin` to be True
163
+ - `check_auth()` - Override for custom authorization logic
164
+
165
+ ## Installation
166
+
167
+ Install the `plain.auth` package from [PyPI](https://pypi.org/project/plain.auth/):
168
+
169
+ ```bash
170
+ uv add plain.auth
171
+ ```
@@ -1,4 +1,3 @@
1
- from plain.csrf.middleware import rotate_token
2
1
  from plain.exceptions import ImproperlyConfigured
3
2
  from plain.models import models_registry
4
3
  from plain.runtime import settings
@@ -76,7 +75,6 @@ def login(request, user):
76
75
  request.session[USER_HASH_SESSION_KEY] = session_auth_hash
77
76
  if hasattr(request, "user"):
78
77
  request.user = user
79
- rotate_token(request)
80
78
 
81
79
 
82
80
  def logout(request):
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "plain.auth"
3
- version = "0.15.0"
4
- description = "User authentication and authorization for Plain."
3
+ version = "0.16.0"
4
+ description = "Add users to your app and decide what they can access."
5
5
  authors = [{name = "Dave Gaeddert", email = "dave.gaeddert@dropseed.dev"}]
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.11"
@@ -1,128 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: plain.auth
3
- Version: 0.15.0
4
- Summary: User authentication and authorization for Plain.
5
- Author-email: Dave Gaeddert <dave.gaeddert@dropseed.dev>
6
- License-File: LICENSE
7
- Requires-Python: >=3.11
8
- Requires-Dist: plain-models<1.0.0
9
- Requires-Dist: plain-sessions<1.0.0
10
- Requires-Dist: plain<1.0.0
11
- Description-Content-Type: text/markdown
12
-
13
- # plain.auth
14
-
15
- Add users to your app and define which views they can access.
16
-
17
- To log a user in, you'll want to pair this package with:
18
-
19
- - `plain-passwords`
20
- - `plain-oauth`
21
- - `plain-passkeys` (TBD)
22
- - `plain-passlinks` (TBD)
23
-
24
- ## Installation
25
-
26
- ```python
27
- # app/settings.py
28
- INSTALLED_PACKAGES = [
29
- # ...
30
- "plain.auth",
31
- "plain.sessions",
32
- "plain.passwords",
33
- ]
34
-
35
- MIDDLEWARE = [
36
- "plain.sessions.middleware.SessionMiddleware",
37
- "plain.auth.middleware.AuthenticationMiddleware",
38
- ]
39
-
40
- AUTH_USER_MODEL = "users.User"
41
- AUTH_LOGIN_URL = "login"
42
- ```
43
-
44
- Create your own user model (`plain create users`).
45
-
46
- ```python
47
- # app/users/models.py
48
- from plain import models
49
- from plain.passwords.models import PasswordField
50
-
51
-
52
- class User(models.Model):
53
- email = models.EmailField()
54
- password = PasswordField()
55
- is_admin = models.BooleanField(default=False)
56
- created_at = models.DateTimeField(auto_now_add=True)
57
-
58
- def __str__(self):
59
- return self.email
60
- ```
61
-
62
- Define your URL/view where users can log in.
63
-
64
- ```python
65
- # app/urls.py
66
- from plain.auth.views import LoginView, LogoutView
67
- from plain.urls import include, path
68
- from plain.passwords.views import PasswordLoginView
69
-
70
-
71
- class LoginView(PasswordLoginView):
72
- template_name = "login.html"
73
-
74
-
75
- urlpatterns = [
76
- path("logout/", LogoutView, name="logout"),
77
- path("login/", LoginView, name="login"),
78
- ]
79
- ```
80
-
81
- ## Checking if a user is logged in
82
-
83
- A `request.user` will either be `None` or point to an instance of a your `AUTH_USER_MODEL`.
84
-
85
- So in templates you can do:
86
-
87
- ```html
88
- {% if request.user %}
89
- <p>Hello, {{ request.user.email }}!</p>
90
- {% else %}
91
- <p>You are not logged in.</p>
92
- {% endif %}
93
- ```
94
-
95
- Or in Python:
96
-
97
- ```python
98
- if request.user:
99
- print(f"Hello, {request.user.email}!")
100
- else:
101
- print("You are not logged in.")
102
- ```
103
-
104
- ## Restricting views
105
-
106
- Use the `AuthViewMixin` to restrict views to logged in users, admin users, or custom logic.
107
-
108
- ```python
109
- from plain.auth.views import AuthViewMixin
110
- from plain.exceptions import PermissionDenied
111
- from plain.views import View
112
-
113
-
114
- class LoggedInView(AuthViewMixin, View):
115
- login_required = True
116
-
117
-
118
- class AdminOnlyView(AuthViewMixin, View):
119
- login_required = True
120
- admin_required = True
121
-
122
-
123
- class CustomPermissionView(AuthViewMixin, View):
124
- def check_auth(self):
125
- super().check_auth()
126
- if not self.request.user.is_special:
127
- raise PermissionDenied("You're not special!")
128
- ```
@@ -1,116 +0,0 @@
1
- # plain.auth
2
-
3
- Add users to your app and define which views they can access.
4
-
5
- To log a user in, you'll want to pair this package with:
6
-
7
- - `plain-passwords`
8
- - `plain-oauth`
9
- - `plain-passkeys` (TBD)
10
- - `plain-passlinks` (TBD)
11
-
12
- ## Installation
13
-
14
- ```python
15
- # app/settings.py
16
- INSTALLED_PACKAGES = [
17
- # ...
18
- "plain.auth",
19
- "plain.sessions",
20
- "plain.passwords",
21
- ]
22
-
23
- MIDDLEWARE = [
24
- "plain.sessions.middleware.SessionMiddleware",
25
- "plain.auth.middleware.AuthenticationMiddleware",
26
- ]
27
-
28
- AUTH_USER_MODEL = "users.User"
29
- AUTH_LOGIN_URL = "login"
30
- ```
31
-
32
- Create your own user model (`plain create users`).
33
-
34
- ```python
35
- # app/users/models.py
36
- from plain import models
37
- from plain.passwords.models import PasswordField
38
-
39
-
40
- class User(models.Model):
41
- email = models.EmailField()
42
- password = PasswordField()
43
- is_admin = models.BooleanField(default=False)
44
- created_at = models.DateTimeField(auto_now_add=True)
45
-
46
- def __str__(self):
47
- return self.email
48
- ```
49
-
50
- Define your URL/view where users can log in.
51
-
52
- ```python
53
- # app/urls.py
54
- from plain.auth.views import LoginView, LogoutView
55
- from plain.urls import include, path
56
- from plain.passwords.views import PasswordLoginView
57
-
58
-
59
- class LoginView(PasswordLoginView):
60
- template_name = "login.html"
61
-
62
-
63
- urlpatterns = [
64
- path("logout/", LogoutView, name="logout"),
65
- path("login/", LoginView, name="login"),
66
- ]
67
- ```
68
-
69
- ## Checking if a user is logged in
70
-
71
- A `request.user` will either be `None` or point to an instance of a your `AUTH_USER_MODEL`.
72
-
73
- So in templates you can do:
74
-
75
- ```html
76
- {% if request.user %}
77
- <p>Hello, {{ request.user.email }}!</p>
78
- {% else %}
79
- <p>You are not logged in.</p>
80
- {% endif %}
81
- ```
82
-
83
- Or in Python:
84
-
85
- ```python
86
- if request.user:
87
- print(f"Hello, {request.user.email}!")
88
- else:
89
- print("You are not logged in.")
90
- ```
91
-
92
- ## Restricting views
93
-
94
- Use the `AuthViewMixin` to restrict views to logged in users, admin users, or custom logic.
95
-
96
- ```python
97
- from plain.auth.views import AuthViewMixin
98
- from plain.exceptions import PermissionDenied
99
- from plain.views import View
100
-
101
-
102
- class LoggedInView(AuthViewMixin, View):
103
- login_required = True
104
-
105
-
106
- class AdminOnlyView(AuthViewMixin, View):
107
- login_required = True
108
- admin_required = True
109
-
110
-
111
- class CustomPermissionView(AuthViewMixin, View):
112
- def check_auth(self):
113
- super().check_auth()
114
- if not self.request.user.is_special:
115
- raise PermissionDenied("You're not special!")
116
- ```
File without changes
File without changes
File without changes