sanic-security 1.12.7__py3-none-any.whl → 1.13.1__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.
- sanic_security/authentication.py +82 -66
- sanic_security/authorization.py +13 -1
- sanic_security/configuration.py +2 -2
- sanic_security/exceptions.py +6 -0
- sanic_security/models.py +5 -13
- sanic_security/test/server.py +2 -4
- sanic_security/test/tests.py +1 -1
- sanic_security/verification.py +34 -5
- {sanic_security-1.12.7.dist-info → sanic_security-1.13.1.dist-info}/METADATA +14 -27
- sanic_security-1.13.1.dist-info/RECORD +16 -0
- sanic_security-1.12.7.dist-info/RECORD +0 -16
- {sanic_security-1.12.7.dist-info → sanic_security-1.13.1.dist-info}/LICENSE +0 -0
- {sanic_security-1.12.7.dist-info → sanic_security-1.13.1.dist-info}/WHEEL +0 -0
- {sanic_security-1.12.7.dist-info → sanic_security-1.13.1.dist-info}/top_level.txt +0 -0
sanic_security/authentication.py
CHANGED
@@ -15,6 +15,7 @@ from sanic_security.exceptions import (
|
|
15
15
|
DeactivatedError,
|
16
16
|
SecondFactorFulfilledError,
|
17
17
|
ExpiredError,
|
18
|
+
AuditWarning,
|
18
19
|
)
|
19
20
|
from sanic_security.models import Account, AuthenticationSession, Role, TwoStepSession
|
20
21
|
from sanic_security.utils import get_ip
|
@@ -128,10 +129,13 @@ async def login(
|
|
128
129
|
request, account, requires_second_factor=require_second_factor
|
129
130
|
)
|
130
131
|
logger.info(
|
131
|
-
f"Client has logged
|
132
|
+
f"Client {get_ip(request)} has logged in with authentication session {authentication_session.id}."
|
132
133
|
)
|
133
134
|
return authentication_session
|
134
135
|
except VerifyMismatchError:
|
136
|
+
logger.warning(
|
137
|
+
f"Client {get_ip(request)} has failed to log into account {account.id}."
|
138
|
+
)
|
135
139
|
raise CredentialsError("Incorrect password.", 401)
|
136
140
|
|
137
141
|
|
@@ -156,8 +160,7 @@ async def logout(request: Request) -> AuthenticationSession:
|
|
156
160
|
authentication_session.active = False
|
157
161
|
await authentication_session.save(update_fields=["active"])
|
158
162
|
logger.info(
|
159
|
-
f"Client has logged out
|
160
|
-
f" of account {authentication_session.bearer.id}"} with authentication session {authentication_session.id}."
|
163
|
+
f"Client {get_ip(request)} has logged out with authentication session {authentication_session.id}."
|
161
164
|
)
|
162
165
|
return authentication_session
|
163
166
|
|
@@ -191,7 +194,8 @@ async def fulfill_second_factor(request: Request) -> AuthenticationSession:
|
|
191
194
|
authentication_session.requires_second_factor = False
|
192
195
|
await authentication_session.save(update_fields=["requires_second_factor"])
|
193
196
|
logger.info(
|
194
|
-
f"
|
197
|
+
f"Client {get_ip(request)} has fulfilled authentication session {authentication_session.id} "
|
198
|
+
"second factor."
|
195
199
|
)
|
196
200
|
return authentication_session
|
197
201
|
|
@@ -262,68 +266,6 @@ def requires_authentication(arg=None):
|
|
262
266
|
return decorator(arg) if callable(arg) else decorator
|
263
267
|
|
264
268
|
|
265
|
-
def attach_refresh_encoder(app: Sanic):
|
266
|
-
"""
|
267
|
-
Automatically encodes the new/refreshed session returned during authentication when client's current session expires.
|
268
|
-
|
269
|
-
Args:
|
270
|
-
app: (Sanic): The main Sanic application instance.
|
271
|
-
"""
|
272
|
-
|
273
|
-
@app.on_response
|
274
|
-
async def refresh_encoder_middleware(request, response):
|
275
|
-
if hasattr(request.ctx, "authentication_session"):
|
276
|
-
authentication_session = request.ctx.authentication_session
|
277
|
-
if authentication_session.is_refresh:
|
278
|
-
authentication_session.encode(response)
|
279
|
-
|
280
|
-
|
281
|
-
def create_initial_admin_account(app: Sanic) -> None:
|
282
|
-
"""
|
283
|
-
Creates the initial admin account that can be logged into and has complete authoritative access.
|
284
|
-
|
285
|
-
Args:
|
286
|
-
app (Sanic): The main Sanic application instance.
|
287
|
-
"""
|
288
|
-
|
289
|
-
@app.listener("before_server_start")
|
290
|
-
async def create(app, loop):
|
291
|
-
if security_config.SECRET == DEFAULT_CONFIG["SECRET"]:
|
292
|
-
warnings.warn("Secret should be changed from default.")
|
293
|
-
if security_config.INITIAL_ADMIN_EMAIL == DEFAULT_CONFIG["INITIAL_ADMIN_EMAIL"]:
|
294
|
-
warnings.warn("Initial admin email should be changed from default.")
|
295
|
-
if (
|
296
|
-
security_config.INITIAL_ADMIN_PASSWORD
|
297
|
-
== DEFAULT_CONFIG["INITIAL_ADMIN_PASSWORD"]
|
298
|
-
):
|
299
|
-
warnings.warn("Initial admin password should be changed from default.")
|
300
|
-
try:
|
301
|
-
role = await Role.filter(name="Admin").get()
|
302
|
-
except DoesNotExist:
|
303
|
-
role = await Role.create(
|
304
|
-
description="Has root abilities, assign sparingly.",
|
305
|
-
permissions="*:*",
|
306
|
-
name="Admin",
|
307
|
-
)
|
308
|
-
try:
|
309
|
-
account = await Account.filter(
|
310
|
-
email=security_config.INITIAL_ADMIN_EMAIL
|
311
|
-
).get()
|
312
|
-
await account.fetch_related("roles")
|
313
|
-
if role not in account.roles:
|
314
|
-
await account.roles.add(role)
|
315
|
-
logger.warning("Initial admin account role has been reinstated.")
|
316
|
-
except DoesNotExist:
|
317
|
-
account = await Account.create(
|
318
|
-
username="Admin",
|
319
|
-
email=security_config.INITIAL_ADMIN_EMAIL,
|
320
|
-
password=password_hasher.hash(security_config.INITIAL_ADMIN_PASSWORD),
|
321
|
-
verified=True,
|
322
|
-
)
|
323
|
-
await account.roles.add(role)
|
324
|
-
logger.info("Initial admin account created.")
|
325
|
-
|
326
|
-
|
327
269
|
def validate_email(email: str) -> str:
|
328
270
|
"""
|
329
271
|
Validates email format.
|
@@ -402,3 +344,77 @@ def validate_password(password: str) -> str:
|
|
402
344
|
400,
|
403
345
|
)
|
404
346
|
return password
|
347
|
+
|
348
|
+
|
349
|
+
def initialize_security(app: Sanic, create_root=True) -> None:
|
350
|
+
"""
|
351
|
+
Audits configuration, creates root administrator account, and attaches refresh encoder middleware.
|
352
|
+
|
353
|
+
Args:
|
354
|
+
app (Sanic): The main Sanic application instance.
|
355
|
+
create_root (bool): Determines root account creation on initialization.
|
356
|
+
"""
|
357
|
+
|
358
|
+
@app.on_response
|
359
|
+
async def refresh_encoder_middleware(request, response):
|
360
|
+
if hasattr(request.ctx, "authentication_session"):
|
361
|
+
authentication_session = request.ctx.authentication_session
|
362
|
+
if authentication_session.is_refresh:
|
363
|
+
authentication_session.encode(response)
|
364
|
+
|
365
|
+
@app.listener("before_server_start")
|
366
|
+
async def audit_configuration(app, loop):
|
367
|
+
if security_config.SECRET == DEFAULT_CONFIG["SECRET"]:
|
368
|
+
warnings.warn("Secret should be changed from default.", AuditWarning)
|
369
|
+
if not security_config.SESSION_HTTPONLY:
|
370
|
+
warnings.warn("HttpOnly should be enabled.", AuditWarning)
|
371
|
+
if not security_config.SESSION_SECURE:
|
372
|
+
warnings.warn("Secure should be enabled.", AuditWarning)
|
373
|
+
if security_config.SESSION_SAMESITE.lower() == "none":
|
374
|
+
warnings.warn("SameSite should not be set to none.", AuditWarning)
|
375
|
+
if (
|
376
|
+
create_root
|
377
|
+
and security_config.INITIAL_ADMIN_EMAIL
|
378
|
+
== DEFAULT_CONFIG["INITIAL_ADMIN_EMAIL"]
|
379
|
+
):
|
380
|
+
warnings.warn(
|
381
|
+
"Initial admin email should be changed from default.", AuditWarning
|
382
|
+
)
|
383
|
+
if (
|
384
|
+
create_root
|
385
|
+
and security_config.INITIAL_ADMIN_PASSWORD
|
386
|
+
== DEFAULT_CONFIG["INITIAL_ADMIN_PASSWORD"]
|
387
|
+
):
|
388
|
+
warnings.warn(
|
389
|
+
"Initial admin password should be changed from default.", AuditWarning
|
390
|
+
)
|
391
|
+
|
392
|
+
@app.listener("before_server_start")
|
393
|
+
async def create_root_account(app, loop):
|
394
|
+
if not create_root:
|
395
|
+
return
|
396
|
+
try:
|
397
|
+
role = await Role.filter(name="Root").get()
|
398
|
+
except DoesNotExist:
|
399
|
+
role = await Role.create(
|
400
|
+
description="Has administrator abilities, assign sparingly.",
|
401
|
+
permissions="*:*",
|
402
|
+
name="Root",
|
403
|
+
)
|
404
|
+
try:
|
405
|
+
account = await Account.filter(
|
406
|
+
email=security_config.INITIAL_ADMIN_EMAIL
|
407
|
+
).get()
|
408
|
+
await account.fetch_related("roles")
|
409
|
+
if role not in account.roles:
|
410
|
+
await account.roles.add(role)
|
411
|
+
logger.warning("Initial admin account role has been reinstated.")
|
412
|
+
except DoesNotExist:
|
413
|
+
account = await Account.create(
|
414
|
+
username="Root",
|
415
|
+
email=security_config.INITIAL_ADMIN_EMAIL,
|
416
|
+
password=password_hasher.hash(security_config.INITIAL_ADMIN_PASSWORD),
|
417
|
+
verified=True,
|
418
|
+
)
|
419
|
+
await account.roles.add(role)
|
420
|
+
logger.info("Initial admin account created.")
|
sanic_security/authorization.py
CHANGED
@@ -8,6 +8,7 @@ from tortoise.exceptions import DoesNotExist
|
|
8
8
|
from sanic_security.authentication import authenticate
|
9
9
|
from sanic_security.exceptions import AuthorizationError, AnonymousError
|
10
10
|
from sanic_security.models import Role, Account, AuthenticationSession
|
11
|
+
from sanic_security.utils import get_ip
|
11
12
|
|
12
13
|
"""
|
13
14
|
Copyright (c) 2020-present Nicholas Aidan Stewart
|
@@ -58,6 +59,9 @@ async def check_permissions(
|
|
58
59
|
"""
|
59
60
|
authentication_session = await authenticate(request)
|
60
61
|
if authentication_session.anonymous:
|
62
|
+
logger.warning(
|
63
|
+
f"Client {get_ip(request)} attempted an unauthorized action anonymously."
|
64
|
+
)
|
61
65
|
raise AnonymousError()
|
62
66
|
roles = await authentication_session.bearer.roles.filter(deleted=False).all()
|
63
67
|
for role in roles:
|
@@ -66,6 +70,9 @@ async def check_permissions(
|
|
66
70
|
):
|
67
71
|
if fnmatch(required_permission, role_permission):
|
68
72
|
return authentication_session
|
73
|
+
logger.warning(
|
74
|
+
f"Client {get_ip(request)} with account {authentication_session.bearer.id} attempted an unauthorized action. "
|
75
|
+
)
|
69
76
|
raise AuthorizationError("Insufficient permissions required for this action.")
|
70
77
|
|
71
78
|
|
@@ -93,11 +100,17 @@ async def check_roles(request: Request, *required_roles: str) -> AuthenticationS
|
|
93
100
|
"""
|
94
101
|
authentication_session = await authenticate(request)
|
95
102
|
if authentication_session.anonymous:
|
103
|
+
logger.warning(
|
104
|
+
f"Client {get_ip(request)} attempted an unauthorized action anonymously."
|
105
|
+
)
|
96
106
|
raise AnonymousError()
|
97
107
|
roles = await authentication_session.bearer.roles.filter(deleted=False).all()
|
98
108
|
for role in roles:
|
99
109
|
if role.name in required_roles:
|
100
110
|
return authentication_session
|
111
|
+
logger.warning(
|
112
|
+
f"Client {get_ip(request)} with account {authentication_session.bearer.id} attempted an unauthorized action. "
|
113
|
+
)
|
101
114
|
raise AuthorizationError("Insufficient roles required for this action.")
|
102
115
|
|
103
116
|
|
@@ -120,7 +133,6 @@ async def assign_role(
|
|
120
133
|
description=description, permissions=permissions, name=name
|
121
134
|
)
|
122
135
|
await account.roles.add(role)
|
123
|
-
logger.info(f"Role {role.id} has been assigned to account {account.id}.")
|
124
136
|
return role
|
125
137
|
|
126
138
|
|
sanic_security/configuration.py
CHANGED
@@ -27,11 +27,11 @@ SOFTWARE.
|
|
27
27
|
DEFAULT_CONFIG = {
|
28
28
|
"SECRET": "This is a big secret. Shhhhh",
|
29
29
|
"PUBLIC_SECRET": None,
|
30
|
-
"SESSION_SAMESITE": "
|
30
|
+
"SESSION_SAMESITE": "Strict",
|
31
31
|
"SESSION_SECURE": True,
|
32
32
|
"SESSION_HTTPONLY": True,
|
33
33
|
"SESSION_DOMAIN": None,
|
34
|
-
"SESSION_PREFIX": "
|
34
|
+
"SESSION_PREFIX": "tkn",
|
35
35
|
"SESSION_ENCODING_ALGORITHM": "HS256",
|
36
36
|
"MAX_CHALLENGE_ATTEMPTS": 5,
|
37
37
|
"CAPTCHA_SESSION_EXPIRATION": 60,
|
sanic_security/exceptions.py
CHANGED
sanic_security/models.py
CHANGED
@@ -6,7 +6,6 @@ from typing import Union
|
|
6
6
|
import jwt
|
7
7
|
from captcha.image import ImageCaptcha
|
8
8
|
from jwt import DecodeError
|
9
|
-
from sanic.log import logger
|
10
9
|
from sanic.request import Request
|
11
10
|
from sanic.response import HTTPResponse, raw
|
12
11
|
from tortoise import fields, Model
|
@@ -142,7 +141,6 @@ class Account(BaseModel):
|
|
142
141
|
else:
|
143
142
|
self.disabled = True
|
144
143
|
await self.save(update_fields=["disabled"])
|
145
|
-
logger.info(f"Account {self.id} has been disabled.")
|
146
144
|
|
147
145
|
@property
|
148
146
|
def json(self) -> dict:
|
@@ -320,7 +318,6 @@ class Session(BaseModel):
|
|
320
318
|
if self.active:
|
321
319
|
self.active = False
|
322
320
|
await self.save(update_fields=["active"])
|
323
|
-
logger.info(f"Session {self.id} has been deactivated.")
|
324
321
|
else:
|
325
322
|
raise DeactivatedError("Session is already deactivated.", 403)
|
326
323
|
|
@@ -516,9 +513,6 @@ class VerificationSession(Session):
|
|
516
513
|
else:
|
517
514
|
raise MaxedOutChallengeError()
|
518
515
|
else:
|
519
|
-
logger.info(
|
520
|
-
f"Client has completed verification session {self.id} challenge."
|
521
|
-
)
|
522
516
|
await self.deactivate()
|
523
517
|
|
524
518
|
@classmethod
|
@@ -530,9 +524,7 @@ class VerificationSession(Session):
|
|
530
524
|
|
531
525
|
|
532
526
|
class TwoStepSession(VerificationSession):
|
533
|
-
"""
|
534
|
-
Validates a client using a code sent via email or text.
|
535
|
-
"""
|
527
|
+
"""Validates a client using a code sent via email or text."""
|
536
528
|
|
537
529
|
@classmethod
|
538
530
|
async def new(cls, request: Request, account: Account, **kwargs):
|
@@ -550,9 +542,7 @@ class TwoStepSession(VerificationSession):
|
|
550
542
|
|
551
543
|
|
552
544
|
class CaptchaSession(VerificationSession):
|
553
|
-
"""
|
554
|
-
Validates a client with a captcha challenge.
|
555
|
-
"""
|
545
|
+
"""Validates a client with a captcha challenge."""
|
556
546
|
|
557
547
|
@classmethod
|
558
548
|
async def new(cls, request: Request, **kwargs):
|
@@ -612,6 +602,9 @@ class AuthenticationSession(Session):
|
|
612
602
|
"""
|
613
603
|
Refreshes session if expired and within refresh date.
|
614
604
|
|
605
|
+
Args:
|
606
|
+
request (Request): Sanic request parameter.
|
607
|
+
|
615
608
|
Raises:
|
616
609
|
DeletedError
|
617
610
|
ExpiredError
|
@@ -633,7 +626,6 @@ class AuthenticationSession(Session):
|
|
633
626
|
):
|
634
627
|
self.active = False
|
635
628
|
await self.save(update_fields=["active"])
|
636
|
-
logger.info(f"Client has refreshed authentication session {self.id}.")
|
637
629
|
return await self.new(request, self.bearer, True)
|
638
630
|
else:
|
639
631
|
raise e
|
sanic_security/test/server.py
CHANGED
@@ -10,9 +10,8 @@ from sanic_security.authentication import (
|
|
10
10
|
register,
|
11
11
|
requires_authentication,
|
12
12
|
logout,
|
13
|
-
create_initial_admin_account,
|
14
13
|
fulfill_second_factor,
|
15
|
-
|
14
|
+
initialize_security,
|
16
15
|
)
|
17
16
|
from sanic_security.authorization import (
|
18
17
|
assign_role,
|
@@ -308,7 +307,6 @@ register_tortoise(
|
|
308
307
|
modules={"models": ["sanic_security.models"]},
|
309
308
|
generate_schemas=True,
|
310
309
|
)
|
311
|
-
|
312
|
-
create_initial_admin_account(app)
|
310
|
+
initialize_security(app, True)
|
313
311
|
if __name__ == "__main__":
|
314
312
|
app.run(host="127.0.0.1", port=8000, workers=1, debug=True)
|
sanic_security/test/tests.py
CHANGED
@@ -231,7 +231,7 @@ class LoginTest(TestCase):
|
|
231
231
|
permitted_authorization_response = self.client.post(
|
232
232
|
"http://127.0.0.1:8000/api/test/auth/roles",
|
233
233
|
data={
|
234
|
-
"role": "
|
234
|
+
"role": "Root",
|
235
235
|
"permissions_required": "perm1:create,add, perm2:*",
|
236
236
|
},
|
237
237
|
)
|
sanic_security/verification.py
CHANGED
@@ -8,12 +8,14 @@ from sanic_security.exceptions import (
|
|
8
8
|
JWTDecodeError,
|
9
9
|
NotFoundError,
|
10
10
|
VerifiedError,
|
11
|
+
MaxedOutChallengeError,
|
11
12
|
)
|
12
13
|
from sanic_security.models import (
|
13
14
|
Account,
|
14
15
|
TwoStepSession,
|
15
16
|
CaptchaSession,
|
16
17
|
)
|
18
|
+
from sanic_security.utils import get_ip
|
17
19
|
|
18
20
|
"""
|
19
21
|
Copyright (c) 2020-present Nicholas Aidan Stewart
|
@@ -85,12 +87,21 @@ async def two_step_verification(request: Request) -> TwoStepSession:
|
|
85
87
|
MaxedOutChallengeError
|
86
88
|
|
87
89
|
Returns:
|
88
|
-
|
90
|
+
two_step_session
|
89
91
|
"""
|
90
92
|
two_step_session = await TwoStepSession.decode(request)
|
91
93
|
two_step_session.validate()
|
92
94
|
two_step_session.bearer.validate()
|
93
|
-
|
95
|
+
try:
|
96
|
+
await two_step_session.check_code(request.form.get("code"))
|
97
|
+
except MaxedOutChallengeError as e:
|
98
|
+
logger.warning(
|
99
|
+
f"Client {get_ip(request)} has exceeded maximum two-step session {two_step_session.id} challenge attempts."
|
100
|
+
)
|
101
|
+
raise e
|
102
|
+
logger.info(
|
103
|
+
f"Client {get_ip(request)} has completed two-step session {two_step_session.id} challenge."
|
104
|
+
)
|
94
105
|
return two_step_session
|
95
106
|
|
96
107
|
|
@@ -154,10 +165,19 @@ async def verify_account(request: Request) -> TwoStepSession:
|
|
154
165
|
if two_step_session.bearer.verified:
|
155
166
|
raise VerifiedError()
|
156
167
|
two_step_session.validate()
|
157
|
-
|
168
|
+
try:
|
169
|
+
await two_step_session.check_code(request.form.get("code"))
|
170
|
+
except MaxedOutChallengeError as e:
|
171
|
+
logger.warning(
|
172
|
+
f"Client {get_ip(request)} has exceeded maximum two-step session {two_step_session.id} challenge attempts "
|
173
|
+
"during account verification."
|
174
|
+
)
|
175
|
+
raise e
|
158
176
|
two_step_session.bearer.verified = True
|
159
177
|
await two_step_session.bearer.save(update_fields=["verified"])
|
160
|
-
logger.info(
|
178
|
+
logger.info(
|
179
|
+
f"Client {get_ip(request)} has verified account {two_step_session.bearer.id}."
|
180
|
+
)
|
161
181
|
return two_step_session
|
162
182
|
|
163
183
|
|
@@ -199,7 +219,16 @@ async def captcha(request: Request) -> CaptchaSession:
|
|
199
219
|
"""
|
200
220
|
captcha_session = await CaptchaSession.decode(request)
|
201
221
|
captcha_session.validate()
|
202
|
-
|
222
|
+
try:
|
223
|
+
await captcha_session.check_code(request.form.get("captcha"))
|
224
|
+
except MaxedOutChallengeError as e:
|
225
|
+
logger.warning(
|
226
|
+
f"Client {get_ip(request)} has exceeded maximum captcha session {captcha_session.id} challenge attempts."
|
227
|
+
)
|
228
|
+
raise e
|
229
|
+
logger.info(
|
230
|
+
f"Client {get_ip(request)} has completed captcha session {captcha_session.id} challenge."
|
231
|
+
)
|
203
232
|
return captcha_session
|
204
233
|
|
205
234
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: sanic-security
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.13.1
|
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/
|
@@ -64,7 +64,7 @@ Requires-Dist: cryptography; extra == "dev"
|
|
64
64
|
* [Usage](#usage)
|
65
65
|
* [Authentication](#authentication)
|
66
66
|
* [Captcha](#captcha)
|
67
|
-
* [Two
|
67
|
+
* [Two-Step Verification](#two-step-verification)
|
68
68
|
* [Authorization](#authorization)
|
69
69
|
* [Testing](#testing)
|
70
70
|
* [Tortoise](#tortoise)
|
@@ -79,10 +79,11 @@ Requires-Dist: cryptography; extra == "dev"
|
|
79
79
|
Sanic Security is an authentication, authorization, and verification library designed for use with [Sanic](https://github.com/huge-success/sanic).
|
80
80
|
|
81
81
|
* Login, registration, and authentication with refresh mechanisms
|
82
|
+
* Role based authorization with wildcard permissions
|
82
83
|
* Two-factor authentication
|
83
|
-
* Captcha
|
84
84
|
* Two-step verification
|
85
|
-
*
|
85
|
+
* Captcha
|
86
|
+
* Logging
|
86
87
|
|
87
88
|
Visit [security.na-stewart.com](https://security.na-stewart.com) for documentation.
|
88
89
|
|
@@ -145,12 +146,12 @@ You can load environment variables with a different prefix via `config.load_envi
|
|
145
146
|
|---------------------------------------|------------------------------|----------------------------------------------------------------------------------------------------------------------------------|
|
146
147
|
| **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. |
|
147
148
|
| **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. |
|
148
|
-
| **SESSION_SAMESITE** |
|
149
|
+
| **SESSION_SAMESITE** | Strict | The SameSite attribute of session cookies. |
|
149
150
|
| **SESSION_SECURE** | True | The Secure attribute of session cookies. |
|
150
151
|
| **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. |
|
151
152
|
| **SESSION_DOMAIN** | None | The Domain attribute of session cookies. |
|
152
153
|
| **SESSION_ENCODING_ALGORITHM** | HS256 | The algorithm used to encode and decode session JWT's. |
|
153
|
-
| **SESSION_PREFIX** |
|
154
|
+
| **SESSION_PREFIX** | tkn | Prefix attached to the beginning of session cookies. |
|
154
155
|
| **MAX_CHALLENGE_ATTEMPTS** | 5 | The maximum amount of session challenge attempts allowed. |
|
155
156
|
| **CAPTCHA_SESSION_EXPIRATION** | 60 | The amount of seconds till captcha session expiration on creation. Setting to 0 will disable expiration. |
|
156
157
|
| **CAPTCHA_FONT** | captcha-font.ttf | The file path to the font being used for captcha generation. |
|
@@ -166,19 +167,16 @@ You can load environment variables with a different prefix via `config.load_envi
|
|
166
167
|
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
|
167
168
|
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.
|
168
169
|
|
169
|
-
|
170
|
-
|
171
|
-
## Authentication
|
172
|
-
|
173
|
-
* Initial Administrator Account
|
174
|
-
|
175
|
-
Creates root account if it doesn't exist, you should modify its credentials in config!
|
176
|
-
|
170
|
+
* Initialize sanic-security as follows:
|
177
171
|
```python
|
178
|
-
|
172
|
+
initialize_security(app)
|
179
173
|
if __name__ == "__main__":
|
180
|
-
app.run(host="127.0.0.1", port=8000)
|
174
|
+
app.run(host="127.0.0.1", port=8000, workers=1, debug=True)
|
181
175
|
```
|
176
|
+
|
177
|
+
The tables in the below examples represent example [request form-data](https://sanicframework.org/en/guide/basics/request.html#form).
|
178
|
+
|
179
|
+
## Authentication
|
182
180
|
|
183
181
|
* Registration (With two-step account verification)
|
184
182
|
|
@@ -321,17 +319,6 @@ async def on_authenticate(request):
|
|
321
319
|
return response
|
322
320
|
```
|
323
321
|
|
324
|
-
* Refresh Encoder
|
325
|
-
|
326
|
-
A new/refreshed session is returned during authentication when the client's current session expires and it
|
327
|
-
requires encoding. This should be be done automatically via middleware.
|
328
|
-
|
329
|
-
```python
|
330
|
-
attach_refresh_encoder(app)
|
331
|
-
if __name__ == "__main__":
|
332
|
-
app.run(host="127.0.0.1", port=8000)
|
333
|
-
```
|
334
|
-
|
335
322
|
## Captcha
|
336
323
|
|
337
324
|
A pre-existing font for captcha challenges is included in the Sanic Security repository. You may set your own font by
|
@@ -0,0 +1,16 @@
|
|
1
|
+
sanic_security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
sanic_security/authentication.py,sha256=h0Yq2hWFv1GZoqhPQBmoJnqywGQd6fOYu7zyaqfv6wQ,14432
|
3
|
+
sanic_security/authorization.py,sha256=1SHx4cU_ibC0o_nEDDYURH_l_K6Q66M0SLzpRQrsIXc,7534
|
4
|
+
sanic_security/configuration.py,sha256=br2hI3MHsTBh3yfPer5f3bkKSWfQdCeqfLqWmaDNVoM,5510
|
5
|
+
sanic_security/exceptions.py,sha256=JiCaBR2kjE1Cj0fc_08y-32fqJJXa_1UCw205T4_RTY,5493
|
6
|
+
sanic_security/models.py,sha256=v3tJyL420HEdZXqJCq9uPPSivuYXuQNtqf9QC9wF0TU,22274
|
7
|
+
sanic_security/utils.py,sha256=XAUNalcTi53qTz0D8xiDyDyRlq7Z7ffNBzUONJZqe90,2705
|
8
|
+
sanic_security/verification.py,sha256=js2PkqJU6o46atslJ76n-_cYoY5iz5fbyiV0OFwoySo,8668
|
9
|
+
sanic_security/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
+
sanic_security/test/server.py,sha256=79HaNIH1skWTrh2gIbh8WWVNxvYqPA5GlQ8AqRaCsXQ,12094
|
11
|
+
sanic_security/test/tests.py,sha256=bW5fHJfsCrg8eBmcSqVMLm0R5XRL1ou-XJJRgz09GOE,21993
|
12
|
+
sanic_security-1.13.1.dist-info/LICENSE,sha256=sXlJs9_mG-dCkPfWsDnuzydJWagS82E2gYtkVH9enHA,1100
|
13
|
+
sanic_security-1.13.1.dist-info/METADATA,sha256=7oqMMHZfK9wPEeR1gQ04FvUPqCgVdETi7LES-w16uEM,23011
|
14
|
+
sanic_security-1.13.1.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
15
|
+
sanic_security-1.13.1.dist-info/top_level.txt,sha256=ZybkhHXSjfzhmv8XeqLvnNmLmv21Z0oPX6Ep4DJN8b0,15
|
16
|
+
sanic_security-1.13.1.dist-info/RECORD,,
|
@@ -1,16 +0,0 @@
|
|
1
|
-
sanic_security/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
sanic_security/authentication.py,sha256=CzjbVh7uDjBs22oQCklcbmp5gTDW17AoYaa3lmyPnGk,13783
|
3
|
-
sanic_security/authorization.py,sha256=80vuM_2oNgfhOMuct89R6UBhDYmSWQzIgocKxhKzReE,7030
|
4
|
-
sanic_security/configuration.py,sha256=MKxYjq1q9RBRX2cMJkIe87ke0mLKa69RWoQ5MhVciho,5512
|
5
|
-
sanic_security/exceptions.py,sha256=MTPF4tm_68Nmf_z06RHH_6DTiC_CNiLER1jzEoW1dFk,5398
|
6
|
-
sanic_security/models.py,sha256=zWn9zXl-vwP8qJ-DzBuNNG7MCl1ggZX6yb6Zgh3Jfcs,22601
|
7
|
-
sanic_security/utils.py,sha256=XAUNalcTi53qTz0D8xiDyDyRlq7Z7ffNBzUONJZqe90,2705
|
8
|
-
sanic_security/verification.py,sha256=rNyZk_J53dOzk8qy5iG3yiMRuRcGaYeHwIwnFDH9TWw,7582
|
9
|
-
sanic_security/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
|
-
sanic_security/test/server.py,sha256=-dQAB75C-l9kfN7PnaYNDOLiLXaqg0euZAWF5oXMxeE,12164
|
11
|
-
sanic_security/test/tests.py,sha256=jUZ5kgQF4rFx1bImKqHYR7UeoYAfjtNNXSQYTMfzlg4,21994
|
12
|
-
sanic_security-1.12.7.dist-info/LICENSE,sha256=sXlJs9_mG-dCkPfWsDnuzydJWagS82E2gYtkVH9enHA,1100
|
13
|
-
sanic_security-1.12.7.dist-info/METADATA,sha256=ZGVTjtv-OVMrs_3Ac__jKE1JgdhtIAyhMCW2n3VUW3Y,23393
|
14
|
-
sanic_security-1.12.7.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
15
|
-
sanic_security-1.12.7.dist-info/top_level.txt,sha256=ZybkhHXSjfzhmv8XeqLvnNmLmv21Z0oPX6Ep4DJN8b0,15
|
16
|
-
sanic_security-1.12.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|