Skip to main content

A powerful, simple, and async security library for Sanic.

Project description

Contributors Forks Stargazers Issues Code style: black


Sanic Security

A powerful, simple, and async security library for Sanic.
Documentation · Report Bug · Request Feature

Table of Contents

About The Project

Sanic Security is an authentication and authorization library made easy, designed for use with Sanic. This library is intended to be easy, convenient, and contains a variety of features:

  • Easy login and registering
  • Captcha
  • SMS and email verification
  • JWT
  • Password recovery
  • Wildcard permissions
  • Role permissions
  • Easy database integration
  • Completely async

This repository has been starred by Sanic's core maintainer:

alt text

Getting Started

In order to get started, please install pip.

Prerequisites

  • pip
sudo apt-get install python3-pip

Installation

  • Install pip packages
pip3 install sanic-security

Usage

Once Sanic Security is configured and good to go, implementing is easy as pie.

Initial Setup

Familiarity with Sanic and Tortoise ORM is recommended.

First you have to create a configuration file called security.ini in the project directory. Make sure Python's working directory is the project directory. Below is an example of its contents:

WARNING: You must set a custom secret, or you will compromise your encoded sessions.

[AUTH]
name=ExampleProject
secret=05jF8cSMAdjlXcXeS2ZJUHg7Tbyu
captcha_font=source-sans-pro.light.ttf

[TORTOISE]
username=admin
password=8UVbijLUGYfUtItAi
endpoint=example.cweAenuBY6b.us-north-1.rds.amazonaws.com
schema=exampleschema
models=sanic_security.core.models, example.core.models
engine=mysql
generate=true

[TWILIO]
from=12058469963
token=1bcioi878ygO8fi766Fb34750e82a5ab
sid=AC6156Jg67OOYe75c26dgtoTICifIe51cbf

[SMTP]
host=smtp.gmail.com
port=465
from=test@gmail.com
username=test@gmail.com
password=wfrfouwiurhwlnj
tls=true
start_tls=false

You may remove each section in the configuration you aren't using. For example, if you're not utilizing Twillio you can delete the TWILLIO section.

Once you've configured Sanic Security, you can initialize Sanic with the example below:

initialize_security(app)
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8000, debug=True)

All request bodies must be sent as form-data. For my below examples, I use my own custom json method:

from sanic.response import json as sanic_json
def json(message, content, status_code=200):
    payload = {
        'message': message,
        'status_code': status_code,
        'content': content
    }
    return sanic_json(payload, status=status_code)

Authentication

  • Registration (With all verification requirements)

Phone can be null or empty. A captcha request must be made.

Key Value
username test
email test@test.com
phone 19811354186
password testpass
captcha Aj8HgD
@app.post('api/register')
@requires_captcha()
async def on_register(request, captcha_session):
    two_step_session = await register(request)
    await two_step_session.text_code() # Text verification code.
    await two_step_session.email_code() # Or email verification code.
    response = json('Registration successful', two_step_session.account.json())
    two_step_session.encode(response)
    return response
  • Registration (Without verification requirements)

Phone can be null or empty.

Key Value
username test
email test@test.com
phone 19811354186
password testpass
@app.post('api/register')
async def on_register(request):
    account = await register(request, verified=True)
    return json('Registration Successful!', account.json())
  • Login
Key Value
email test@test.com
password testpass
@app.post('api/login')
async def on_login(request):
    authentication_session = await login(request)
    response = json('Login successful!', authentication_session.account.json())
    authentication_session.encode(response)
    return response
  • Logout
@app.post('api/logout')
async def on_logout(request):
    authentication_session = await logout(request)
    response = json('Logout successful', authentication_session.account.json())
    return response
  • Requires Authentication
@app.get('api/client/authenticate')
@requires_authentication()
async def on_authenticated(request, authentication_session):
    return json('Hello ' + authentication_session.account.username + '! You are now authenticated.', 
                authentication_session.account.json())

Account Recovery

  • Recovery Attempt
Key Value
email test@test.com
captcha Aj8HgD
@app.post('api/recovery/attempt')
@requires_captcha()
async def on_recovery_attempt(request, captcha_session):
    two_step_session = await attempt_account_recovery(request)
    await two_step_session.text_code() # Text verification code.
    await two_step_session.email_code() # Or email verification code.
    response = json('A recovery attempt has been made, please verify account ownership.', two_step_session.json())
    two_step_session.encode(response)
    return response
  • Recovery Fulfill
Key Value
code G8ha9nVa
password newpass
@app.post('api/recovery/fulfill')
@requires_two_step_verification()
async def on_recovery_fulfill(request, two_step_session):
    await fulfill_account_recovery_attempt(request, two_step_session)
    return json('Account recovered successfully.', two_step_session.account.json())

Captcha

You must download a .ttf font for captcha challenges and define the file's path in security.ini.

1001 Free Fonts

Recommended Font

Captcha challenge example:

alt text

  • Request Captcha
@app.get('api/captcha')
async def on_request_captcha(request):
    captcha_session = await request_captcha(request)
    response = json('Captcha request successful!', captcha_session.json())
    captcha_session.encode(response)
    return response
  • Captcha Image
@app.get('api/captcha/img')
async def on_captcha_img(request):
    captcha_session = await CaptchaSession().decode(request)
    return await file(captcha_session.get_image())
  • Requires Captcha
Key Value
captcha Aj8HgD
@app.post('api/captcha/attempt')
@requires_captcha()
async def on_captcha_attempt(request, captcha_session):
    response = json('Your captcha attempt was correct!', captcha_session.json())
    return response

Two-Step Verification

  • Request 2SV (Creates and encodes a code, useful for when a two-step session may be invalid or expired.)
@app.get('api/verification/request')
async def on_request_verification(request):
    two_step_session =  await request_two_step_verification(request)
    await two_step_session.text_code() # Text verification code.
    await two_step_session.email_code() # Or email verification code.
    response = json('Verification request successful', two_step_session.json())
    two_step_session.encode(response)
    return response
  • Resend 2SV Code (Does not create new code, only resends encoded session code.)
@app.post('api/verification/resend')
async def on_resend_verification(request):
    two_step_session = await TwoStepSession().decode(request)
    await two_step_session.text_code() # Text verification code.
    await two_step_session.email_code() # Or email verification code.
    return json('Verification code resend successful', two_step_session.json())
  • Requires Two-Step Verification
Key Value
code G8ha9nVa
@app.get('api/client/verify')
@requires_two_step_verification()
async def on_verified(request, two_step_session):
    return json('Hello ' + two_step_session.account.username + '! You have verified yourself and may continue. ', 
                authentication_session.account.json())
  • Verify Account
Key Value
code G8ha9nVae
@app.post('api/verification/account')
@requires_two_step_verification()
async def on_verify(request, two_step_session):
    await verify_account(two_step_session)
    return json('You have verified your account and may login!', two_step_session.json())

Authorization

Sanic Security comes with two protocols for authorization: role based and wildcard based permissions.

Role-based access control (RBAC) is a policy-neutral access-control mechanism defined around roles and privileges. The components of RBAC such as role-permissions, user-role and role-role relationships make it simple to perform user assignments.

Wildcard permissions support the concept of multiple levels or parts. For example, you could grant a user the permission printer:query. The colon in this example is a special character used to delimit the next part in the permission string. In this example, the first part is the domain that is being operated on (printer), and the second part is the action (query) being performed. This concept was inspired by Apache Shiro's implementation of wildcard based permissions.

Examples of wildcard permissions are:

admin:add,update,delete
admin:add
admin:*
employee:add,delete
employee:delete
employee:*
  • Require Permissions
@app.post('api/account/update')
@require_permissions('admin:update', 'employee:add')
async def on_require_perms(request, authentication_session):
    return text('Admin successfully updated account!')
  • Require Roles
@app.get('api/dashboard/admin')
@require_roles('Admin', 'Moderator')
async def on_require_roles(request, authentication_session):
    return text('Admin gained access!')

Error Handling

@app.exception(SecurityError)
async def on_error(request, exception):
    return exception.response

Middleware

@app.middleware('response')
async def xxs_middleware(request, response):
    xss_prevention_middleware(request, response)


@app.middleware('request')
async def https_middleware(request):
    return https_redirect_middleware(request)

Roadmap

Keep up with Sanic Security's Trello board for a list of proposed features, known issues, and in progress development.

Contributing

Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature/AmazingFeature)
  3. Commit your Changes (git commit -m 'Add some AmazingFeature')
  4. Push to the Branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

License

Distributed under the GNU General Public License v3.0. See LICENSE for more information.

Acknowledgements

Project details


Release history Release notifications | RSS feed

This version

0.9.7

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

sanic-security-0.9.7.tar.gz (34.8 kB view details)

Uploaded Source

Built Distribution

sanic_security-0.9.7-py3-none-any.whl (33.5 kB view details)

Uploaded Python 3

File details

Details for the file sanic-security-0.9.7.tar.gz.

File metadata

  • Download URL: sanic-security-0.9.7.tar.gz
  • Upload date:
  • Size: 34.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/4.0.1 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.60.0 CPython/3.8.5

File hashes

Hashes for sanic-security-0.9.7.tar.gz
Algorithm Hash digest
SHA256 52bed8d76cb52ced5e447033f32f861bf6ffdb7f6e4edba13a3d9a896811d708
MD5 551b909de1b8a811f80f388cff837c1b
BLAKE2b-256 7588dabbdf17978d1de33707b242bf73d2423287b7ac797cd6214dcef664fc2c

See more details on using hashes here.

File details

Details for the file sanic_security-0.9.7-py3-none-any.whl.

File metadata

  • Download URL: sanic_security-0.9.7-py3-none-any.whl
  • Upload date:
  • Size: 33.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.1 importlib_metadata/4.0.1 pkginfo/1.7.0 requests/2.25.1 requests-toolbelt/0.9.1 tqdm/4.60.0 CPython/3.8.5

File hashes

Hashes for sanic_security-0.9.7-py3-none-any.whl
Algorithm Hash digest
SHA256 217df2bf4363a24c125c2931ee6f9f7772ce771e51abcb85fbb8b79ee0e9cef6
MD5 2d36f8d54e29c40feef15eb8e51acd10
BLAKE2b-256 90e965641e217832821fe16bcce3ad230a34cb07bbffb585c94ce390f677686a

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page