Skip to main content

Simple email/password authentication for Django, FastAPI, and Flask

Project description

auth101

auth101 is a small Python package to provide email/password authentication with a pluggable user schema and pluggable user storage backends. It uses Argon2 via passlib for password hashing by default and includes JWT token generation for authenticated sessions.

Features

  • Email/password authentication
  • Secure password hashing with Argon2
  • JWT token generation and verification
  • Pluggable user schema and storage backends
  • In-memory storage for development
  • SQLAlchemy support for SQL database persistence
  • Django ORM support for Django projects

Quick usage

Basic Authentication

from auth101 import Authenticator
from auth101.models import User

# default in-memory store
auth = Authenticator()
user = auth.register("alice@example.com", "s3cr3t", full_name="Alice")
assert auth.authenticate("alice@example.com", "s3cr3t")

# provide your own factory to accept a different user schema
def my_user_factory(**kwargs):
    return User(**kwargs)

auth2 = Authenticator(user_factory=my_user_factory)

JWT Token Authentication

from auth101 import Authenticator

# Configure authenticator with JWT support
auth = Authenticator(
    jwt_secret_key="your-secret-key-change-in-production",
    jwt_expires_in_minutes=60,  # Token expires in 60 minutes
    jwt_algorithm="HS256"  # Optional, defaults to HS256
)

# Register a new user and get JWT token
user, token = auth.register_with_token("bob@example.com", "password123")
print(f"Token: {token}")

# Authenticate existing user and get JWT token
result = auth.authenticate_with_token("bob@example.com", "password123")
if result:
    user, token = result
    print(f"User authenticated: {user.email}")
    print(f"Token: {token}")

Token Verification

from auth101.core.security import verify_token, get_user_id_from_token

# Verify a token
payload = verify_token(token, "your-secret-key")
if payload:
    print(f"Token valid, user_id: {payload['sub']}")
else:
    print("Token invalid or expired")

# Extract user_id from token
user_id = get_user_id_from_token(token, "your-secret-key")
if user_id:
    print(f"User ID: {user_id}")

Configuration Options

When initializing the Authenticator, you can configure:

Simplified configuration:

  • db_url: Database connection string for SQLAlchemy (e.g., "postgresql://user:pass@localhost/db")
  • django_model: Django model class for DjangoUserStore
  • auto_create_tables: Automatically create database tables (for SQLAlchemy, default: False)

Advanced configuration:

  • store: User storage backend (default: MemoryUserStore)
  • user_factory: Factory function to create user instances
  • pwd_ctx: Password hashing context (default: Argon2)

JWT token configuration:

  • jwt_secret_key: Secret key for JWT signing (required for JWT features)
  • jwt_algorithm: JWT algorithm (default: "HS256")
  • jwt_expires_in_minutes: Token expiration time in minutes (default: 60)

Note: You can only provide one of store, db_url, or django_model. If none are provided, an in-memory store is used by default.

Database Persistence

SQLAlchemy Support

Install SQLAlchemy support:

pip install auth101[sqlalchemy]

Simplified approach - just pass a connection string:

from auth101 import Authenticator

# Simply pass your database URL!
auth = Authenticator(
    db_url="postgresql://user:pass@localhost/mydb",
    auto_create_tables=True  # Automatically create tables
)

user = auth.register("alice@example.com", "password123")

Advanced approach - for custom engine configuration:

from sqlalchemy import create_engine
from auth101 import Authenticator
from auth101.core.persistence import SQLAlchemyUserStore

# Create engine with custom options
engine = create_engine(
    "postgresql://user:pass@localhost/mydb",
    pool_size=10,
    max_overflow=20
)

# Create store and tables
store = SQLAlchemyUserStore(engine)
store.create_tables()

# Use with authenticator
auth = Authenticator(store=store)
user = auth.register("alice@example.com", "password123")

Works with any SQLAlchemy-supported database: PostgreSQL, MySQL, SQLite, etc.

Django Support

Install for Django:

pip install auth101[django]
  1. Add model to your Django app:
from django.db import models
import uuid

class AuthUser(models.Model):
    id = models.CharField(max_length=36, primary_key=True, 
                         default=lambda: str(uuid.uuid4()))
    email = models.EmailField(unique=True, db_index=True)
    password_hash = models.CharField(max_length=255)
    is_active = models.BooleanField(default=True)
    
    class Meta:
        db_table = 'auth_users'
  1. Run migrations:
python manage.py makemigrations
python manage.py migrate
  1. Use in your Django views:

Simplified approach:

from auth101 import Authenticator
from myapp.models import AuthUser

# Just pass your Django model!
auth = Authenticator(django_model=AuthUser)

# In your view:
user = auth.register(email, password)
authenticated = auth.authenticate(email, password)

Advanced approach - if you need explicit store control:

from auth101 import Authenticator
from auth101.core.persistence import DjangoUserStore
from myapp.models import AuthUser

store = DjangoUserStore(AuthUser)
auth = Authenticator(store=store)

# In your view:
user = auth.register(email, password)
authenticated = auth.authenticate(email, password)

Framework Integration Examples

FastAPI

from fastapi import FastAPI, HTTPException, Depends
from auth101 import Authenticator
from auth101.core.security import get_user_id_from_token

# Initialize with database
auth = Authenticator(
    db_url="postgresql://user:pass@localhost/db",
    auto_create_tables=True,
    jwt_secret_key="your-secret-key",
    jwt_expires_in_minutes=60
)

app = FastAPI()

@app.post("/register")
async def register(email: str, password: str):
    try:
        user, token = auth.register_with_token(email, password)
        return {"user_id": user.id, "token": token}
    except ValueError as e:
        raise HTTPException(400, str(e))

@app.post("/login")
async def login(email: str, password: str):
    result = auth.authenticate_with_token(email, password)
    if not result:
        raise HTTPException(401, "Invalid credentials")
    user, token = result
    return {"token": token}

# Protected route
def get_current_user(token: str):
    user_id = get_user_id_from_token(token, auth.jwt_secret_key)
    if not user_id:
        raise HTTPException(401, "Invalid token")
    return user_id

@app.get("/protected")
async def protected(user_id: str = Depends(get_current_user)):
    return {"message": f"Hello user {user_id}"}

Flask

from flask import Flask, request, jsonify
from auth101 import Authenticator
from auth101.core.security import get_user_id_from_token
from functools import wraps

# Initialize with database
auth = Authenticator(
    db_url="sqlite:///auth.db",
    auto_create_tables=True,
    jwt_secret_key="your-secret-key"
)

app = Flask(__name__)

# Protected route decorator
def token_required(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization', '').replace('Bearer ', '')
        user_id = get_user_id_from_token(token, auth.jwt_secret_key)
        if not user_id:
            return jsonify({"error": "Invalid token"}), 401
        return f(user_id, *args, **kwargs)
    return decorated

@app.route('/register', methods=['POST'])
def register():
    data = request.json
    try:
        user, token = auth.register_with_token(data['email'], data['password'])
        return jsonify({"user_id": user.id, "token": token})
    except ValueError as e:
        return jsonify({"error": str(e)}), 400

@app.route('/login', methods=['POST'])
def login():
    data = request.json
    result = auth.authenticate_with_token(data['email'], data['password'])
    if not result:
        return jsonify({"error": "Invalid credentials"}), 401
    user, token = result
    return jsonify({"token": token})

@app.route('/protected')
@token_required
def protected(user_id):
    return jsonify({"message": f"Hello user {user_id}"})

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

auth101-0.2.0.tar.gz (16.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

auth101-0.2.0-py3-none-any.whl (17.4 kB view details)

Uploaded Python 3

File details

Details for the file auth101-0.2.0.tar.gz.

File metadata

  • Download URL: auth101-0.2.0.tar.gz
  • Upload date:
  • Size: 16.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for auth101-0.2.0.tar.gz
Algorithm Hash digest
SHA256 76dcc4e16837e5d33cdb4cf35aa23116ceaf4d9025121b3ada9c9d37b01f0f14
MD5 49ae53caed15743bdf473695c2eae798
BLAKE2b-256 13045aa442183e55049eebf579e73a159aa87ba4ab96827f9e2ad4013f2ae4d6

See more details on using hashes here.

File details

Details for the file auth101-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: auth101-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 17.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for auth101-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4a38ec9a0929e089d3a5013e6ebf498b00215a01a95c70d7a59022915c916c09
MD5 33e2e52f0d4d07c90d792e31c7b052fa
BLAKE2b-256 70786871867cec1be120cc5750ef23d05ef0535f4d11afb8790de7c543e5633a

See more details on using hashes here.

Supported by

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