Skip to main content

An effective, simple, and async security library for Sanic.

Project description

Contributors Forks Stargazers Issues Conda Downloads Code style: black


Sanic Security

An effective, simple, and async security library for Sanic.

Table of Contents

About The Project

Sanic Security is an authentication, authorization, and verification library designed for use with Sanic. This library contains a variety of features including:

  • Login, registration, and authentication
  • Two-step verification
  • Two-factor authentication
  • Captcha
  • Wildcard and role based authorization

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

aphopkins

Please visit security.sunsetdeveloper.com for documentation.

Getting Started

In order to get started, please install pip.

Prerequisites

  • pip
sudo apt-get install python3-pip

Installation

  • Install the Sanic Security pip package.
pip3 install sanic-security

Configuration

Sanic Security configuration is merely an object that can be modified either using dot-notation or like a dictionary.

For example:

from sanic_security.configuration import config

config.SECRET = "This is a big secret. Shhhhh"
config["CAPTCHA_FONT"] = "./resources/captcha.ttf"

You can also use the update() method like on regular dictionaries.

  • Default configuration values:
Key Value
SECRET This is a big secret. Shhhhh
CACHE ./security-cache
SESSION_SAMESITE strict
SESSION_SECURE False
SESSION_HTTPONLY True
SESSION_DOMAIN None
SESSION_EXPIRES_ON_CLIENT False
SESSION_PREFIX token
SESSION_ENCODING_ALGORITHM HS256
CAPTCHA_SESSION_EXPIRATION 60
CAPTCHA_FONT captcha.ttf
TWO_STEP_SESSION_EXPIRATION 200
AUTHENTICATION_SESSION_EXPIRATION 2692000

Usage

Sanic Security implementation is easy.

The tables in the below examples represent example request form-data.

Authentication

  • Registration

Phone can be null or empty.

Key Value
username test
email test@test.com
phone 19811354186
password testpass
captcha Aj8HgD
@app.post("api/auth/register")
@requires_captcha()
async def on_register(request, captcha_session):
    account = await register(request)
    two_step_session = await request_two_step_verification(request, account)
    await email_code(two_step_session.code) #Custom method for emailing verification code.
    response = json("Registration successful!", two_step_session.account.json())
    two_step_session.encode(response)
    return response
  • Verify Account
Key Value
code G8ha9nVae
@app.post("api/auth/verify")
async def on_verify(request):
    two_step_session = await verify_account(request)
    return json("You have verified your account and may login!", two_step_session.account.json())
  • Login
Key Value
email test@test.com
password testpass
@app.post("api/auth/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
  • Login (With two-factor authentication)
Key Value
email test@test.com
password testpass
@app.post("api/auth/login")
async def on_two_factor_login(request):
    authentication_session = await login(request, two_factor=True)
    two_step_session = await request_two_step_verification(request, authentication_session.account)
    await email_code(two_step_session.code) #Custom method for emailing verification code.
    response = json("Login successful! A second factor is now required to be authenticated.", authentication_session.account.json())
    authentication_session.encode(response)
    two_step_session.encode(response)
    return response
  • Second Factor
Key Value
code G8ha9nVae
@app.post("api/auth/login/second-factor")
@requires_two_step_verification()
async def on_login_second_factor(request, two_step_verification):
  authentication_session = await on_second_factor(request)
  response = json("Second factor attempt successful! You may now be authenticated!",
                  authentication_session.account.json())
  return response
  • Logout
@app.post("api/auth/logout")
@requires_authentication()
async def on_logout(request, authentication_session):
    await logout(authentication_session)
    response = json("Logout successful!", authentication_session.account.json())
    return response
  • Requires Authentication
@app.post("api/auth")
@requires_authentication()
async def on_authenticated(request, authentication_session):
    return json(f"Hello {authentication_session.account.username}! You have been authenticated.", 
                authentication_session.account.json())

Captcha

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

1001 Free Fonts

Recommended Font

Captcha challenge example:

Captcha image.

  • Request Captcha
@app.post("api/captcha/request")
async def on_request_captcha(request):
    captcha_session = await request_captcha(request)
    response = await captcha_session.get_image()
    captcha_session.encode(response)
    return response
  • Requires Captcha
Key Value
captcha Aj8HgD
@app.post("api/captcha")
@requires_captcha()
async def on_captcha_attempt(request, captcha_session):
    return json("Captcha attempt successful!", captcha_session.json())

Two-step Verification

  • Request Two-step Verification
Key Value
email test@test.com
captcha Aj8HgD
@app.post("api/verification/request")
@requires_captcha()
async def on_request_verification(request, captcha_session):
    two_step_session = await request_two_step_verification(request)
    await email_code(two_step_session.code) #Custom method for emailing verification code.
    response = json("Verification request successful!", two_step_session.account.json())
    two_step_session.encode(response)
    return response
  • Resend Two-step Verification Code
@app.post("api/verification/resend")
async def on_resend_verification(request):
    two_step_session = await TwoStepSession.decode(request)
    await email_code(two_step_session.code) #Custom method for emailing verification code.
    return json("Verification code resend successful!", two_step_session.account.json())
  • Requires Two-step Verification
Key Value
code G8ha9nVa
@app.post("api/verification")
@requires_two_step_verification()
async def on_verification(request, two_step_session):
    response = json("Two-step verification attempt successful!", two_step_session.account.json())
    return response

Authorization

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

Role-based permissions is a policy-neutral access-control mechanism defined around roles and privileges.

Wildcard permissions support the concept of multiple levels or parts. For example, you could grant a user the permission printer:query, printer:query,delete, and/or printer:*.

  • Require Permissions
@app.post("api/auth/perms")
@require_permissions("admin:update", "employee:add")
async def on_require_perms(request, authentication_session):
    return text("Account permitted.")
  • Require Roles
@app.post("api/auth/roles")
@require_roles("Admin", "Moderator")
async def on_require_roles(request, authentication_session):
    return text("Account permitted.")

Testing

  • Install httpx:
pip3 install httpx
  • Make sure the test Sanic instance (test/server.py) is running on your machine.

  • Run the unit test client (test/unit.py) and wait for results.

Tortoise

Sanic Security uses Tortoise ORM for database operations.

Tortoise ORM is an easy-to-use asyncio ORM (Object Relational Mapper).

  • Initialise your models and database like so:
async def init():
    # Here we create a SQLite DB using file "db.sqlite3"
    # also specify the app name of "models"
    # which contain models from "app.models"
    await Tortoise.init(
        db_url='sqlite://db.sqlite3',
        modules={'models': ['sanic_security.models', 'app.models']}
    )
    # Generate the schema
    await Tortoise.generate_schemas()

or

from tortoise.contrib.sanic import register_tortoise

register_tortoise(
    app, db_url="sqlite://:memory:", modules={"models": ["app.models", "sanic_security. models"]}, generate_schemas=True
)
  • Define your models like so:
from tortoise.models import Model
from tortoise import fields

class Tournament(Model):
    id = fields.IntField(pk=True)
    name = fields.TextField()
  • Use it like so:
# Create instance by save
tournament = Tournament(name='New Tournament')
await tournament.save()

# Or by .create()
await Tournament.create(name='Another Tournament')

# Now search for a record
tour = await Tournament.filter(name__contains='Another').first()
print(tour.name)

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.

Versioning

0.0.0.0

Major.Minor.Revision.Patch

Project details


Release history Release notifications | RSS feed

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-1.3.0.0.tar.gz (33.7 kB view details)

Uploaded Source

File details

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

File metadata

  • Download URL: sanic-security-1.3.0.0.tar.gz
  • Upload date:
  • Size: 33.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.4.2 importlib_metadata/4.8.1 pkginfo/1.7.1 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.5

File hashes

Hashes for sanic-security-1.3.0.0.tar.gz
Algorithm Hash digest
SHA256 dbe4e39eeef5397843d33e41687d33331e27bc17d8f3984b6a638aa2ee49f07c
MD5 0cedd8d0bf6f20ddbccdc7e678bf098c
BLAKE2b-256 a8c27823791d415f18a07ca7bb5298c3fdb165422650a4fd90f0f6bfe0520e1c

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