Authentication and authorization library for Genro microservices ecosystem
genro-auth provides token-based authentication with refresh capabilities, scope-based authorization, and FastAPI integration for the Genro microservices ecosystem.
- 🔐 Token Management - Generate, validate, and revoke access tokens
- 🔄 Refresh Tokens - Long-lived tokens for obtaining new access tokens
- 🎯 Scope-Based Authorization - Fine-grained permission control
- ⚡ FastAPI Integration - Ready-to-use dependencies for FastAPI
- 💾 Multiple Storage Backends - Memory (dev) and Redis (production)
- 🧪 Easy Testing - Mock tokens for unit tests
- 🔒 Secure by Default - Token hashing, expiration, revocation
# Basic installation
pip install genro-auth
# With Redis support
pip install genro-auth[redis]from genro_auth import TokenManager
from genro_auth.storage import MemoryTokenStorage
# Initialize token manager
storage = MemoryTokenStorage()
token_manager = TokenManager(
token_ttl=3600, # 1 hour access token
refresh_ttl=86400, # 24 hours refresh token
storage_backend=storage
)
# Generate token pair
tokens = token_manager.generate_token(
user_id='user123',
scopes=['storage.read', 'storage.write']
)
print(tokens)
# {
# 'access_token': '...',
# 'refresh_token': '...',
# 'expires_in': 3600,
# 'token_type': 'Bearer'
# }
# Validate token
user_data = token_manager.validate_token(tokens['access_token'])
print(user_data)
# {
# 'user_id': 'user123',
# 'scopes': ['storage.read', 'storage.write'],
# 'type': 'access',
# 'expires_at': datetime(...)
# }
# Refresh token
new_tokens = token_manager.refresh_token(tokens['refresh_token'])
# Revoke token
token_manager.revoke_token(tokens['access_token'])from fastapi import FastAPI, Depends
from genro_auth import TokenManager
from genro_auth.fastapi import create_auth_dependency, create_scope_dependency
from genro_auth.storage import MemoryTokenStorage
app = FastAPI()
token_manager = TokenManager(storage_backend=MemoryTokenStorage())
# Create auth dependency
require_auth = create_auth_dependency(token_manager)
require_admin = create_scope_dependency(['admin'])
@app.post("/login")
async def login(username: str, password: str):
"""Generate access token."""
# Your credential validation logic here
if not validate_credentials(username, password):
raise HTTPException(401, "Invalid credentials")
return token_manager.generate_token(
user_id=username,
scopes=['read', 'write']
)
@app.get("/protected")
async def protected_route(user_data: dict = Depends(require_auth)):
"""Protected endpoint - requires valid token."""
return {"message": f"Hello {user_data['user_id']}"}
@app.get("/admin")
async def admin_route(user_data: dict = Depends(require_admin)):
"""Admin endpoint - requires 'admin' scope."""
return {"message": "Admin access granted"}
@app.post("/refresh")
async def refresh(refresh_token: str):
"""Refresh access token."""
new_tokens = token_manager.refresh_token(refresh_token)
if not new_tokens:
raise HTTPException(401, "Invalid refresh token")
return new_tokens
@app.post("/logout")
async def logout(user_data: dict = Depends(require_auth)):
"""Revoke current token."""
# Get token from request (implementation depends on your setup)
token_manager.revoke_token(current_token)
return {"ok": True}import redis
from genro_auth import TokenManager
from genro_auth.storage import RedisTokenStorage
# Connect to Redis
redis_client = redis.Redis(
host='localhost',
port=6379,
decode_responses=True
)
# Use Redis storage
storage = RedisTokenStorage(redis_client)
token_manager = TokenManager(
token_ttl=3600,
refresh_ttl=86400,
storage_backend=storage
)For development and testing:
from genro_auth.storage import MemoryTokenStorage
storage = MemoryTokenStorage()Pros: No dependencies, fast Cons: Not persistent, not scalable
For production:
import redis
from genro_auth.storage import RedisTokenStorage
redis_client = redis.Redis(host='localhost', port=6379)
storage = RedisTokenStorage(redis_client)Pros: Persistent, scalable, automatic expiration Cons: Requires Redis server
Implement your own backend:
from genro_auth.storage import TokenStorage
class CustomStorage(TokenStorage):
def set(self, key: str, value: dict):
# Your implementation
pass
def get(self, key: str) -> dict | None:
# Your implementation
pass
def delete(self, key: str):
# Your implementation
passUse scopes for fine-grained authorization:
# Generate token with scopes
tokens = token_manager.generate_token(
user_id='user123',
scopes=['storage.read', 'storage.write', 'storage.delete']
)
# Check scopes in FastAPI
from genro_auth.fastapi import create_scope_dependency
require_write = create_scope_dependency(['storage.write'])
@app.put("/files/{path}")
async def upload_file(
path: str,
data: bytes,
user_data: dict = Depends(require_write)
):
# Only users with storage.write scope can access
...Common scope patterns:
resource.action- e.g.,storage.read,mail.sendadmin- Full administrative accessread,write,delete- Generic permissions
import pytest
from genro_auth import TokenManager
from genro_auth.storage import MemoryTokenStorage
@pytest.fixture
def token_manager():
return TokenManager(storage_backend=MemoryTokenStorage())
def test_protected_endpoint(token_manager):
# Generate test token
tokens = token_manager.generate_token(
user_id='test_user',
scopes=['read']
)
# Use in test
response = client.get(
"/protected",
headers={"Authorization": f"Bearer {tokens['access_token']}"}
)
assert response.status_code == 200# Token lifetimes (seconds)
AUTH_TOKEN_TTL=3600 # 1 hour
AUTH_REFRESH_TTL=86400 # 24 hours
# Redis (if using)
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=optionaltoken_manager = TokenManager(
token_ttl=3600, # Access token lifetime (seconds)
refresh_ttl=86400, # Refresh token lifetime (seconds)
storage_backend=storage # Storage backend instance
)- Token Storage: Tokens are hashed (SHA-256) before storage
- Expiration: Both access and refresh tokens expire
- Revocation: Tokens can be revoked before expiration
- HTTPS: Always use HTTPS in production
- Token Rotation: Refreshing revokes old access token
- Secrets: Use strong, random tokens (32 bytes)
┌─────────────────────────────────────┐
│ Your Application │
└──────────┬──────────────────────────┘
│
↓
┌─────────────────────────────────────┐
│ genro-auth │
│ ┌─────────────┐ ┌──────────────┐ │
│ │TokenManager │ │ FastAPI │ │
│ │ │ │ Dependencies │ │
│ └──────┬──────┘ └──────────────┘ │
│ │ │
│ ↓ │
│ ┌─────────────────────────────┐ │
│ │ Storage Backend │ │
│ │ (Memory / Redis / Custom) │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
See the examples/ directory for complete working examples:
basic_usage.py- Token generation and validationfastapi_app.py- Complete FastAPI integrationredis_setup.py- Production Redis configurationcustom_storage.py- Implementing custom storage backend
See API Documentation for detailed API reference.
# Clone repository
git clone https://github.com/genropy/genro-auth
cd genro-auth
# Install development dependencies
pip install -e ".[dev,redis]"
# Run tests
pytest
# Run tests with coverage
pytest --cov=genro_auth --cov-report=html
# Format code
black genro_auth tests
# Lint
ruff check genro_auth tests
# Type check
mypy genro_auth- Token generation and validation
- Refresh token support
- Scope-based authorization
- FastAPI dependencies
- Memory storage backend
- Redis storage backend
- JWT support (in addition to opaque tokens)
- OAuth2 integration helpers
- Rate limiting per token
- Token usage audit logging
- Database storage backend (PostgreSQL, SQLite)
- genro-storage - Storage abstraction layer
- genro-storage-proxy - Storage HTTP microservice
- genro-mail-proxy - Email service
MIT License - see LICENSE file for details.
Contributions are welcome! Please read CONTRIBUTING.md for guidelines.
- Documentation: https://genro-auth.readthedocs.io
- Issues: https://github.com/genropy/genro-auth/issues
- Discussions: https://github.com/genropy/genro-auth/discussions