Skip to main content

A re-usable FastAPI library that implements authentication, users, groups and permission handling.

Project description

Tasos API Authentication Library

python package

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

tasos_apiauth-0.1.2.tar.gz (20.4 kB view hashes)

Uploaded Source

Built Distribution

tasos_apiauth-0.1.2-py3-none-any.whl (23.1 kB view hashes)

Uploaded Python 3

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