Authentication and authorization module for Bazis framework.
Project description
Bazis Authing
Extension package for Bazis providing a flexible authentication system with support for various login methods.
Quick Start
# Install the package
uv add bazis-authing
# Configure in settings.py
BAZIS_AUTH_KINDS = [
'bazis.contrib.authing.password', # Username/password authentication
]
# Register routes in main router.py
from bazis.core.routing import BazisRouter
router = BazisRouter(prefix='/api/v1')
router.register('bazis.contrib.authing.router')
Table of Contents
- Description
- Requirements
- Installation
- Core Components
- Usage
- How It Works
- Examples
- Extension
- License
- Links
Description
Bazis Authing is an extension package for the Bazis framework that provides a flexible and extensible authentication system. The package includes:
- Universal
/auth/endpoint — central point for checking authentication status and getting available login methods - Built-in password authentication — ready-to-use module for username/password authentication
- AuthStore — cookie-based authentication state storage system
- Support for multiple authentication methods — easily add OAuth, SAML, LDAP, and other providers
- JWT integration — automatic JWT token generation after successful authentication
This package requires bazis and bazis-users packages to be installed.
Requirements
- Python: 3.12+
- bazis: latest version
- bazis-users: latest version
- PostgreSQL: 12+
- Redis: For caching
Installation
Using uv (recommended)
uv add bazis-authing
Using pip
pip install bazis-authing
Core Components
/auth/ Endpoint
Central endpoint for authentication operations.
URL: GET /api/v1/authing/auth/
Purpose:
- Check current authentication status
- Get list of available login methods
- Get JWT token for authenticated users
Behavior:
- If user is NOT authenticated → returns 401 error with list of available login methods:
{
"errors": [
{
"status": 401,
"code": "UNAUTHORIZED",
"title": "User is not authorized.",
"detail": "User is not authorized.",
"meta": {
"actions": [
{
"code": "password",
"name": "Login/Password",
"url": "/api/v1/authing/password/",
"method": "POST"
}
],
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
}
]
}
- If user is authenticated → returns user data and JWT token:
{
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"username": "user1",
"first_name": "John",
"last_name": "Doe",
"email": "user1@example.com",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"logout_actions": [
{
"url": "/api/v1/authing/logout/",
"method": "POST"
}
]
}
Authentication Methods
Password Authentication
Built-in module for username and password authentication.
Module code: password
Endpoints:
-
POST /api/v1/authing/password/ — web authentication with redirect
- Accepts username and password
- On successful authentication, sets cookie and redirects to
/auth/ - On failed authentication, returns error
-
POST /api/v1/authing/password/token/ — get JWT token for Swagger/API
- Accepts
usernameandpasswordviaOAuth2PasswordRequestForm - Returns JWT token directly without cookie
- Used for authentication in Swagger UI
- Accepts
Request schema (POST /password/):
{
"username": "user1",
"password": "password123"
}
Response schema (POST /password/token/):
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer"
}
AuthStore
Cookie-based authentication state storage system.
Location: bazis.contrib.authing.service.AuthStore
Purpose:
- Store temporary authentication data in encrypted cookie
- Track multi-step authentication progress
- Pass errors between requests
Main methods:
login(user, request, auth_type)— registers successful user authenticationset_error(code, detail)— saves authentication errordata_reset()— clears storageresponse_set_cookie(response)— adds cookie to response
AuthStoreTokenRequired:
Dependency for endpoints requiring authentication token in cookie:
from bazis.contrib.authing.service import AuthStoreTokenRequired
from fastapi import Depends
@router.post('/password/')
def password_auth(auth_store: AuthStoreTokenRequired = Depends()):
# auth_store guaranteed to contain valid token
pass
Usage
Project Setup
1. Add to settings.py:
# settings.py
# List of available authentication methods
BAZIS_AUTH_KINDS = [
'bazis.contrib.authing.password', # Username/password
# Add other methods here
]
2. Register routes:
# router.py
from bazis.core.routing import BazisRouter
router = BazisRouter(prefix='/api/v1')
# Register authentication routes
router.register('bazis.contrib.authing.router')
Basic Password Authentication
Typical web authentication flow:
Step 1: Client requests authentication status:
GET /api/v1/authing/auth/
Response (if not authenticated):
{
"errors": [
{
"status": 401,
"code": "UNAUTHORIZED",
"meta": {
"actions": [
{
"code": "password",
"name": "Login/Password",
"url": "/api/v1/authing/password/",
"method": "POST"
}
],
"token": "auth_token_here"
}
}
]
}
Step 2: Client performs authentication:
POST /api/v1/authing/password/
Cookie: bazis_auth=auth_token_here
Content-Type: application/json
{
"username": "user1",
"password": "password123"
}
Response (successful authentication):
HTTP 303 See Other
Location: /api/v1/authing/auth/?token=updated_token
Set-Cookie: bazis_auth=updated_token; Path=/; HttpOnly
Step 3: Client follows redirect and gets user data:
GET /api/v1/authing/auth/?token=updated_token
Cookie: bazis_auth=updated_token
Response:
{
"user_id": "123e4567-e89b-12d3-a456-426614174000",
"username": "user1",
"email": "user1@example.com",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Getting JWT Token
For direct JWT token retrieval (e.g., for mobile apps or Swagger):
POST /api/v1/authing/password/token/
Content-Type: application/x-www-form-urlencoded
username=user1&password=password123
Response:
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"token_type": "bearer"
}
This token can be used in the Authorization: Bearer <token> header for all subsequent requests.
How It Works
System Architecture
┌─────────────┐
│ Client │
└──────┬──────┘
│
│ 1. GET /auth/ (no cookie)
▼
┌─────────────────────┐
│ /auth/ endpoint │◄─── Checks user status
└──────┬──────────────┘
│
│ 2. Returns 401 + available actions
▼
┌─────────────┐
│ Client │
└──────┬──────┘
│
│ 3. POST /password/ (with credentials)
▼
┌─────────────────────┐
│ Password endpoint │◄─── Authenticates user
└──────┬──────────────┘
│
│ 4. Sets cookie + redirects to /auth/
▼
┌─────────────────────┐
│ /auth/ endpoint │◄─── Returns user data + JWT
└──────┬──────────────┘
│
│ 5. Returns user + token
▼
┌─────────────┐
│ Client │
└─────────────┘
Authentication Process
-
Initialization:
- Client requests
/auth/without cookie - System creates temporary token and returns it in meta
- Client saves this token
- Client requests
-
Authentication:
- Client sends credentials with token in cookie
- System validates credentials via Django
authenticate() - On success — saves
user_idin AuthStore - Sets updated cookie and redirects
-
Token Retrieval:
- Client requests
/auth/with cookie - System extracts
user_idfrom AuthStore - Generates JWT token via
user.jwt_build() - Returns user data and token
- Client requests
Security
- HttpOnly cookie — protection against XSS attacks
- Temporary tokens — limited lifetime for auth cookie
- Encrypted storage — cookie data is encrypted
- JWT tokens — for further API operations
Examples
Client Application Usage Example
// JavaScript client for authentication
class AuthClient {
constructor(baseUrl) {
this.baseUrl = baseUrl;
this.authToken = null;
this.jwtToken = null;
}
async checkAuth() {
const response = await fetch(`${this.baseUrl}/authing/auth/`, {
credentials: 'include'
});
if (response.ok) {
const data = await response.json();
this.jwtToken = data.token;
return { authenticated: true, user: data };
}
const error = await response.json();
this.authToken = error.errors[0].meta.token;
return {
authenticated: false,
actions: error.errors[0].meta.actions
};
}
async loginWithPassword(username, password) {
const response = await fetch(`${this.baseUrl}/authing/password/`, {
method: 'POST',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
'Cookie': `bazis_auth=${this.authToken}`
},
body: JSON.stringify({ username, password }),
redirect: 'follow'
});
if (response.ok) {
return await this.checkAuth();
}
throw new Error('Authentication failed');
}
async apiRequest(url, options = {}) {
return fetch(url, {
...options,
headers: {
...options.headers,
'Authorization': `Bearer ${this.jwtToken}`
}
});
}
}
// Usage
const auth = new AuthClient('https://api.example.com/api/v1');
// Check authentication status
const status = await auth.checkAuth();
if (!status.authenticated) {
// Perform login
await auth.loginWithPassword('user1', 'password123');
}
// Now you can make requests to protected endpoints
const response = await auth.apiRequest('/api/v1/protected/resource/');
Authentication Testing
import pytest
from bazis_test_utils.utils import get_api_client
from bazis.contrib.users import get_user_model
User = get_user_model()
@pytest.mark.django_db(transaction=True)
def test_password_authentication_flow(sample_app):
# Create user
user = User.objects.create_user(
'testuser',
email='test@example.com',
password='testpass123'
)
# Step 1: Get status (not authenticated)
response = get_api_client(sample_app).get('/api/v1/authing/auth/')
assert response.status_code == 400
data = response.json()
assert 'errors' in data
error = data['errors'][0]
assert error['code'] == 'UNAUTHORIZED'
auth_token = error['meta']['token']
actions = error['meta']['actions']
# Check available login methods
assert len(actions) == 1
assert actions[0]['code'] == 'password'
# Step 2: Perform authentication
response = get_api_client(sample_app, auth_token).post(
'/api/v1/authing/password/',
json_data={
'username': 'testuser',
'password': 'testpass123',
},
)
assert response.status_code == 200
# Step 3: Verify received data
data = response.json()
assert data['user_id'] == str(user.id)
assert data['username'] == 'testuser'
assert data['email'] == 'test@example.com'
assert 'token' in data # JWT token
# Step 4: Test invalid credentials
response = get_api_client(sample_app, auth_token).post(
'/api/v1/authing/password/',
json_data={
'username': 'testuser',
'password': 'wrongpassword',
},
)
assert response.status_code == 400
Extension
Adding New Authentication Method
You can add your own authentication method (OAuth, SAML, LDAP, etc.):
1. Create a module (e.g., myapp/auth_oauth.py):
from bazis.core.routing import BazisRouter
from django.utils.translation import gettext_lazy as _
AUTH_CODE = 'oauth'
router = BazisRouter(prefix='/oauth', tags=[_('OAuth Authentication')])
def get_login_action():
"""Returns login method information for /auth/"""
return {
'code': AUTH_CODE,
'name': 'OAuth Login',
'url': '/api/v1/authing/oauth/',
'method': 'GET',
}
def get_logout_actions():
"""Returns logout actions (optional)"""
return {
'url': '/api/v1/authing/oauth/logout/',
'method': 'POST',
}
@router.get('/')
def oauth_login():
# OAuth authentication logic
pass
2. Add to settings.py:
BAZIS_AUTH_KINDS = [
'bazis.contrib.authing.password',
'myapp.auth_oauth', # Your module
]
3. Register routes:
# myapp/router.py
from bazis.core.routing import BazisRouter
from . import auth_oauth
router = BazisRouter()
router.include_router(auth_oauth.router)
Now your authentication method will automatically appear in the list of available methods in /auth/!
License
Apache License 2.0
See LICENSE file for details.
Links
- Bazis Documentation — main repository
- Bazis Users — user management package
- Bazis Authing Repository — package repository
- Issue Tracker — report bugs or request features
Support
If you have questions or issues:
- Check Bazis documentation
- Search existing issues
- Create a new issue with detailed information
Made with ❤️ by Bazis team
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bazis_authing-2.2.0.tar.gz.
File metadata
- Download URL: bazis_authing-2.2.0.tar.gz
- Upload date:
- Size: 89.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5f914a3cfbda68e7afa8249a58678af0140b98487bf1a1584e32ef900cba8d46
|
|
| MD5 |
0a087a5d7ccf2e76ebfb409c81e633ce
|
|
| BLAKE2b-256 |
f72fc1a762805800ed66767cd802def8042a14b209ea708a98af55901367e798
|
File details
Details for the file bazis_authing-2.2.0-py3-none-any.whl.
File metadata
- Download URL: bazis_authing-2.2.0-py3-none-any.whl
- Upload date:
- Size: 25.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a00ed02f9bbcff5ec2751bcf2c3164233e08b18ea8a926d3e01b7691c92cfc6
|
|
| MD5 |
c4cf4e53a09236b963ed29ebe373dbe3
|
|
| BLAKE2b-256 |
3810d13b65179458a1dd53fdf672e793aaf9ff1ec4553e947c93578a815fd48b
|