sanic-security 1.15.3__tar.gz → 1.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.
Files changed (22) hide show
  1. {sanic_security-1.15.3/sanic_security.egg-info → sanic_security-1.16.0}/PKG-INFO +132 -46
  2. {sanic_security-1.15.3 → sanic_security-1.16.0}/README.md +128 -43
  3. {sanic_security-1.15.3 → sanic_security-1.16.0}/pyproject.toml +3 -3
  4. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security/authentication.py +16 -22
  5. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security/authorization.py +44 -18
  6. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security/configuration.py +9 -0
  7. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security/exceptions.py +5 -3
  8. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security/models.py +55 -52
  9. sanic_security-1.16.0/sanic_security/oauth.py +239 -0
  10. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security/test/server.py +75 -28
  11. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security/test/tests.py +25 -10
  12. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security/utils.py +9 -3
  13. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security/verification.py +1 -4
  14. {sanic_security-1.15.3 → sanic_security-1.16.0/sanic_security.egg-info}/PKG-INFO +132 -46
  15. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security.egg-info/SOURCES.txt +1 -0
  16. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security.egg-info/requires.txt +4 -2
  17. {sanic_security-1.15.3 → sanic_security-1.16.0}/LICENSE +0 -0
  18. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security/__init__.py +0 -0
  19. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security/test/__init__.py +0 -0
  20. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security.egg-info/dependency_links.txt +0 -0
  21. {sanic_security-1.15.3 → sanic_security-1.16.0}/sanic_security.egg-info/top_level.txt +0 -0
  22. {sanic_security-1.15.3 → sanic_security-1.16.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: sanic-security
3
- Version: 1.15.3
3
+ Version: 1.16.0
4
4
  Summary: An async security library for the Sanic framework.
5
5
  Author-email: Aidan Stewart <me@na-stewart.com>
6
6
  Project-URL: Documentation, https://security.na-stewart.com/
@@ -17,11 +17,12 @@ License-File: LICENSE
17
17
  Requires-Dist: tortoise-orm>=0.17.0
18
18
  Requires-Dist: pyjwt>=1.7.0
19
19
  Requires-Dist: captcha>=0.4
20
- Requires-Dist: pillow>=9.5.0
21
20
  Requires-Dist: argon2-cffi>=20.1.0
22
21
  Requires-Dist: sanic>=21.3.0
22
+ Provides-Extra: oauth
23
+ Requires-Dist: httpx-oauth>=0.16.1; extra == "oauth"
23
24
  Provides-Extra: dev
24
- Requires-Dist: httpx; extra == "dev"
25
+ Requires-Dist: httpx-oauth; extra == "dev"
25
26
  Requires-Dist: black; extra == "dev"
26
27
  Requires-Dist: blacken-docs; extra == "dev"
27
28
  Requires-Dist: pdoc3; extra == "dev"
@@ -62,6 +63,7 @@ Requires-Dist: cryptography>=3.3.1; extra == "crypto"
62
63
  * [Installation](#installation)
63
64
  * [Configuration](#configuration)
64
65
  * [Usage](#usage)
66
+ * [OAuth](#oauth)
65
67
  * [Authentication](#authentication)
66
68
  * [CAPTCHA](#captcha)
67
69
  * [Two-step Verification](#two-step-verification)
@@ -79,6 +81,7 @@ Requires-Dist: cryptography>=3.3.1; extra == "crypto"
79
81
  Sanic Security is an authentication, authorization, and verification library designed for use with the
80
82
  [Sanic](https://github.com/huge-success/sanic) framework.
81
83
 
84
+ * OAuth2 integration
82
85
  * Login, registration, and authentication with refresh mechanisms
83
86
  * Role based authorization with wildcard permissions
84
87
  * Image & audio CAPTCHA
@@ -102,7 +105,7 @@ pip3 install sanic-security
102
105
 
103
106
  * Install the Sanic Security pip package with the `cryptography` dependency included.
104
107
 
105
- If you are planning on encoding or decoding JWTs using certain digital signature algorithms (like RSA or ECDSA which use
108
+ If you're planning on encoding or decoding JWTs using certain digital signature algorithms (like RSA or ECDSA which use
106
109
  the public secret and private secret), you will need to install the `cryptography` library. This can be installed explicitly, or
107
110
  as an extra requirement.
108
111
 
@@ -110,7 +113,17 @@ as an extra requirement.
110
113
  pip3 install sanic-security[crypto]
111
114
  ````
112
115
 
113
- * Update sanic-security if already installed.
116
+ * Install the Sanic Security pip package with the `httpx-oauth` dependency included.
117
+
118
+ If you're planning on utilizing OAuth, you will need to install the `httpx-oauth` library. This can be installed explicitly, or
119
+ as an extra requirement.
120
+
121
+ ```shell
122
+ pip3 install sanic-security[oauth]
123
+ ````
124
+
125
+ * Update Sanic Security if already installed.
126
+
114
127
  ```shell
115
128
  pip3 install sanic-security --upgrade
116
129
  ```
@@ -138,41 +151,119 @@ You can load environment variables with a different prefix via `config.load_envi
138
151
 
139
152
  * Default configuration values:
140
153
 
141
- | Key | Value | Description |
142
- |---------------------------------------|------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
143
- | **SECRET** | This is a big secret. Shhhhh | The secret used for generating and signing JWTs. This should be a string unique to your application. Keep it safe. |
144
- | **PUBLIC_SECRET** | None | The secret used for verifying and decoding JWTs and can be publicly shared. This should be a string unique to your application. |
145
- | **SESSION_SAMESITE** | Strict | The SameSite attribute of session cookies. |
146
- | **SESSION_SECURE** | True | The Secure attribute of session cookies. |
147
- | **SESSION_HTTPONLY** | True | The HttpOnly attribute of session cookies. HIGHLY recommended that you do not turn this off, unless you know what you are doing. |
148
- | **SESSION_DOMAIN** | None | The Domain attribute of session cookies. |
149
- | **SESSION_ENCODING_ALGORITHM** | HS256 | The algorithm used to encode and decode session JWT's. |
150
- | **SESSION_PREFIX** | tkn | Prefix attached to the beginning of session cookies. |
151
- | **MAX_CHALLENGE_ATTEMPTS** | 3 | The maximum amount of session challenge attempts allowed. |
152
- | **CAPTCHA_SESSION_EXPIRATION** | 180 | The amount of seconds till captcha session expiration on creation. Setting to 0 will disable expiration. |
153
- | **CAPTCHA_FONT** | captcha-font.ttf | The file path to the font being used for captcha generation. Several fonts can be used by separating them via comma. |
154
- | **CAPTCHA_VOICE** | captcha-voice/ | The directory of the voice library being used for audio captcha generation. |
155
- | **TWO_STEP_SESSION_EXPIRATION** | 300 | The amount of seconds till two-step session expiration on creation. Setting to 0 will disable expiration. |
156
- | **AUTHENTICATION_SESSION_EXPIRATION** | 86400 | The amount of seconds till authentication session expiration on creation. Setting to 0 will disable expiration. |
157
- | **AUTHENTICATION_REFRESH_EXPIRATION** | 604800 | The amount of seconds till authentication refresh expiration. Setting to 0 will disable refresh mechanism. |
158
- | **ALLOW_LOGIN_WITH_USERNAME** | False | Allows login via username; unique constraint is disabled when set to false. |
159
- | **INITIAL_ADMIN_EMAIL** | admin@example.com | Email used when creating the initial admin account. |
160
- | **INITIAL_ADMIN_PASSWORD** | admin123 | Password used when creating the initial admin account. |
154
+ | Key | Value | Description |
155
+ |---------------------------------------|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
156
+ | **SECRET** | This is a big secret. Shhhhh | The secret used for generating and signing JWTs. This should be a string unique to your application. Keep it safe. |
157
+ | **PUBLIC_SECRET** | None | The secret used for verifying and decoding JWTs and can be publicly shared. This should be a string unique to your application. |
158
+ | **OAUTH_CLIENT** | None | The client ID provided by the OAuth provider, this is used to identify the application making the OAuth request. |
159
+ | **OAUTH_SECRET** | None | The client secret provided by the OAuth provider, this is used in conjunction with the client ID to authenticate the application. |
160
+ | **SESSION_SAMESITE** | Strict | The SameSite attribute of session cookies. |
161
+ | **SESSION_SECURE** | True | The Secure attribute of session cookies. |
162
+ | **SESSION_HTTPONLY** | True | The HttpOnly attribute of session cookies. HIGHLY recommended that you do not turn this off, unless you know what you are doing. |
163
+ | **SESSION_DOMAIN** | None | The Domain attribute of session cookies. |
164
+ | **SESSION_ENCODING_ALGORITHM** | HS256 | The algorithm used to encode and decode session JWT's. |
165
+ | **SESSION_PREFIX** | tkn | Prefix attached to the beginning of session cookies. |
166
+ | **MAX_CHALLENGE_ATTEMPTS** | 3 | The maximum amount of session challenge attempts allowed. |
167
+ | **CAPTCHA_SESSION_EXPIRATION** | 180 | The amount of seconds till captcha session expiration on creation. Setting to 0 will disable expiration. |
168
+ | **CAPTCHA_FONT** | captcha-font.ttf | The file path to the font being used for captcha generation. Several fonts can be used by separating them via comma. |
169
+ | **CAPTCHA_VOICE** | captcha-voice/ | The directory of the voice library being used for audio captcha generation. |
170
+ | **TWO_STEP_SESSION_EXPIRATION** | 300 | The amount of seconds till two-step session expiration on creation. Setting to 0 will disable expiration. |
171
+ | **AUTHENTICATION_SESSION_EXPIRATION** | 86400 | The amount of seconds till authentication session expiration on creation. Setting to 0 will disable expiration. |
172
+ | **AUTHENTICATION_REFRESH_EXPIRATION** | 604800 | The amount of seconds till authentication refresh expiration. Setting to 0 will disable refresh mechanism. |
173
+ | **ALLOW_LOGIN_WITH_USERNAME** | False | Allows login via username; unique constraint is disabled when set to false. |
174
+ | **INITIAL_ADMIN_EMAIL** | admin@example.com | Email used when creating the initial admin account. |
175
+ | **INITIAL_ADMIN_PASSWORD** | admin123 | Password used when creating the initial admin account. |
161
176
 
162
177
  ## Usage
163
178
 
164
179
  Sanic Security's authentication and verification functionality is session based. A new session will be created for the user after the user logs in or requests some form of verification (two-step, captcha). The session data is then encoded into a JWT and stored on a cookie on the user’s browser. The session cookie is then sent
165
180
  along with every subsequent request. The server can then compare the session stored on the cookie against the session information stored in the database to verify user’s identity and send a response with the corresponding state.
166
181
 
167
- * Initialize sanic-security as follows:
182
+ * Initialize Sanic Security as follows:
168
183
  ```python
169
184
  initialize_security(app)
185
+ initialize_oauth(app) # Remove if not utilizing OAuth
170
186
  if __name__ == "__main__":
171
187
  app.run(host="127.0.0.1", port=8000, workers=1, debug=True)
172
188
  ```
173
189
 
174
190
  The tables in the below examples represent example [request form-data](https://sanicframework.org/en/guide/basics/request.html#form).
175
191
 
192
+ ## OAuth
193
+
194
+ OAuth2 provides users with a familiar experience by having them register/login using their existing credentials from other trusted services (such as Google, Discord, etc.).
195
+
196
+ This feature is designed to complement existing authentication protocols by linking a Sanic Security account with the user's OAuth credentials. As a result, developers can leverage all of Sanic Security's capabilities including robust session handling and account management.
197
+
198
+ * Define OAuth clients
199
+
200
+ You can [utilize various OAuth clients](https://frankie567.github.io/httpx-oauth/reference/httpx_oauth.clients/) based on your needs or [customize one](https://frankie567.github.io/httpx-oauth/usage/).
201
+ ID and secret should be stored and referenced via configuration.
202
+
203
+ ```python
204
+ discord_oauth = DiscordOAuth2(
205
+ "1325594509043830895",
206
+ "WNMYbkDJjGlC0ej60qM-50tC9mMy0EXa",
207
+ )
208
+ google_oauth = GoogleOAuth2(
209
+ "480512993828-e2e9tqtl2b8or62hc4l7hpoh478s3ni1.apps.googleusercontent.com",
210
+ "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",
211
+ )
212
+ ```
213
+
214
+ * Redirect to authorization URL
215
+
216
+ ```python
217
+ @app.route("api/security/oauth", methods=["GET", "POST"])
218
+ async def on_oauth_request(request):
219
+ return redirect(
220
+ await oauth_url(
221
+ google_oauth, "http://localhost:8000/api/security/oauth/callback"
222
+ )
223
+ )
224
+ ```
225
+
226
+ * Handle OAuth callback
227
+
228
+ ```python
229
+ @app.get("api/security/oauth/callback")
230
+ async def on_oauth_callback(request):
231
+ token_info, authentication_session = await oauth_callback(
232
+ request, google_oauth, "http://localhost:8000/api/security/oauth/callback"
233
+ )
234
+ response = json(
235
+ "Authorization successful.",
236
+ {"token_info": token_info, "auth_session": authentication_session.json},
237
+ )
238
+ oauth_encode(response, token_info)
239
+ authentication_session.encode(response)
240
+ return response
241
+ ```
242
+
243
+ * Get access token
244
+
245
+ ```python
246
+ @app.get("api/security/oauth/token")
247
+ async def on_oauth_token(request):
248
+ token_info = await decode_oauth(request, google_oauth)
249
+ return json(
250
+ "Access token retrieved.",
251
+ token_info,
252
+ )
253
+ ```
254
+
255
+ * Requires access token (This method is not called directly and instead used as a decorator)
256
+
257
+ ```python
258
+ @app.get("api/security/oauth/token")
259
+ @requires_oauth(google_oauth)
260
+ async def on_oauth_token(request):
261
+ return json(
262
+ "Access token retrieved.",
263
+ request.ctx.oauth,
264
+ )
265
+ ```
266
+
176
267
  ## Authentication
177
268
 
178
269
  * Registration (With two-step account verification)
@@ -324,7 +415,9 @@ or you can download/create your own and specify its path in the configuration.
324
415
  @app.get("api/security/captcha")
325
416
  async def on_captcha_img_request(request):
326
417
  captcha_session = await request_captcha(request)
327
- response = captcha_session.get_image() # Captcha: LJ0F3U
418
+ response = raw(
419
+ captcha_session.get_image(), content_type="image/jpeg"
420
+ ) # Captcha: LJ0F3U
328
421
  captcha_session.encode(response)
329
422
  return response
330
423
  ```
@@ -334,8 +427,12 @@ async def on_captcha_img_request(request):
334
427
  ```python
335
428
  @app.get("api/security/captcha/audio")
336
429
  async def on_captcha_audio_request(request):
337
- captcha_session = await CaptchaSession.decode(request)
338
- return captcha_session.get_audio() # Captcha: LJ0F3U
430
+ captcha_session = await request_captcha(request)
431
+ response = raw(
432
+ captcha_session.get_audio(), content_type="audio/mpeg"
433
+ ) # Captcha: LJ0F3U
434
+ captcha_session.encode(response)
435
+ return response
339
436
  ```
340
437
 
341
438
  * Attempt CAPTCHA
@@ -447,8 +544,10 @@ Wildcard permissions support the concept of multiple levels or parts. For exampl
447
544
  await assign_role(
448
545
  "Chat Room Moderator",
449
546
  account,
450
- "channels:view,delete, voice:*, account:suspend,mute",
451
547
  "Can read and delete messages in all chat rooms, suspend and mute accounts, and control voice chat.",
548
+ "channels:view,delete",
549
+ "voice:*",
550
+ "account:suspend,mute",
452
551
  )
453
552
  ```
454
553
 
@@ -467,7 +566,7 @@ async def on_check_perms(request):
467
566
 
468
567
  ```python
469
568
  @app.post("api/security/perms")
470
- @require_permissions("channels:view", "voice:*")
569
+ @requires_permission("channels:view", "voice:*")
471
570
  async def on_check_perms(request):
472
571
  return text("Account is authorized.")
473
572
  ```
@@ -485,7 +584,7 @@ async def on_check_roles(request):
485
584
 
486
585
  ```python
487
586
  @app.post("api/security/roles")
488
- @require_roles("Chat Room Moderator")
587
+ @requires_role("Chat Room Moderator")
489
588
  async def on_check_roles(request):
490
589
  return text("Account is authorized.")
491
590
  ```
@@ -582,16 +681,3 @@ Distributed under the MIT License. See `LICENSE` for more information.
582
681
  * PATCH version when you make backwards compatible bug fixes.
583
682
 
584
683
  [https://semver.org/](https://semver.org/)
585
-
586
- <!-- MARKDOWN LINKS & IMAGES -->
587
- <!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
588
- [contributors-shield]: https://img.shields.io/github/contributors/sunset-developer/sanic-security.svg?style=flat-square
589
- [contributors-url]: https://github.com/sunset-developer/sanic-security/graphs/contributors
590
- [forks-shield]: https://img.shields.io/github/forks/sunset-developer/sanic-security.svg?style=flat-square
591
- [forks-url]: https://github.com/sunset-developer/sanic-security/network/members
592
- [stars-shield]: https://img.shields.io/github/stars/sunset-developer/sanic-security.svg?style=flat-square
593
- [stars-url]: https://github.com/sunset-developer/sanic-security/stargazers
594
- [issues-shield]: https://img.shields.io/github/issues/sunset-developer/sanic-security.svg?style=flat-square
595
- [issues-url]: https://github.com/sunset-developer/sanic-security/issues
596
- [license-shield]: https://img.shields.io/github/license/sunset-developer/sanic-security.svg?style=flat-square
597
- [license-url]: https://github.com/sunset-developer/sanic-security/blob/master/LICENSE
@@ -31,6 +31,7 @@
31
31
  * [Installation](#installation)
32
32
  * [Configuration](#configuration)
33
33
  * [Usage](#usage)
34
+ * [OAuth](#oauth)
34
35
  * [Authentication](#authentication)
35
36
  * [CAPTCHA](#captcha)
36
37
  * [Two-step Verification](#two-step-verification)
@@ -48,6 +49,7 @@
48
49
  Sanic Security is an authentication, authorization, and verification library designed for use with the
49
50
  [Sanic](https://github.com/huge-success/sanic) framework.
50
51
 
52
+ * OAuth2 integration
51
53
  * Login, registration, and authentication with refresh mechanisms
52
54
  * Role based authorization with wildcard permissions
53
55
  * Image & audio CAPTCHA
@@ -71,7 +73,7 @@ pip3 install sanic-security
71
73
 
72
74
  * Install the Sanic Security pip package with the `cryptography` dependency included.
73
75
 
74
- If you are planning on encoding or decoding JWTs using certain digital signature algorithms (like RSA or ECDSA which use
76
+ If you're planning on encoding or decoding JWTs using certain digital signature algorithms (like RSA or ECDSA which use
75
77
  the public secret and private secret), you will need to install the `cryptography` library. This can be installed explicitly, or
76
78
  as an extra requirement.
77
79
 
@@ -79,7 +81,17 @@ as an extra requirement.
79
81
  pip3 install sanic-security[crypto]
80
82
  ````
81
83
 
82
- * Update sanic-security if already installed.
84
+ * Install the Sanic Security pip package with the `httpx-oauth` dependency included.
85
+
86
+ If you're planning on utilizing OAuth, you will need to install the `httpx-oauth` library. This can be installed explicitly, or
87
+ as an extra requirement.
88
+
89
+ ```shell
90
+ pip3 install sanic-security[oauth]
91
+ ````
92
+
93
+ * Update Sanic Security if already installed.
94
+
83
95
  ```shell
84
96
  pip3 install sanic-security --upgrade
85
97
  ```
@@ -107,41 +119,119 @@ You can load environment variables with a different prefix via `config.load_envi
107
119
 
108
120
  * Default configuration values:
109
121
 
110
- | Key | Value | Description |
111
- |---------------------------------------|------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
112
- | **SECRET** | This is a big secret. Shhhhh | The secret used for generating and signing JWTs. This should be a string unique to your application. Keep it safe. |
113
- | **PUBLIC_SECRET** | None | The secret used for verifying and decoding JWTs and can be publicly shared. This should be a string unique to your application. |
114
- | **SESSION_SAMESITE** | Strict | The SameSite attribute of session cookies. |
115
- | **SESSION_SECURE** | True | The Secure attribute of session cookies. |
116
- | **SESSION_HTTPONLY** | True | The HttpOnly attribute of session cookies. HIGHLY recommended that you do not turn this off, unless you know what you are doing. |
117
- | **SESSION_DOMAIN** | None | The Domain attribute of session cookies. |
118
- | **SESSION_ENCODING_ALGORITHM** | HS256 | The algorithm used to encode and decode session JWT's. |
119
- | **SESSION_PREFIX** | tkn | Prefix attached to the beginning of session cookies. |
120
- | **MAX_CHALLENGE_ATTEMPTS** | 3 | The maximum amount of session challenge attempts allowed. |
121
- | **CAPTCHA_SESSION_EXPIRATION** | 180 | The amount of seconds till captcha session expiration on creation. Setting to 0 will disable expiration. |
122
- | **CAPTCHA_FONT** | captcha-font.ttf | The file path to the font being used for captcha generation. Several fonts can be used by separating them via comma. |
123
- | **CAPTCHA_VOICE** | captcha-voice/ | The directory of the voice library being used for audio captcha generation. |
124
- | **TWO_STEP_SESSION_EXPIRATION** | 300 | The amount of seconds till two-step session expiration on creation. Setting to 0 will disable expiration. |
125
- | **AUTHENTICATION_SESSION_EXPIRATION** | 86400 | The amount of seconds till authentication session expiration on creation. Setting to 0 will disable expiration. |
126
- | **AUTHENTICATION_REFRESH_EXPIRATION** | 604800 | The amount of seconds till authentication refresh expiration. Setting to 0 will disable refresh mechanism. |
127
- | **ALLOW_LOGIN_WITH_USERNAME** | False | Allows login via username; unique constraint is disabled when set to false. |
128
- | **INITIAL_ADMIN_EMAIL** | admin@example.com | Email used when creating the initial admin account. |
129
- | **INITIAL_ADMIN_PASSWORD** | admin123 | Password used when creating the initial admin account. |
122
+ | Key | Value | Description |
123
+ |---------------------------------------|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|
124
+ | **SECRET** | This is a big secret. Shhhhh | The secret used for generating and signing JWTs. This should be a string unique to your application. Keep it safe. |
125
+ | **PUBLIC_SECRET** | None | The secret used for verifying and decoding JWTs and can be publicly shared. This should be a string unique to your application. |
126
+ | **OAUTH_CLIENT** | None | The client ID provided by the OAuth provider, this is used to identify the application making the OAuth request. |
127
+ | **OAUTH_SECRET** | None | The client secret provided by the OAuth provider, this is used in conjunction with the client ID to authenticate the application. |
128
+ | **SESSION_SAMESITE** | Strict | The SameSite attribute of session cookies. |
129
+ | **SESSION_SECURE** | True | The Secure attribute of session cookies. |
130
+ | **SESSION_HTTPONLY** | True | The HttpOnly attribute of session cookies. HIGHLY recommended that you do not turn this off, unless you know what you are doing. |
131
+ | **SESSION_DOMAIN** | None | The Domain attribute of session cookies. |
132
+ | **SESSION_ENCODING_ALGORITHM** | HS256 | The algorithm used to encode and decode session JWT's. |
133
+ | **SESSION_PREFIX** | tkn | Prefix attached to the beginning of session cookies. |
134
+ | **MAX_CHALLENGE_ATTEMPTS** | 3 | The maximum amount of session challenge attempts allowed. |
135
+ | **CAPTCHA_SESSION_EXPIRATION** | 180 | The amount of seconds till captcha session expiration on creation. Setting to 0 will disable expiration. |
136
+ | **CAPTCHA_FONT** | captcha-font.ttf | The file path to the font being used for captcha generation. Several fonts can be used by separating them via comma. |
137
+ | **CAPTCHA_VOICE** | captcha-voice/ | The directory of the voice library being used for audio captcha generation. |
138
+ | **TWO_STEP_SESSION_EXPIRATION** | 300 | The amount of seconds till two-step session expiration on creation. Setting to 0 will disable expiration. |
139
+ | **AUTHENTICATION_SESSION_EXPIRATION** | 86400 | The amount of seconds till authentication session expiration on creation. Setting to 0 will disable expiration. |
140
+ | **AUTHENTICATION_REFRESH_EXPIRATION** | 604800 | The amount of seconds till authentication refresh expiration. Setting to 0 will disable refresh mechanism. |
141
+ | **ALLOW_LOGIN_WITH_USERNAME** | False | Allows login via username; unique constraint is disabled when set to false. |
142
+ | **INITIAL_ADMIN_EMAIL** | admin@example.com | Email used when creating the initial admin account. |
143
+ | **INITIAL_ADMIN_PASSWORD** | admin123 | Password used when creating the initial admin account. |
130
144
 
131
145
  ## Usage
132
146
 
133
147
  Sanic Security's authentication and verification functionality is session based. A new session will be created for the user after the user logs in or requests some form of verification (two-step, captcha). The session data is then encoded into a JWT and stored on a cookie on the user’s browser. The session cookie is then sent
134
148
  along with every subsequent request. The server can then compare the session stored on the cookie against the session information stored in the database to verify user’s identity and send a response with the corresponding state.
135
149
 
136
- * Initialize sanic-security as follows:
150
+ * Initialize Sanic Security as follows:
137
151
  ```python
138
152
  initialize_security(app)
153
+ initialize_oauth(app) # Remove if not utilizing OAuth
139
154
  if __name__ == "__main__":
140
155
  app.run(host="127.0.0.1", port=8000, workers=1, debug=True)
141
156
  ```
142
157
 
143
158
  The tables in the below examples represent example [request form-data](https://sanicframework.org/en/guide/basics/request.html#form).
144
159
 
160
+ ## OAuth
161
+
162
+ OAuth2 provides users with a familiar experience by having them register/login using their existing credentials from other trusted services (such as Google, Discord, etc.).
163
+
164
+ This feature is designed to complement existing authentication protocols by linking a Sanic Security account with the user's OAuth credentials. As a result, developers can leverage all of Sanic Security's capabilities including robust session handling and account management.
165
+
166
+ * Define OAuth clients
167
+
168
+ You can [utilize various OAuth clients](https://frankie567.github.io/httpx-oauth/reference/httpx_oauth.clients/) based on your needs or [customize one](https://frankie567.github.io/httpx-oauth/usage/).
169
+ ID and secret should be stored and referenced via configuration.
170
+
171
+ ```python
172
+ discord_oauth = DiscordOAuth2(
173
+ "1325594509043830895",
174
+ "WNMYbkDJjGlC0ej60qM-50tC9mMy0EXa",
175
+ )
176
+ google_oauth = GoogleOAuth2(
177
+ "480512993828-e2e9tqtl2b8or62hc4l7hpoh478s3ni1.apps.googleusercontent.com",
178
+ "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",
179
+ )
180
+ ```
181
+
182
+ * Redirect to authorization URL
183
+
184
+ ```python
185
+ @app.route("api/security/oauth", methods=["GET", "POST"])
186
+ async def on_oauth_request(request):
187
+ return redirect(
188
+ await oauth_url(
189
+ google_oauth, "http://localhost:8000/api/security/oauth/callback"
190
+ )
191
+ )
192
+ ```
193
+
194
+ * Handle OAuth callback
195
+
196
+ ```python
197
+ @app.get("api/security/oauth/callback")
198
+ async def on_oauth_callback(request):
199
+ token_info, authentication_session = await oauth_callback(
200
+ request, google_oauth, "http://localhost:8000/api/security/oauth/callback"
201
+ )
202
+ response = json(
203
+ "Authorization successful.",
204
+ {"token_info": token_info, "auth_session": authentication_session.json},
205
+ )
206
+ oauth_encode(response, token_info)
207
+ authentication_session.encode(response)
208
+ return response
209
+ ```
210
+
211
+ * Get access token
212
+
213
+ ```python
214
+ @app.get("api/security/oauth/token")
215
+ async def on_oauth_token(request):
216
+ token_info = await decode_oauth(request, google_oauth)
217
+ return json(
218
+ "Access token retrieved.",
219
+ token_info,
220
+ )
221
+ ```
222
+
223
+ * Requires access token (This method is not called directly and instead used as a decorator)
224
+
225
+ ```python
226
+ @app.get("api/security/oauth/token")
227
+ @requires_oauth(google_oauth)
228
+ async def on_oauth_token(request):
229
+ return json(
230
+ "Access token retrieved.",
231
+ request.ctx.oauth,
232
+ )
233
+ ```
234
+
145
235
  ## Authentication
146
236
 
147
237
  * Registration (With two-step account verification)
@@ -293,7 +383,9 @@ or you can download/create your own and specify its path in the configuration.
293
383
  @app.get("api/security/captcha")
294
384
  async def on_captcha_img_request(request):
295
385
  captcha_session = await request_captcha(request)
296
- response = captcha_session.get_image() # Captcha: LJ0F3U
386
+ response = raw(
387
+ captcha_session.get_image(), content_type="image/jpeg"
388
+ ) # Captcha: LJ0F3U
297
389
  captcha_session.encode(response)
298
390
  return response
299
391
  ```
@@ -303,8 +395,12 @@ async def on_captcha_img_request(request):
303
395
  ```python
304
396
  @app.get("api/security/captcha/audio")
305
397
  async def on_captcha_audio_request(request):
306
- captcha_session = await CaptchaSession.decode(request)
307
- return captcha_session.get_audio() # Captcha: LJ0F3U
398
+ captcha_session = await request_captcha(request)
399
+ response = raw(
400
+ captcha_session.get_audio(), content_type="audio/mpeg"
401
+ ) # Captcha: LJ0F3U
402
+ captcha_session.encode(response)
403
+ return response
308
404
  ```
309
405
 
310
406
  * Attempt CAPTCHA
@@ -416,8 +512,10 @@ Wildcard permissions support the concept of multiple levels or parts. For exampl
416
512
  await assign_role(
417
513
  "Chat Room Moderator",
418
514
  account,
419
- "channels:view,delete, voice:*, account:suspend,mute",
420
515
  "Can read and delete messages in all chat rooms, suspend and mute accounts, and control voice chat.",
516
+ "channels:view,delete",
517
+ "voice:*",
518
+ "account:suspend,mute",
421
519
  )
422
520
  ```
423
521
 
@@ -436,7 +534,7 @@ async def on_check_perms(request):
436
534
 
437
535
  ```python
438
536
  @app.post("api/security/perms")
439
- @require_permissions("channels:view", "voice:*")
537
+ @requires_permission("channels:view", "voice:*")
440
538
  async def on_check_perms(request):
441
539
  return text("Account is authorized.")
442
540
  ```
@@ -454,7 +552,7 @@ async def on_check_roles(request):
454
552
 
455
553
  ```python
456
554
  @app.post("api/security/roles")
457
- @require_roles("Chat Room Moderator")
555
+ @requires_role("Chat Room Moderator")
458
556
  async def on_check_roles(request):
459
557
  return text("Account is authorized.")
460
558
  ```
@@ -550,17 +648,4 @@ Distributed under the MIT License. See `LICENSE` for more information.
550
648
 
551
649
  * PATCH version when you make backwards compatible bug fixes.
552
650
 
553
- [https://semver.org/](https://semver.org/)
554
-
555
- <!-- MARKDOWN LINKS & IMAGES -->
556
- <!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
557
- [contributors-shield]: https://img.shields.io/github/contributors/sunset-developer/sanic-security.svg?style=flat-square
558
- [contributors-url]: https://github.com/sunset-developer/sanic-security/graphs/contributors
559
- [forks-shield]: https://img.shields.io/github/forks/sunset-developer/sanic-security.svg?style=flat-square
560
- [forks-url]: https://github.com/sunset-developer/sanic-security/network/members
561
- [stars-shield]: https://img.shields.io/github/stars/sunset-developer/sanic-security.svg?style=flat-square
562
- [stars-url]: https://github.com/sunset-developer/sanic-security/stargazers
563
- [issues-shield]: https://img.shields.io/github/issues/sunset-developer/sanic-security.svg?style=flat-square
564
- [issues-url]: https://github.com/sunset-developer/sanic-security/issues
565
- [license-shield]: https://img.shields.io/github/license/sunset-developer/sanic-security.svg?style=flat-square
566
- [license-url]: https://github.com/sunset-developer/sanic-security/blob/master/LICENSE
651
+ [https://semver.org/](https://semver.org/)
@@ -4,13 +4,12 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sanic-security"
7
- version = "1.15.3"
7
+ version = "1.16.0"
8
8
  requires-python = ">=3.8"
9
9
  dependencies = [
10
10
  "tortoise-orm>=0.17.0",
11
11
  "pyjwt>=1.7.0",
12
12
  "captcha>=0.4",
13
- "pillow>=9.5.0",
14
13
  "argon2-cffi>=20.1.0",
15
14
  "sanic>=21.3.0",
16
15
  ]
@@ -23,7 +22,8 @@ keywords = ["security", "authentication", "authorization", "verification", "asyn
23
22
  classifiers = ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Topic :: Security", "License :: OSI Approved :: MIT License", "Programming Language :: Python"]
24
23
 
25
24
  [project.optional-dependencies]
26
- dev = ["httpx", "black", "blacken-docs", "pdoc3", "cryptography"]
25
+ oauth= ["httpx-oauth>=0.16.1"]
26
+ dev = ["httpx-oauth", "black", "blacken-docs", "pdoc3", "cryptography"]
27
27
  crypto = ["cryptography>=3.3.1"]
28
28
 
29
29
  [project.urls]