Package: sanic-security
Sanic SecurityAn effective, simple, and async security library for the Sanic framework.Table of ContentsAbout the ProjectGetting StartedPrerequisitesInstallationConfigurationUsageAuthenticationCaptchaTwo Step VerificationAuthorizationTestingTortoiseContributingLicenseVersioningSupportAbout The ProjectSanic Security is an authentication, authorization, and verification library designed for use withSanic.
This library contains a variety of features including:Login, registration, and authenticationTwo-factor authenticationCaptchaTwo-step verificationRole based authorization with wildcard permissionsPlease visitsecurity.na-stewart.comfor documentation,and check outblog.na-stewart.comfor an example implementation.Getting StartedIn order to get started, please installPip.InstallationInstall the Sanic Security pip package.pip3installsanic-securityInstall the Sanic Security pip package with thecryptographydependency included.If you are planning on encoding or decoding JWTs using certain digital signature algorithms (like RSA or ECDSA which use
the public secret and private secret), you will need to install thecryptographylibrary. This can be installed explicitly, or
as a required extra in thesanic-securityrequirement.pip3installsanic-security[crypto]For developers, fork Sanic Security and install development dependencies.pip3install-e".[dev]"Update sanic-security if already installed.pip3install--upgradesanic-securityConfigurationSanic Security configuration is merely an object that can be modified either using dot-notation or like a
dictionary.For example:fromsanic_security.configurationimportconfigconfig.SECRET="This is a big secret. Shhhhh"config["CAPTCHA_FONT"]="./resources/captcha-font.ttf"You can also use the update() method like on regular dictionaries.Any environment variables defined with the SANIC_SECURITY_ prefix will be applied to the config. For example, setting
SANIC_SECURITY_SECRET will be loaded by the application automatically and fed into the SECRET config variable.You can load environment variables with a different prefix via calling theconfig.load_environment_variables("NEW_PREFIX_")method.Default configuration values:KeyValueDescriptionSECRETThis is a big secret. ShhhhhThe secret used for generating and signing JWTs. This should be a string unique to your application. Keep it safe.PUBLIC_SECRETNoneThe secret used for verifying and decoding JWTs and can be publicly shared. This should be a string unique to your application.SESSION_SAMESITEstrictThe SameSite attribute of session cookies.SESSION_SECURETrueThe Secure attribute of session cookies.SESSION_HTTPONLYTrueThe HttpOnly attribute of session cookies. HIGHLY recommended that you do not turn this off, unless you know what you are doing.SESSION_DOMAINNoneThe Domain attribute of session cookies.SESSION_ENCODING_ALGORITHMHS256The algorithm used to encode and decode session JWT's.SESSION_PREFIXtokenPrefix attached to the beginning of session cookies.MAX_CHALLENGE_ATTEMPTS5The maximum amount of session challenge attempts allowed.CAPTCHA_SESSION_EXPIRATION60The amount of seconds till captcha session expiration on creation. Setting to 0 will disable expiration.CAPTCHA_FONTcaptcha-font.ttfThe file path to the font being used for captcha generation.TWO_STEP_SESSION_EXPIRATION200The amount of seconds till two step session expiration on creation. Setting to 0 will disable expiration.AUTHENTICATION_SESSION_EXPIRATION2692000The amount of seconds till authentication session expiration on creation. Setting to 0 will disable expiration.ALLOW_LOGIN_WITH_USERNAMEFalseAllows login via username and email.INITIAL_ADMIN_EMAILadmin@example.comEmail used when creating the initial admin account.INITIAL_ADMIN_PASSWORDadmin123Password used when creating the initial admin account.UsageSanic 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 would be sent
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.The tables in the below examples represent examplerequest form-data.AuthenticationInitial Administrator AccountThis account can be logged into and has complete authoritative access. Login credentials should be modified in config!create_initial_admin_account(app)if__name__=="__main__":app.run(host="127.0.0.1",port=8000)Registration (With Two-step Verification)Phone can be null or empty.KeyValueusernameexampleemailexample@example.comphone19811354186passwordexamplepass@app.post("api/security/register")asyncdefon_register(request):account=awaitregister(request)two_step_session=awaitrequest_two_step_verification(request,account)awaitemail_code(account.email,two_step_session.code# Code = AJ8HGD)# Custom method for emailing verification code.response=json("Registration successful! Email verification required.",two_step_session.bearer.json,)two_step_session.encode(response)returnresponseVerify AccountVerifies the client's account via two-step session code.KeyValuecodeAJ8HGD@app.post("api/security/verify")asyncdefon_verify(request):two_step_session=awaitverify_account(request)returnjson("You have verified your account and may login!",two_step_session.bearer.json)Login (With Two-factor Authentication)Login credentials are retrieved via the Authorization header. Credentials are constructed by first combining the
username and the password with a colon (aladdin:opensesame), and then by encoding the resulting string in base64
(YWxhZGRpbjpvcGVuc2VzYW1l). Here is an example authorization header:Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l.You can use a username as well as an email for login ifALLOW_LOGIN_WITH_USERNAMEis true in the config.@app.post("api/security/login")asyncdefon_login(request):authentication_session=awaitlogin(request,require_second_factor=True)two_step_session=awaitrequest_two_step_verification(request,authentication_session.bearer)awaitemail_code(authentication_session.bearer.email,two_step_session.code# Code = BG5KLP)# Custom method for emailing verification code.response=json("Login successful! Two-factor authentication required.",authentication_session.bearer.json,)authentication_session.encode(response)two_step_session.encode(response)returnresponseFulfill Second FactorFulfills client authentication session's second factor requirement via two-step session code.KeyValuecodeBG5KLP@app.post("api/security/validate-2fa")asyncdefon_two_factor_authentication(request):authentication_session=awaitfulfill_second_factor(request)response=json("Authentication session second-factor fulfilled! You are now authenticated.",authentication_session.bearer.json,)authentication_session.encode(response)returnresponseLogout@app.post("api/security/logout")asyncdefon_logout(request):authentication_session=awaitlogout(request)response=json("Logout successful!",authentication_session.bearer.json)returnresponseAuthenticate@app.post("api/security/auth")asyncdefon_authenticate(request):authentication_session=awaitauthenticate(request)returnjson("You have been authenticated.",authentication_session.bearer.json,)Requires Authentication (This method is not called directly and instead used as a decorator.)@app.post("api/security/auth")@requires_authenticationasyncdefon_authenticate(request):returnjson("You have been authenticated.",request.ctx.authentication_session.bearer.json,)CaptchaA pre-existing font for captcha challenges is included in the Sanic Security repository. You may set your own font by
downloading a .ttf font and defining the file's path in the configuration.1001 Free FontsRecommended FontCaptcha challenge example:Request Captcha@app.get("api/security/captcha")asyncdefon_captcha_img_request(request):captcha_session=awaitrequest_captcha(request)response=captcha_session.get_image()# Captcha: FV9NMQcaptcha_session.encode(response)returnresponseCaptchaKeyValuecaptchaFV9NMQ@app.post("api/security/captcha")asyncdefon_captcha(request):captcha_session=awaitcaptcha(request)returnjson("Captcha attempt successful!",captcha_session.json)Requires Captcha (This method is not called directly and instead used as a decorator.)KeyValuecaptchaFV9NMQ@app.post("ap/security/captcha")@requires_captchaasyncdefon_captcha(request):returnjson("Captcha attempt successful!",request.ctx.captcha_session.json)Two-step VerificationTwo-step verification should be integrated with other custom functionality. For example, account verification during registration.Request Two-step VerificationKeyValueemailexample@example.com@app.post("api/security/two-step/request")asyncdefon_two_step_request(request):two_step_session=awaitrequest_two_step_verification(request)awaitemail_code(account.email,two_step_session.code# Code = DT6JZX)# Custom method for emailing verification code.response=json("Verification request successful!",two_step_session.bearer.json)two_step_session.encode(response)returnresponseResend Two-step Verification Code@app.post("api/security/two-step/resend")asyncdefon_two_step_resend(request):two_step_session=awaitTwoStepSession.decode(request)awaitemail_code(account.email,two_step_session.code# Code = DT6JZX)# Custom method for emailing verification code.returnjson("Verification code resend successful!",two_step_session.bearer.json)Two-step VerificationKeyValuecodeDT6JZX@app.post("api/security/two-step")asyncdefon_two_step_verification(request):two_step_session=awaittwo_step_verification(request)response=json("Two-step verification attempt successful!",two_step_session.bearer.json)returnresponseRequires Two-step Verification (This method is not called directly and instead used as a decorator.)KeyValuecodeDT6JZX@app.post("api/security/two-step")@requires_two_step_verificationasyncdefon_two_step_verification(request):response=json("Two-step verification attempt successful!",request.ctx.two_step_session.bearer.json,)returnresponseAuthorizationSanic Security uses role based authorization with wildcard permissions.Roles are created for various job functions. The permissions to perform certain operations are assigned to specific roles.
Users are assigned particular roles, and through those role assignments acquire the permissions needed to perform
particular system functions. Since users are not assigned permissions directly, but only acquire them through their
role (or roles), management of individual user rights becomes a matter of simply assigning appropriate roles to the
user's account; this simplifies common operations, such as adding a user, or changing a user's department.Wildcard permissions support the concept of multiple levels or parts. For example, you could grant a user the permissionprinter:query,printer:query,delete, orprinter:*.Assign Roleawaitassign_role("Chat Room Moderator",account,"channels:view,delete, account:suspend,mute, voice:*","Can read and delete messages in all chat rooms, suspend and mute accounts, and control voice chat.",)Check Permissions@app.post("api/security/perms")asyncdefon_check_perms(request):authentication_session=awaitcheck_permissions(request,"channels:view","voice:*")returntext("Account is authorized.")Require Permissions (This method is not called directly and instead used as a decorator.)@app.post("api/security/perms")@require_permissions("channels:view","voice:*")asyncdefon_check_perms(request):returntext("Account is authorized.")Check Roles@app.post("api/security/roles")asyncdefon_check_roles(request):authentication_session=awaitcheck_roles(request,"Chat Room Moderator")returntext("Account is authorized.")Require Roles (This method is not called directly and instead used as a decorator.)@app.post("api/security/roles")@require_roles("Chat Room Moderator")asyncdefon_check_roles(request):returntext("Account is authorized.")TestingSet theTEST_DATABASE_URLconfiguration value.Make sure the test Sanic instance (test/server.py) is running on your machine.Run the unit test client (test/tests.py) for results.TortoiseSanic Security usesTortoise ORMfor database operations.Tortoise ORM is an easy-to-use asyncio ORM (Object Relational Mapper).Initialise your models and database like so:asyncdefinit():awaitTortoise.init(db_url="sqlite://db.sqlite3",modules={"models":["sanic_security.models","app.models"]},)awaitTortoise.generate_schemas()orregister_tortoise(app,db_url="sqlite://db.sqlite3",modules={"models":["sanic_security.models","app.models"]},generate_schemas=True,)Define your models like so:fromtortoise.modelsimportModelfromtortoiseimportfieldsclassTournament(Model):id=fields.IntField(pk=True)name=fields.TextField()Use it like so:# Create instance by savetournament=Tournament(name="New Tournament")awaittournament.save()# Or by .create()awaitTournament.create(name="Another Tournament")# Now search for a recordtour=awaitTournament.filter(name__contains="Another").first()print(tour.name)ContributingContributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make aregreatly appreciated.Fork the ProjectCreate your Feature Branch (git checkout -b feature/AmazingFeature)Commit your Changes (git commit -m 'Add some AmazingFeature')Push to the Branch (git push origin feature/AmazingFeature)Open a Pull RequestLicenseDistributed under the GNU Affero General Public License v3.0. SeeLICENSEfor more information.Versioning0.0.0MAJOR version when you make incompatible API changes.MINOR version when you add functionality in a backwards compatible manner.PATCH version when you make backwards compatible bug fixes.https://semver.org/