Authentication System - Developer Quick Start¶
Prerequisites¶
- Python 3.12+
- Docker and Docker Compose
- Access to the codebase
Local Development Setup¶
1. Environment Variables¶
Copy the example env file and configure required settings:
Required auth-related variables:
# JWT Configuration
JWT_SECRET_KEY=your-secret-key-min-32-chars!!!!
JWT_ALGORITHM=HS256
ACCESS_TOKEN_EXPIRE_MINUTES=360
REFRESH_TOKEN_EXPIRE_MINUTES=10080
REFRESH_TOKEN_EXPIRE_MINUTES_SHORT=1440
# Password Hashing
BCRYPT_ROUNDS=12
# Redis (session state, rate limiting, blacklists)
REDIS_URL=redis://redis:6379/0
# Encryption (Virtuozzo creds, TOTP secrets)
ENCRYPTION_KEY=base64-urlsafe-32-byte-key
# Database
DATABASE_URL=postgresql+asyncpg://user:pass@postgres:5432/mbpanel # pragma: allowlist secret (example only)
# Email (Postmark - for OTP, verification, password reset)
POSTMARK_SERVER_TOKEN=your-postmark-token
POSTMARK_FROM_EMAIL=noreply@yourdomain.com
POSTMARK_DEVICE_APPROVAL_TEMPLATE_ID=123456
POSTMARK_SUSPICIOUS_LOGIN_TEMPLATE_ID=234567
2. Start Infrastructure¶
3. Install Backend Dependencies¶
4. Run Migrations¶
5. Start Development Server¶
The API will be available at http://localhost:8000
6. Access API Documentation¶
- Swagger UI:
http://localhost:8000/docs(requires basic auth if configured) - ReDoc:
http://localhost:8000/redoc
Common Development Tasks¶
Running Tests¶
# All backend tests
make test-backend
# Unit tests only (no infrastructure required)
pytest tests/unit/ -v
# Integration tests (requires Redis, Postgres)
pytest tests/integration/ -v
# Auth module tests specifically
pytest tests/unit/modules/auth/ -v
# With coverage
pytest tests/unit/modules/auth/ --cov=app/modules/auth --cov-report=html
Seeding Test Data¶
Debugging Auth Issues¶
Enable debug logging:
Check Redis state:
docker-compose exec redis redis-cli
> KEYS preauth:*
> GET preauth:some-token
> KEYS refresh:*
> GET blacklist:some-jti
View active sessions:
docker-compose exec db psql -U mbpanel -d mbpanel
> SELECT * FROM active_sessions WHERE revoked_at IS NULL;
Adding a New Auth Endpoint¶
- Define schemas in
backend/app/modules/auth/schemas.py:
from pydantic import BaseModel
class MyAuthRequest(BaseModel):
some_field: str
class MyAuthResponse(BaseModel):
result: str
- Add business logic in
backend/app/modules/auth/service.py:
class AuthService:
async def my_auth_method(self, payload: MyAuthRequest) -> MyAuthResponse:
# Your logic here
return MyAuthResponse(result="success")
- Add route in
backend/app/modules/auth/router.py:
@router.post("/my-endpoint", response_model=MyAuthResponse)
async def my_endpoint(
payload: MyAuthRequest,
user: UserDep, # Requires authentication
service: AuthService = Depends(get_auth_service_dep),
):
return await service.my_auth_method(payload)
Adding a New Permission¶
- Seed the permission in a migration or seed script:
# In backend/app/infrastructure/database/models/rbac.py
PERMISSIONS = [
# ... existing permissions
Permission(slug="my.new.permission", description="..."),
]
-
Run migration/seed
-
Assign to roles via the API or admin interface
-
Protect endpoints using the permission:
from app.core.dependencies import require_permission
@router.post("/protected")
async def protected_endpoint(
_: None = Depends(require_permission("my.new.permission")),
# ...
):
# Only users with this permission can access
pass
Testing with Authentication¶
Using test user:
import pytest
from httpx import AsyncClient
@pytest.mark.asyncio
async def test_protected_endpoint(async_client: AsyncClient):
# First, login to get tokens
response = await async_client.post("/api/v1/auth/login", json={
"email": "test@example.com",
"password": "testpassword123", # pragma: allowlist secret
})
pre_auth_token = response.json()["pre_auth_token"]
# Exchange session
response = await async_client.post("/api/v1/auth/session-exchange", json={
"pre_auth_token": pre_auth_token,
"team_id": 1,
})
# Extract cookies
cookies = response.cookies
# Make authenticated request
response = await async_client.get(
"/api/v1/auth/me",
cookies=cookies
)
assert response.status_code == 200
File Locations¶
| Component | Location |
|---|---|
| JWT Token Logic | backend/app/core/jwt.py |
| Password Hashing | backend/app/core/security.py |
| Middleware | backend/app/core/middleware.py |
| Dependencies | backend/app/core/dependencies.py |
| Permissions | backend/app/core/permissions.py |
| Auth Service | backend/app/modules/auth/service.py |
| Auth Router | backend/app/modules/auth/router.py |
| Auth Schemas | backend/app/modules/auth/schemas.py |
| Auth Constants | backend/app/modules/auth/constants.py |
| Database Models | backend/app/infrastructure/database/models/ |
Useful Commands¶
# Check Redis for active tokens
docker-compose exec redis redis-cli KEYS "refresh:*"
# Check active sessions in database
docker-compose exec db psql -U mbpanel -d mbpanel -c "SELECT * FROM active_sessions;"
# View recent login activity
docker-compose exec db psql -U mbpanel -d mbpanel -c "SELECT * FROM login_activity ORDER BY created_at DESC LIMIT 10;"
# Manually blacklist a token
docker-compose exec redis redis-cli SETEX "blacklist:token-jti" 604800 "1"
# Clear all Redis data (dev only!)
docker-compose exec redis redis-cli FLUSHALL
Troubleshooting¶
| Issue | Solution |
|---|---|
| Token validation fails | Check JWT_SECRET_KEY matches between services |
| Redis connection errors | Verify REDIS_URL and that Redis is running |
| Permission denied | Check user has required permission via /api/v1/auth/me |
| Session expired | Refresh tokens have TTL; user must re-login |
| Tests fail with Redis errors | Ensure Redis is running: make up |
Next Steps¶
- API Reference - Complete endpoint documentation
- Auth Flows - Sequence diagrams for all flows
- Testing Guide - Testing patterns and strategies
- Troubleshooting - Common issues and solutions