A re-usable FastAPI library that implements authentication, users, groups and permission handling.
Project description
Tasos API Authentication Library
A re-usable library that implements authentication, users, groups and permission handling for FastAPI. This library is meant to allow a FastAPI developer to get up and running quickly with functions to register, authenticate and manage groups and permissions for a new web application. It currently supports JSON Web Tokens (JWT) for authentication and depends on SqlAlchemy for database management and access. This library is written using asyncio.
NOTE: This is a work in progress and is not ready for production use.
Motivation
I wanted to create a library that would allow me to quickly setup authentication and authorization for a new web application. I also wanted to test out GitHub Copilot - this code and portions of the README were generated with Copilot's AI assistance.
Installation
The latest stable version can be found on pypi.org and can be installed with the package manager of your choice, e.g. for Poetry:
poetry add tasos-apiauth
Quick Start
Pre-requisites:
- Python 3.11+
- Poetry for package management
- Alembic for database migrations
- aiosqlite or your async database of choice (any SQLAlchemy supported database should work)
- Uvicorn for running the app
To try this library out in its current state, use Poetry to install the project as a dependency in your own project, e.g. you can run the following:
poetry add https://github.com/tristeng/tasos-apiauth.git
Create a .env file in the root of your project and fill in the values (or set them in your environment):
APIAUTH_SECRET_KEY=***REDACTED***
APIAUTH_ALGORITHM=HS256
APIAUTH_ACCESS_TOKEN_EXPIRE_MINUTES=15
APIAUTH_DATABASE_URL=sqlite+aiosqlite:///./demo.db?check_same_thread=false
See tasos/apiauth/config.py
for more configuration options and their defaults.
You can generate your secret key with the following command:
generate with openssl rand -hex 32
or
python -c 'import secrets; print(secrets.token_hex(32))'
Initialize alembic for asyncio:
alembic init -t async alembic
Edit the alembic.ini
file and set the sqlalchemy.url
to the same value as APIAUTH_DATABASE_URL
in your .env file.
Edit the alembic/env.py
file and add the following (and as you develop your app, add any other models you create)):
from tasos.apiauth.model import Base
target_metadata = Base.metadata
and run the migrations:
alembic revision --autogenerate -m "Initial migration"
alembic upgrade head
Create an admin user for yourself with the included CLI, and follow the prompts to choose a password:
python -m tasos.apiauth.cli newuser you@website.com --admin
Create a main.py file in the root of your project and create your FastAPI app:
from fastapi import FastAPI
from tasos.apiauth.api import add_all_endpoints_to_app
# create your app as you like
app = FastAPI()
# add the endpoints to your app using the default URLs
add_all_endpoints_to_app(app)
If you only want to add select endpoints with custom base URLs:
from fastapi import FastAPI
from tasos.apiauth.api import add_base_endpoints_to_app, add_user_endpoints_to_app, add_group_endpoints_to_app, \
add_permission_endpoints_to_app
# create your app as you like
app = FastAPI()
# add only the endpoints you want at the paths you desire (path is optional and defaults to /api/auth)
add_base_endpoints_to_app(app, path="/api/auth")
add_user_endpoints_to_app(app, path="/api/users")
add_group_endpoints_to_app(app, path="/api/groups")
add_permission_endpoints_to_app(app, path="/api/permissions")
# you could also add all of them at the default paths
# add_all_endpoints_to_app(app)
Run the app in development mode:
uvicorn main:app --reload
You should now be able to navigate to http://localhost:8000/docs to see the Swagger UI and use its interactive features to interact with the API. You can also navigate to http://localhost:8000/redoc to see the ReDoc UI.
Custom Registration Functions
If you want to customize the registration process, you can create your own registration functions by appending them to the post registration hooks list. Define your function(s) to accept a database session and user object. For example:
from tasos.apiauth.model import UserOrm
from tasos.apiauth.api.base import post_registration_hooks
from sqlalchemy.ext.asyncio import AsyncSession
async def send_registration_email_hook(db: AsyncSession, user: UserOrm) -> None:
# for example, save a registration code to the database and send an email to the user with a link to confirm their
# email address
pass
post_registration_hooks.append(send_registration_email_hook)
Permissions
To implement permissions, you will first need to create a StrEnum class that defines the permissions you want to use, for example:
from enum import StrEnum
class MyCustomPermissions(StrEnum):
read = "read"
write = "write"
delete = "delete"
NOTE: We use a string enum instead of strings to avoid typos.
At this point, you'll need to create an alembic changeset to add the permissions to the database. You can do this by:
alembic revision --autogenerate -m "Add my custom permissions"
Edit the generated migration file and add the following:
from alembic import op
from tasos.apiauth.model import PermissionOrm
from wherever import MyCustomPermissions
def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.bulk_insert(PermissionOrm.__table__, [{"name": perm} for perm in MyCustomPermissions])
# ### end Alembic commands ###
Run the migration:
alembic upgrade head
As you add more permissions, you can repeat this process - you can use the same enumeration class or create a new one.
Now you can use the UserHasAllPermissions
or UserHasAnyPermission
dependency to check if a user has the permissions
you want. For example:
from fastapi import Depends, FastAPI
from tasos.apiauth.helpers import UserHasAllPermissions, UserHasAnyPermission
from tasos.apiauth.api import add_all_endpoints_to_app
from wherever import MyCustomPermissions
# create your app as you like
app = FastAPI()
# add the endpoints to your app using the default URLs
add_all_endpoints_to_app(app)
# you can add as many or as few permissions as you want using the list arguments
perm_checker_all = UserHasAllPermissions(MyCustomPermissions.read, MyCustomPermissions.write)
perm_checker_any = UserHasAnyPermission(MyCustomPermissions.read, MyCustomPermissions.write)
# now inject the dependency into your routes
@app.get("/some/endpoint", dependencies=[Depends(perm_checker_all)]) # user must have both read and write permissions
async def some_endpoint1() -> dict[str, str]:
return {"works": "wooo!"}
# user must have either read or write permission
@app.get("/some/other/endpoint", dependencies=[Depends(perm_checker_any)])
async def some_other_endpoint1() -> dict[str, str]:
return {"works": "wooo!"}
If the user doesn't have ALL of the permissions you specified, they will get a 403 Forbidden response.
Development
To develop this library, clone the repo and install the dependencies with Poetry:
poetry install
Testing
The tests are separated into unit and integration tests. The unit tests are run against a mocked database and the
integration tests are run against a real database (SQLite). The integration tests are slower and the unit test mocking
can interfere with the integration tests, so they are separated. The integration tests are marked with the
@pytest.mark.integration
decorator and can be run separately from the unit tests.
To run the unit tests:
poetry run pytest -m "not integration"
To run the integration tests:
poetry run pytest -m "integration"
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file tasos_apiauth-0.2.1.tar.gz
.
File metadata
- Download URL: tasos_apiauth-0.2.1.tar.gz
- Upload date:
- Size: 20.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/5.1.0 CPython/3.12.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | f21d1bde08bbb7c3ce2e2fc73a5c0c942ac9e024c3c7aa169ec4e08d63d041d9 |
|
MD5 | 7a5f966b15ff6f033e37f258dc44d1a5 |
|
BLAKE2b-256 | 0c7c91363b14b5d1452516ec719826f0b7df08cf76c4b1f8210c462f69de0cdd |
File details
Details for the file tasos_apiauth-0.2.1-py3-none-any.whl
.
File metadata
- Download URL: tasos_apiauth-0.2.1-py3-none-any.whl
- Upload date:
- Size: 23.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/5.1.0 CPython/3.12.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 0f579e81fda3052f33ecd46e717197c0484f504478b4ad08ceefe31a51cab206 |
|
MD5 | 82590ed25ce2e7833d38ddd02ecae12c |
|
BLAKE2b-256 | 49bee7c7076008065f3d843a83365b5dec11251b329c91101bdf628e4523234f |