sanic-security 1.12.0__py3-none-any.whl → 1.12.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.
@@ -195,10 +195,10 @@ async def fulfill_second_factor(request: Request) -> AuthenticationSession:
195
195
  return authentication_session
196
196
 
197
197
 
198
- async def authenticate(request: Request) -> tuple[bool, AuthenticationSession]:
198
+ async def authenticate(request: Request) -> AuthenticationSession:
199
199
  """
200
200
  Validates client's authentication session and account. New/Refreshed session automatically returned
201
- if expired during authentication, requires encoding.
201
+ if client's session expired during authentication, requires encoding.
202
202
 
203
203
  Args:
204
204
  request (Request): Sanic request parameter.
@@ -214,6 +214,7 @@ async def authenticate(request: Request) -> tuple[bool, AuthenticationSession]:
214
214
  UnverifiedError
215
215
  DisabledError
216
216
  SecondFactorRequiredError
217
+ ExpiredError
217
218
  """
218
219
  authentication_session = await AuthenticationSession.decode(request)
219
220
  try:
@@ -227,8 +228,8 @@ async def authenticate(request: Request) -> tuple[bool, AuthenticationSession]:
227
228
 
228
229
  def requires_authentication(arg=None):
229
230
  """
230
- Validates client's authentication session and account. New/Refreshed session automatically returned if expired
231
- during authentication, requires encoding.
231
+ Validates client's authentication session and account. New/Refreshed session automatically returned
232
+ if client's session expired during authentication, requires encoding.
232
233
 
233
234
  Example:
234
235
  This method is not called directly and instead used as a decorator:
@@ -245,6 +246,7 @@ def requires_authentication(arg=None):
245
246
  DeactivatedError
246
247
  UnverifiedError
247
248
  DisabledError
249
+ ExpiredError
248
250
  """
249
251
 
250
252
  def decorator(func):
@@ -272,7 +274,7 @@ def create_initial_admin_account(app: Sanic) -> None:
272
274
  role = await Role.filter(name="Head Admin").get()
273
275
  except DoesNotExist:
274
276
  role = await Role.create(
275
- description="Has the ability to control any aspect of the API, assign sparingly.",
277
+ description="Has root abilities, assign sparingly.",
276
278
  permissions="*:*",
277
279
  name="Head Admin",
278
280
  )
@@ -283,9 +285,7 @@ def create_initial_admin_account(app: Sanic) -> None:
283
285
  await account.fetch_related("roles")
284
286
  if role not in account.roles:
285
287
  await account.roles.add(role)
286
- logger.warning(
287
- 'The initial admin account role "Head Admin" was removed and has been reinstated.'
288
- )
288
+ logger.warning("Initial admin account role has been reinstated.")
289
289
  except DoesNotExist:
290
290
  account = await Account.create(
291
291
  username="Head-Admin",
@@ -39,7 +39,7 @@ DEFAULT_CONFIG = {
39
39
  "CAPTCHA_FONT": "captcha-font.ttf",
40
40
  "TWO_STEP_SESSION_EXPIRATION": 300,
41
41
  "AUTHENTICATION_SESSION_EXPIRATION": 86400,
42
- "AUTHENTICATION_REFRESH_EXPIRATION": 2592000,
42
+ "AUTHENTICATION_REFRESH_EXPIRATION": 604800,
43
43
  "ALLOW_LOGIN_WITH_USERNAME": False,
44
44
  "INITIAL_ADMIN_EMAIL": "admin@example.com",
45
45
  "INITIAL_ADMIN_PASSWORD": "admin123",
@@ -65,7 +65,7 @@ class Config(dict):
65
65
  CAPTCHA_FONT (str): The file path to the font being used for captcha generation.
66
66
  TWO_STEP_SESSION_EXPIRATION (int): The amount of seconds till two-step session expiration on creation. Setting to 0 will disable expiration.
67
67
  AUTHENTICATION_SESSION_EXPIRATION (int): The amount of seconds till authentication session expiration on creation. Setting to 0 will disable expiration.
68
- AUTHENTICATION_REFRESH_EXPIRATION (int): The amount of seconds till authentication session refresh expiration.
68
+ AUTHENTICATION_REFRESH_EXPIRATION (int): The amount of seconds till authentication session refresh expiration. Setting to 0 will disable refresh mechanism.
69
69
  ALLOW_LOGIN_WITH_USERNAME (bool): Allows login via username and email.
70
70
  INITIAL_ADMIN_EMAIL (str): Email used when creating the initial admin account.
71
71
  INITIAL_ADMIN_PASSWORD (str): Password used when creating the initial admin account.
@@ -128,7 +128,11 @@ class DeactivatedError(SessionError):
128
128
  Raised when session is deactivated.
129
129
  """
130
130
 
131
- def __init__(self, message: str = "Session is deactivated.", code: int = 401):
131
+ def __init__(
132
+ self,
133
+ message: str = "Session has been deactivated or refreshed.",
134
+ code: int = 401,
135
+ ):
132
136
  super().__init__(message, code)
133
137
 
134
138
 
sanic_security/models.py CHANGED
@@ -524,13 +524,13 @@ class AuthenticationSession(Session):
524
524
  Used to authenticate and identify a client.
525
525
 
526
526
  Attributes:
527
- requires_second_factor (bool): Determines if session requires a second factor.
528
527
  refresh_expiration_date (bool): Date and time the session can no longer be refreshed.
529
- is_refresh (bool): Will only be true when instantiated during refresh of expired session.
528
+ requires_second_factor (bool): Determines if session requires a second factor.
529
+ is_refresh (bool): Will only be true once when instantiated during refresh of expired session.
530
530
  """
531
531
 
532
- requires_second_factor: bool = fields.BooleanField(default=False)
533
532
  refresh_expiration_date: datetime.datetime = fields.DatetimeField(null=True)
533
+ requires_second_factor: bool = fields.BooleanField(default=False)
534
534
  is_refresh: bool = False
535
535
 
536
536
  def validate(self) -> None:
@@ -549,7 +549,7 @@ class AuthenticationSession(Session):
549
549
 
550
550
  async def refresh(self, request: Request):
551
551
  """
552
- Seamlessly creates new session if within refresh date.
552
+ Refreshes session if expired and within refresh date.
553
553
 
554
554
  Raises:
555
555
  DeletedError
@@ -566,7 +566,8 @@ class AuthenticationSession(Session):
566
566
  raise NotExpiredError()
567
567
  except ExpiredError as e:
568
568
  if (
569
- datetime.datetime.now(datetime.timezone.utc)
569
+ self.refresh_expiration_date
570
+ and datetime.datetime.now(datetime.timezone.utc)
570
571
  <= self.refresh_expiration_date
571
572
  ):
572
573
  self.active = False
@@ -19,7 +19,7 @@ from sanic_security.authorization import (
19
19
  check_roles,
20
20
  )
21
21
  from sanic_security.configuration import config as security_config
22
- from sanic_security.exceptions import SecurityError, CredentialsError
22
+ from sanic_security.exceptions import SecurityError
23
23
  from sanic_security.models import Account, CaptchaSession, AuthenticationSession
24
24
  from sanic_security.utils import json
25
25
  from sanic_security.verification import (
@@ -110,7 +110,7 @@ async def on_login(request):
110
110
  )
111
111
  two_step_session.encode(response)
112
112
  else:
113
- response = json("Login successful!", authentication_session.bearer.json)
113
+ response = json("Login successful!", authentication_session.json)
114
114
  authentication_session.encode(response)
115
115
  return response
116
116
 
@@ -148,7 +148,7 @@ async def on_logout(request):
148
148
  Logout of currently logged in account.
149
149
  """
150
150
  authentication_session = await logout(request)
151
- response = json("Logout successful!", authentication_session.bearer.json)
151
+ response = json("Logout successful!", authentication_session.json)
152
152
  return response
153
153
 
154
154
 
@@ -170,11 +170,19 @@ async def on_authenticate(request):
170
170
  "refresh": authentication_session.is_refresh,
171
171
  },
172
172
  )
173
- if authentication_session.is_refresh:
174
- request.ctx.authentication_session.encode(response)
175
173
  return response
176
174
 
177
175
 
176
+ @app.on_response
177
+ async def authentication_refresh_encoder(request, response):
178
+ try:
179
+ authentication_session = request.ctx.authentication_session
180
+ if authentication_session.is_refresh:
181
+ authentication_session.encode(response)
182
+ except AttributeError:
183
+ pass
184
+
185
+
178
186
  @app.post("api/test/auth/expire")
179
187
  @requires_authentication
180
188
  async def on_authentication_expire(request):
@@ -306,6 +306,8 @@ class LoginTest(TestCase):
306
306
  "http://127.0.0.1:8000/api/test/auth",
307
307
  )
308
308
  assert authenticate_response.status_code == 200, authenticate_response.text
309
+ logout_response = self.client.post("http://127.0.0.1:8000/api/test/auth/logout")
310
+ assert logout_response.status_code == 200, logout_response.text
309
311
 
310
312
 
311
313
  class VerificationTest(TestCase):
@@ -568,8 +570,9 @@ class MiscTest(TestCase):
568
570
  "http://127.0.0.1:8000/api/test/auth",
569
571
  )
570
572
  assert (
571
- json.loads(authenticate_refresh_response.text)["data"]["refresh"] is True
573
+ authenticate_refresh_response.status_code == 200
572
574
  ), authenticate_refresh_response.text
575
+ assert json.loads(authenticate_refresh_response.text)["data"]["refresh"] is True
573
576
  authenticate_response = self.client.post(
574
577
  "http://127.0.0.1:8000/api/test/auth",
575
578
  ) # Since session refresh handling is complete, it will be returned as a regular session now.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: sanic-security
3
- Version: 1.12.0
4
- Summary: An effective, simple, and async security library for the Sanic framework.
3
+ Version: 1.12.2
4
+ Summary: An async security library for the Sanic framework.
5
5
  Author-email: Aidan Stewart <na.stewart365@gmail.com>
6
6
  Project-URL: Documentation, https://security.na-stewart.com/
7
7
  Project-URL: Repository, https://github.com/na-stewart/sanic-security
@@ -48,7 +48,7 @@ Requires-Dist: cryptography ; extra == 'dev'
48
48
  <p align="center">
49
49
  <h3 align="center">Sanic Security</h3>
50
50
  <p align="center">
51
- An effective, simple, and async security library for the Sanic framework.
51
+ An async security library for the Sanic framework.
52
52
  </p>
53
53
  </p>
54
54
 
@@ -77,7 +77,6 @@ Requires-Dist: cryptography ; extra == 'dev'
77
77
  ## About The Project
78
78
 
79
79
  Sanic Security is an authentication, authorization, and verification library designed for use with [Sanic](https://github.com/huge-success/sanic).
80
- This library contains a variety of features including:
81
80
 
82
81
  * Login, registration, and authentication with refresh mechanisms
83
82
  * Two-factor authentication
@@ -85,7 +84,7 @@ This library contains a variety of features including:
85
84
  * Two-step verification
86
85
  * Role based authorization with wildcard permissions
87
86
 
88
- Please visit [security.na-stewart.com](https://security.na-stewart.com) for documentation and [click here for a comprehensive implementation guide](https://blog.na-stewart.com/entry?id=3).
87
+ Please visit [security.na-stewart.com](https://security.na-stewart.com) for documentation and [here for an implementation guide](https://blog.na-stewart.com/entry?id=3).
89
88
 
90
89
  <!-- GETTING STARTED -->
91
90
  ## Getting Started
@@ -103,7 +102,7 @@ pip3 install sanic-security
103
102
 
104
103
  If you are planning on encoding or decoding JWTs using certain digital signature algorithms (like RSA or ECDSA which use
105
104
  the public secret and private secret), you will need to install the `cryptography` library. This can be installed explicitly, or
106
- as a required extra in the `sanic-security` requirement.
105
+ as an extra requirement.
107
106
 
108
107
  ```shell
109
108
  pip3 install sanic-security[crypto]
@@ -138,7 +137,7 @@ You can also use the update() method like on regular dictionaries.
138
137
  Any environment variables defined with the SANIC_SECURITY_ prefix will be applied to the config. For example, setting
139
138
  SANIC_SECURITY_SECRET will be loaded by the application automatically and fed into the SECRET config variable.
140
139
 
141
- You can load environment variables with a different prefix via calling the `config.load_environment_variables("NEW_PREFIX_")` method.
140
+ You can load environment variables with a different prefix via `config.load_environment_variables("NEW_PREFIX_")` method.
142
141
 
143
142
  * Default configuration values:
144
143
 
@@ -157,7 +156,7 @@ You can load environment variables with a different prefix via calling the `conf
157
156
  | **CAPTCHA_FONT** | captcha-font.ttf | The file path to the font being used for captcha generation. |
158
157
  | **TWO_STEP_SESSION_EXPIRATION** | 200 | The amount of seconds till two-step session expiration on creation. Setting to 0 will disable expiration. |
159
158
  | **AUTHENTICATION_SESSION_EXPIRATION** | 86400 | The amount of seconds till authentication session expiration on creation. Setting to 0 will disable expiration. |
160
- | **AUTHENTICATION_REFRESH_EXPIRATION** | 2592000 | The amount of seconds till authentication refresh expiration. |
159
+ | **AUTHENTICATION_REFRESH_EXPIRATION** | 604800 | The amount of seconds till authentication refresh expiration. Setting to 0 will disable refresh mechanism. |
161
160
  | **ALLOW_LOGIN_WITH_USERNAME** | False | Allows login via username and email. |
162
161
  | **INITIAL_ADMIN_EMAIL** | admin@example.com | Email used when creating the initial admin account. |
163
162
  | **INITIAL_ADMIN_PASSWORD** | admin123 | Password used when creating the initial admin account. |
@@ -181,7 +180,7 @@ This account can be logged into and has complete authoritative access. Login cre
181
180
  app.run(host="127.0.0.1", port=8000)
182
181
  ```
183
182
 
184
- * Registration (With Two-step Verification)
183
+ * Registration (With two-step account verification)
185
184
 
186
185
  Phone can be null or empty.
187
186
 
@@ -214,7 +213,7 @@ Verifies the client's account via two-step session code.
214
213
 
215
214
  | Key | Value |
216
215
  |----------|--------|
217
- | **code** | AJ8HGD |
216
+ | **code** | 197251 |
218
217
 
219
218
  ```python
220
219
  @app.post("api/security/verify")
@@ -223,7 +222,7 @@ async def on_verify(request):
223
222
  return json("You have verified your account and may login!", two_step_session.json)
224
223
  ```
225
224
 
226
- * Login (With Two-factor Authentication)
225
+ * Login (With two-factor authentication)
227
226
 
228
227
  Login credentials are retrieved via the Authorization header. Credentials are constructed by first combining the
229
228
  username and the password with a colon (aladdin:opensesame), and then by encoding the resulting string in base64
@@ -256,7 +255,7 @@ Fulfills client authentication session's second factor requirement via two-step
256
255
 
257
256
  | Key | Value |
258
257
  |----------|--------|
259
- | **code** | BG5KLP |
258
+ | **code** | 197251 |
260
259
 
261
260
  ```python
262
261
  @app.post("api/security/fulfill-2fa")
@@ -296,7 +295,7 @@ async def on_logout(request):
296
295
 
297
296
  * Authenticate
298
297
 
299
- New/Refreshed session automatically returned if expired during authentication, requires encoding.
298
+ New/Refreshed session returned if client's session expired during authentication, requires encoding.
300
299
 
301
300
  ```python
302
301
  @app.post("api/security/auth")
@@ -311,15 +310,15 @@ async def on_authenticate(request):
311
310
  return response
312
311
  ```
313
312
 
314
- * Requires Authentication (This method is not called directly and instead used as a decorator.)
313
+ * Requires Authentication (This method is not called directly and instead used as a decorator)
315
314
 
316
- New/Refreshed session automatically returned if expired during authentication, requires encoding.
315
+ New/Refreshed session returned if client's session expired during authentication, requires encoding.
317
316
 
318
317
  ```python
319
318
  @app.post("api/security/auth")
320
319
  @requires_authentication
321
320
  async def on_authenticate(request):
322
- authentication_session = request.ctx.authentication_session.json
321
+ authentication_session = request.ctx.authentication_session
323
322
  response = json(
324
323
  "You have been authenticated.",
325
324
  authentication_session.json,
@@ -329,6 +328,21 @@ async def on_authenticate(request):
329
328
  return response
330
329
  ```
331
330
 
331
+ * Authentication Middleware
332
+
333
+ Refreshed session can be encoded automatically via middleware.
334
+
335
+ ```python
336
+ @app.on_response
337
+ async def authentication_refresh_encoder(request, response):
338
+ try:
339
+ authentication_session = request.ctx.authentication_session
340
+ if authentication_session.is_refresh:
341
+ authentication_session.encode(response)
342
+ except AttributeError:
343
+ pass
344
+ ```
345
+
332
346
  ## Captcha
333
347
 
334
348
  A pre-existing font for captcha challenges is included in the Sanic Security repository. You may set your own font by
@@ -344,7 +358,7 @@ downloading a .ttf font and defining the file's path in the configuration.
344
358
  @app.get("api/security/captcha")
345
359
  async def on_captcha_img_request(request):
346
360
  captcha_session = await request_captcha(request)
347
- response = captcha_session.get_image() # Captcha: FV9NMQ
361
+ response = captcha_session.get_image() # Captcha: 192731
348
362
  captcha_session.encode(response)
349
363
  return response
350
364
  ```
@@ -353,7 +367,7 @@ async def on_captcha_img_request(request):
353
367
 
354
368
  | Key | Value |
355
369
  |-------------|--------|
356
- | **captcha** | FV9NMQ |
370
+ | **captcha** | 192731 |
357
371
 
358
372
  ```python
359
373
  @app.post("api/security/captcha")
@@ -362,11 +376,11 @@ async def on_captcha(request):
362
376
  return json("Captcha attempt successful!", captcha_session.json)
363
377
  ```
364
378
 
365
- * Requires Captcha (This method is not called directly and instead used as a decorator.)
379
+ * Requires Captcha (This method is not called directly and instead used as a decorator)
366
380
 
367
381
  | Key | Value |
368
382
  |-------------|--------|
369
- | **captcha** | FV9NMQ |
383
+ | **captcha** | 192731 |
370
384
 
371
385
  ```python
372
386
  @app.post("api/security/captcha")
@@ -388,9 +402,9 @@ Two-step verification should be integrated with other custom functionality. For
388
402
  ```python
389
403
  @app.post("api/security/two-step/request")
390
404
  async def on_two_step_request(request):
391
- two_step_session = await request_two_step_verification(request)
405
+ two_step_session = await request_two_step_verification(request) # Code = 197251
392
406
  await email_code(
393
- two_step_session.bearer.email, two_step_session.code # Code = 197251
407
+ two_step_session.bearer.email, two_step_session.code
394
408
  ) # Custom method for emailing verification code.
395
409
  response = json("Verification request successful!", two_step_session.json)
396
410
  two_step_session.encode(response)
@@ -402,9 +416,9 @@ async def on_two_step_request(request):
402
416
  ```python
403
417
  @app.post("api/security/two-step/resend")
404
418
  async def on_two_step_resend(request):
405
- two_step_session = await TwoStepSession.decode(request)
419
+ two_step_session = await TwoStepSession.decode(request) # Code = 197251
406
420
  await email_code(
407
- two_step_session.bearer.email, two_step_session.code # Code = 197251
421
+ two_step_session.bearer.email, two_step_session.code
408
422
  ) # Custom method for emailing verification code.
409
423
  return json("Verification code resend successful!", two_step_session.json)
410
424
  ```
@@ -413,7 +427,7 @@ async def on_two_step_resend(request):
413
427
 
414
428
  | Key | Value |
415
429
  |----------|--------|
416
- | **code** | DT6JZX |
430
+ | **code** | 197251 |
417
431
 
418
432
  ```python
419
433
  @app.post("api/security/two-step")
@@ -423,11 +437,11 @@ async def on_two_step_verification(request):
423
437
  return response
424
438
  ```
425
439
 
426
- * Requires Two-step Verification (This method is not called directly and instead used as a decorator.)
440
+ * Requires Two-step Verification (This method is not called directly and instead used as a decorator)
427
441
 
428
442
  | Key | Value |
429
443
  |----------|--------|
430
- | **code** | DT6JZX |
444
+ | **code** | 197251 |
431
445
 
432
446
  ```python
433
447
  @app.post("api/security/two-step")
@@ -492,7 +506,7 @@ async def on_check_roles(request):
492
506
  return text("Account is authorized.")
493
507
  ```
494
508
 
495
- * Require Roles (This method is not called directly and instead used as a decorator.)
509
+ * Require Roles (This method is not called directly and instead used as a decorator)
496
510
 
497
511
  ```python
498
512
  @app.post("api/security/roles")
@@ -0,0 +1,16 @@
1
+ sanic_security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ sanic_security/authentication.py,sha256=j-nFOLuNBcacKH34J04JIbsKSZ2JMH33ZqnS6vipwfQ,12508
3
+ sanic_security/authorization.py,sha256=aQztMiZG9LDctr_C6QEzO5qScwbxpiLk96XVxwdCChM,6921
4
+ sanic_security/configuration.py,sha256=p44nTSrBQQSJZYN6qJEod_Ettf90rRNlmPxmNzxqQ9A,5514
5
+ sanic_security/exceptions.py,sha256=8c3xoQSiIKfSiOQOtw49RG8Qdlc3vZDzqjrEnPad4Ds,5411
6
+ sanic_security/models.py,sha256=aB1fFCutUHDAg8jG3_VdZijOblXnSYh99gyA5fOm1u4,20528
7
+ sanic_security/utils.py,sha256=Zgde7W69ixwv_H8eTs7indO5_U2Jvq62YUpG6ipN768,2629
8
+ sanic_security/verification.py,sha256=vrxYborEOBKEirOHczul9WYub5j6T2ldXE1gsoA8iyY,7503
9
+ sanic_security/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ sanic_security/test/server.py,sha256=qQtbQh8m9QYf4g1SL8QJbOyyJzAXFaNmDyBxU8b6RBc,12627
11
+ sanic_security/test/tests.py,sha256=Hg40wlZfC-CDZX6lIjeT6uXy-3BJMc4ChJsnCRCBIu8,22459
12
+ sanic_security-1.12.2.dist-info/LICENSE,sha256=sXlJs9_mG-dCkPfWsDnuzydJWagS82E2gYtkVH9enHA,1100
13
+ sanic_security-1.12.2.dist-info/METADATA,sha256=YRKIzZgdU5Ka7QXR3Q0DdAj5dnNgHVh4P9vaVi03wV0,23954
14
+ sanic_security-1.12.2.dist-info/WHEEL,sha256=mguMlWGMX-VHnMpKOjjQidIo1ssRlCFu4a4mBpz1s2M,91
15
+ sanic_security-1.12.2.dist-info/top_level.txt,sha256=ZybkhHXSjfzhmv8XeqLvnNmLmv21Z0oPX6Ep4DJN8b0,15
16
+ sanic_security-1.12.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.1.0)
2
+ Generator: setuptools (70.1.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,16 +0,0 @@
1
- sanic_security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- sanic_security/authentication.py,sha256=haCrLHF6S-goSmsgQAvI8m59ccSAqjdYVK1ObS2oE_k,12546
3
- sanic_security/authorization.py,sha256=aQztMiZG9LDctr_C6QEzO5qScwbxpiLk96XVxwdCChM,6921
4
- sanic_security/configuration.py,sha256=U-xUgceT5ZRjbxYocrzhxyJYFBkobCrlxLNMVGJNX2k,5470
5
- sanic_security/exceptions.py,sha256=D_7uq9wubZL2C6F4-jxGWUaXUdJtyR6V0tGL_JWw82k,5357
6
- sanic_security/models.py,sha256=RET2FjOgO-bOdmTYeKgR3zmdSScu9Z6PV52rVGd1fJI,20474
7
- sanic_security/utils.py,sha256=Zgde7W69ixwv_H8eTs7indO5_U2Jvq62YUpG6ipN768,2629
8
- sanic_security/verification.py,sha256=vrxYborEOBKEirOHczul9WYub5j6T2ldXE1gsoA8iyY,7503
9
- sanic_security/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- sanic_security/test/server.py,sha256=Tv9KR_BiSb-6Fx7BCxPPPOeWyKUOEFLTptn9dysEbAo,12458
11
- sanic_security/test/tests.py,sha256=lAI-yd4HVttYKazSxOmyRMCcL_d8eIxciBMrRKE1v4o,22231
12
- sanic_security-1.12.0.dist-info/LICENSE,sha256=sXlJs9_mG-dCkPfWsDnuzydJWagS82E2gYtkVH9enHA,1100
13
- sanic_security-1.12.0.dist-info/METADATA,sha256=UdYofgFLFs331K0j8m0PXJ83bDKjGGjv_lnDmKQdpkw,23698
14
- sanic_security-1.12.0.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
15
- sanic_security-1.12.0.dist-info/top_level.txt,sha256=ZybkhHXSjfzhmv8XeqLvnNmLmv21Z0oPX6Ep4DJN8b0,15
16
- sanic_security-1.12.0.dist-info/RECORD,,