Skip to main content

A Python authentication bridge that integrates with frappe-client to enable SSO across Django, Flask, FastAPI, and vanilla Python apps

Project description

Frappe Auth Bridge

A secure Python authentication bridge that integrates with frappe-client to authenticate users, persist web sessions, sync roles & permissions, and enable SSO across Django, Flask, FastAPI, and vanilla Python applications.

Features

  • Secure Authentication via frappe-client using username/password or API keys
  • Session Management with encrypted storage and auto-refresh
  • Role & Permission Caching for fast authorization checks
  • Framework Support for Django, Flask, FastAPI, and vanilla Python
  • Auto Token Refresh before expiration
  • Multi-Tenant Support for multiple Frappe servers
  • Async HTTP with httpx for high performance
  • Audit Logging for all authentication events
  • Rate Limiting to prevent abuse
  • Security First with Fernet encryption and HTTPS-only cookies

Installation

Basic Installation

pip install frappe-auth-bridge

With Framework Support

# For Django
pip install frappe-auth-bridge[django]

# For Flask
pip install frappe-auth-bridge[flask]

# For FastAPI
pip install frappe-auth-bridge[fastapi]

# For Redis session backend
pip install frappe-auth-bridge[redis]

# Install all extras
pip install frappe-auth-bridge[all]

Development Installation

git clone https://github.com/yourusername/frappe-auth-bridge.git
cd frappe-auth-bridge
pip install -e .

Quick Start

Vanilla Python

from frappe_auth_bridge import FrappeAuthBridge

# Initialize
auth = FrappeAuthBridge("https://example.erpnext.com")

# Login
session = auth.login_with_password("user@example.com", "password")
print(f"Logged in as: {session.user.email}")
print(f"Roles: {session.user.roles}")

# Logout
auth.logout(session.session_id)

Using FrappeClient Directly

After authentication, you can access the underlying FrappeClient to perform any Frappe API operations:

from frappe_auth_bridge import FrappeAuthBridge

auth = FrappeAuthBridge("https://example.erpnext.com")

# Option 1: After login, use auth.client directly
session = auth.login_with_password("user@example.com", "password")

# Now you can use all FrappeClient methods
users = auth.client.get_list("User", fields=["name", "email"])
user_doc = auth.client.get_doc("User", "user@example.com")

# Insert a document
new_todo = auth.client.insert({
    "doctype": "ToDo",
    "description": "My new todo"
})

# Update a document
auth.client.update({
    "doctype": "ToDo",
    "name": new_todo.get("name"),
    "status": "Closed"
})

# Delete a document
auth.client.delete("ToDo", new_todo.get("name"))

# Make API calls
result = auth.client.get_api("frappe.client.get_list", {
    "doctype": "Company",
    "fields": '["name"]'
})

# Option 2: Set credentials for client separately
auth.set_client_credentials("user@example.com", "password")
companies = auth.client.get_list("Company")  # Auto-authenticates

# Option 3: Get a one-off client with explicit credentials
client = auth.get_client(username="user@example.com", password="password")
docs = client.get_list("Customer")

# Option 4: Use API key for client
auth.set_client_api_key("api-key", "api-secret")
items = auth.client.get_list("Item")

FastAPI

from fastapi import FastAPI, Request
from frappe_auth_bridge import FrappeAuthBridge
from frappe_auth_bridge.middleware.fastapi import FrappeAuthMiddleware

app = FastAPI()
auth_bridge = FrappeAuthBridge("https://example.erpnext.com")

# Add middleware
app.add_middleware(
    FrappeAuthMiddleware,
    auth_bridge=auth_bridge,
    exempt_paths=["/", "/login", "/docs"]
)

@app.get("/secure")
async def secure_endpoint(request: Request):
    if not hasattr(request.state, 'user') or request.state.user is None:
        return {"error": "Not authenticated"}, 401
    
    return {"user": request.state.user.email}

Flask

from flask import Flask, g
from frappe_auth_bridge import FrappeAuthBridge
from frappe_auth_bridge.middleware.flask import FrappeAuthMiddleware, auth_required

app = Flask(__name__)
auth_bridge = FrappeAuthBridge("https://example.erpnext.com")

# Initialize middleware
FrappeAuthMiddleware(app, auth_bridge)

@app.route("/secure")
@auth_required
def secure():
    return {"user": g.user.email}

Django

# settings.py
from frappe_auth_bridge import FrappeAuthBridge

FRAPPE_AUTH_BRIDGE = FrappeAuthBridge("https://example.erpnext.com")

MIDDLEWARE = [
    # ... other middleware
    'frappe_auth_bridge.middleware.django.FrappeAuthMiddleware',
]

# views.py
def secure_view(request):
    if not hasattr(request, 'user') or request.user is None:
        return JsonResponse({"error": "Not authenticated"}, status=401)
    
    return JsonResponse({"user": request.user.email})

Configuration

Environment Variables

# Optional: Custom encryption key for session storage
export FRAPPE_AUTH_SECRET_KEY="your-secret-key"

# Frappe server URL
export FRAPPE_URL="https://example.erpnext.com"

Advanced Configuration

from frappe_auth_bridge import FrappeAuthBridge
from frappe_auth_bridge.session import RedisSessionStore
from frappe_auth_bridge.security import EncryptionManager

# Custom encryption
encryption = EncryptionManager("custom-key")

# Redis session backend
session_store = RedisSessionStore(
    encryption_manager=encryption,
    redis_url="redis://localhost:6379"
)

# Initialize with custom settings
auth = FrappeAuthBridge(
    frappe_url="https://example.erpnext.com",
    session_store=session_store,
    enable_rate_limiting=True,
    enable_audit_logging=True,
    session_ttl_seconds=3600,  # 1 hour
)

Multi-Tenant Support

from frappe_auth_bridge import FrappeAuthBridge
from frappe_auth_bridge.models import TenantConfig

auth = FrappeAuthBridge(
    frappe_url="https://default.erpnext.com",
    multi_tenant=True
)

# Add tenants
auth.add_tenant(TenantConfig(
    tenant_id="company_a",
    frappe_url="https://company-a.erpnext.com"
))

auth.add_tenant(TenantConfig(
    tenant_id="company_b",
    frappe_url="https://company-b.erpnext.com"
))

# Login to specific tenant
session = auth.login_with_password(
    "user@example.com",
    "password",
    tenant_id="company_a"
)

API Reference

FrappeAuthBridge

Main authentication class:

  • login_with_password(username, password, tenant_id=None) - Authenticate with credentials
  • authenticate_api_key(api_key, api_secret, tenant_id=None) - Authenticate with API keys
  • refresh_token(session_id) - Refresh an expiring session
  • logout(session_id) - Invalidate a session
  • get_user_roles(user_email) - Get cached user roles
  • get_user_permissions(user_email) - Get cached permissions
  • validate_permission(user_email, doctype, permission_type) - Check permission

Security Features

  • Fernet Encryption for all session data at rest
  • No Plaintext Passwords ever stored
  • HTTPS-only Cookies in production
  • Rate Limiting with token bucket algorithm
  • Audit Logging with sensitive field redaction
  • Environment Variable Secrets only
  • SSL Certificate Verification enforced
  • Session Auto-Refresh before expiry
  • Token Revocation on logout

Testing

# Install dev dependencies
pip install -e ".[dev]"

# Run tests
pytest tests/ -v

# With coverage
pytest tests/ --cov=frappe_auth_bridge --cov-report=html

# Run specific test
pytest tests/test_core.py -v

Examples

See the examples/ directory for complete working examples:

  • fastapi_example.py - FastAPI application with login/logout
  • flask_example.py - Flask application with middleware
  • django_example.py - Django configuration and views
  • vanilla_example.py - Standalone Python script

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

License

MIT License - see LICENSE file for details

Credits

Built with:

Support

For issues and questions:

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

frappe_auth_bridge-0.2.0.tar.gz (20.1 kB view details)

Uploaded Source

Built Distribution

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

frappe_auth_bridge-0.2.0-py3-none-any.whl (24.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: frappe_auth_bridge-0.2.0.tar.gz
  • Upload date:
  • Size: 20.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for frappe_auth_bridge-0.2.0.tar.gz
Algorithm Hash digest
SHA256 11ac5a5ede485b206116a2e1a2e7d1bbef258b2f0e6508e7b4d3cfb2a03dd348
MD5 98ed9ef7683d6d89254a2725ddc8ff0b
BLAKE2b-256 d4e3097daf96cfdcab321612bb2c250f56803007ab272bda9f9ff06393872054

See more details on using hashes here.

Provenance

The following attestation bundles were made for frappe_auth_bridge-0.2.0.tar.gz:

Publisher: pypi.yml on mymi14s/frappe-auth-bridge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

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

File metadata

File hashes

Hashes for frappe_auth_bridge-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7db4bfa37e0608febab2852dbda236476326eb5270dd4b61083e14c5450a75e1
MD5 02eab8bc3c8e035a9bedfb95db4036de
BLAKE2b-256 14b196081cfb842e5b230acf6b25b5dac64604897da10b1e6c697277aec97a87

See more details on using hashes here.

Provenance

The following attestation bundles were made for frappe_auth_bridge-0.2.0-py3-none-any.whl:

Publisher: pypi.yml on mymi14s/frappe-auth-bridge

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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