A powerful, simple, and async authentication and authorization library for Sanic.
Documentation
·
Report Bug
·
Request Feature
Async Auth is an authentication and authorization library made easy. Specifically designed for use with Sanic. This library comes packed with features such as:
- SMS and email verification
- Easy login and registering
- JWT
- Easy database integration
- Wildcard permissions
- Role permissions
- Captcha
- Password recovery
- Completely async
This repository has been starred by Sanic's core maintainer:
Documentation is currently auto generated. This is a placeholder until I write out better documentation.
In order to get started, please install pip.
- pip
sudo apt-get install python3-pip
- Install pip packages
pip3 install asyncauth
Once Async Auth is all setup and good to go, implementing is easy as pie.
First you have to create a configuration file called auth.ini. Below is an example of it's contents:
[AUTH]
secret=05jF8cSMAdjlXcXeS2ZJUHg7Tbyu
captcha_font=source-sans-pro.light.ttf
[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
Once you've configured Async Auth, you can initialize Sanic with the example below:
if __name__ == '__main__':
register_tortoise(app, db_url='mysql://username:password@rds.amazonaws.com/asyncauth',
modules={"models": ['asyncauth.core.models']}, generate_schemas=True)
app.run(host='0.0.0.0', port=8000, debug=True)
Most request bodies should be sent as form-data
. For my below examples, I use my own custom json method:
def json(message, content, status_code=200):
payload = {
'message': message,
'status_code': status_code,
'content': content
}
return sanic_json(payload, status=status_code)
- Registration (With all verification requirements)
Phone can be null or empty.
Key | Value |
---|---|
username | test |
test@test.com | |
phone | +19811354186 |
password | testpass |
captcha | Aj8HgD |
@app.post('api/register')
@requires_captcha()
async def on_register(request, captcha_session):
verification_session = await register(request)
await verification_session.text_code() # Text verification code.
await verification_session.email_code() # Or email verification code.
response = json('Registration successful', verification_session.account.json())
await verification_session.encode(response)
return response
- Registration (Without verification requirements)
Phone can be null or empty.
Key | Value |
---|---|
username | test |
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 |
---|---|
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())
await 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
- Account Recovery Request
This request is sent with an url argument instead of form-data
.
Key | Value |
---|---|
test@test.com |
@app.get('api/recovery/request')
@requires_captcha()
async def on_recovery_request(request):
verification_session = await request_account_recovery(request)
await verification_session.text_code() # Text verification code.
await verification_session.email_code() # Or email verification code.
response = json('Recovery request successful', verification_session.json())
await verification_session.encode(response)
return response
- Account Recovery
Key | Value |
---|---|
code | G8ha9nVa |
password | newpass |
@app.post('api/recovery')
@requires_verification()
async def on_recovery(request, verification_session):
await account_recovery(request, verification_session)
return json('Account recovered successfully', verification_session.account.json())
- Requires Authentication
@app.get('api/authentication')
@requires_authentication()
async def on_authentication(request, authentication_session):
return json('Hello ' + authentication_session.account.username + '! You are now authenticated.',
authentication_session.account.json())
You must download a .ttf font for captcha challenges and define the file's path in auth.ini.
- 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())
await captcha_session.encode(response)
return response
- Captcha Image
@app.get('api/captcha/img')
async def on_captcha_img(request):
img_path = await CaptchaSession().captcha_img(request)
return await file(img_path)
- Request Verification (Creates and encodes a new verification code, useful for when a verification session may be invalidated)
@app.get('api/verification/request')
async def on_request_verification(request):
verification_session = await request_verification(request)
await verification_session.text_code() # Text verification code.
await verification_session.email_code() # Or email verification code.
response = json('Verification request successful', verification_session.json())
await verification_session.encode(response)
return response
- Resend Verification (Does not create new verification code, simply resends the code)
@app.get('api/verification/resend')
async def on_resend_verification(request):
verification_session = await VerificationSession().decode(request)
await verification_session.text_code() # Text verification code.
await verification_session.email_code() # Or email verification code.
return json('Verification code resend successful', verification_session.json())
- Verify Account
Key | Value |
---|---|
code | G8ha9nVae |
@app.post('api/register/verify')
@requires_verification()
async def on_verify(request, verification_session):
await verify_account(verification_session)
return json('Verification successful!', verification_session.json())
- Requires Verification
Key | Value |
---|---|
code | G8ha9nVa |
@app.post('api/verification')
@requires_verification()
async def on_verification(request, verification_session):
return json('Hello ' + verification_session.account.username + '! You have verified yourself!',
authentication_session.account.json())
Async Auth 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.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):
"""
Tests client role authorization access.
"""
return text('Admin gained access!')
@app.exception(AuthError)
async def on_error(request, exception):
return json('An error has occurred!', {
'error': type(exception).__name__,
'summary': str(exception)
}, status_code=exception.status_code)
@app.middleware('response')
async def response_middleware(request, response):
xss_prevention(request, response)
@app.middleware('request')
async def request_middleware(request):
return https_redirect(request)
Keep up with Async Auth's Trello board for a list of proposed features, known issues, and in progress development.
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.
- Fork the Project
- Create 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 Request
Distributed under the GNU General Public License v3.0. See LICENSE
for more information.
Aidan Stewart - aidanstewart@sunsetdeveloper.com
Project Link: https://github.com/sunset-developer/Amy-Rose