360 lines
8.5 KiB
Markdown
360 lines
8.5 KiB
Markdown
# Authy2 Backend - Authentication & Authorization API
|
|
|
|
Production-ready Flask/SQLAlchemy API for authentication and authorization services.
|
|
|
|
## Features
|
|
|
|
- 🔐 **Multi-method Authentication**: Password, OAuth (Google, GitHub, Microsoft), SAML, OIDC
|
|
- 👥 **Multi-tenancy**: Organization-based access control with roles
|
|
- 🔑 **Session Management**: Secure session handling with Redis
|
|
- 📝 **Audit Logging**: Comprehensive activity tracking
|
|
- 🛡️ **Security**: Bcrypt password hashing, CORS, security headers, rate limiting
|
|
- 📊 **API Response Envelope**: Consistent response format across all endpoints
|
|
- ✅ **Validation**: Marshmallow schemas for request/response validation
|
|
- 🧪 **Testing**: Comprehensive unit and integration tests
|
|
- 📚 **Documentation**: OpenAPI/Swagger compatible
|
|
|
|
## Tech Stack
|
|
|
|
- **Framework**: Flask 3.0
|
|
- **Database**: PostgreSQL with SQLAlchemy ORM
|
|
- **Caching/Sessions**: Redis
|
|
- **Validation**: Marshmallow
|
|
- **Testing**: Pytest
|
|
- **Security**: Flask-Bcrypt, Flask-CORS
|
|
- **Migration**: Flask-Migrate (Alembic)
|
|
|
|
## Quick Start
|
|
|
|
### Prerequisites
|
|
|
|
- Python 3.11+
|
|
- PostgreSQL 14+
|
|
- Redis 6+
|
|
|
|
### Installation
|
|
|
|
1. **Clone the repository**:
|
|
```bash
|
|
git clone <repository-url>
|
|
cd authy2/backend
|
|
```
|
|
|
|
2. **Create virtual environment**:
|
|
```bash
|
|
python -m venv venv
|
|
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
```
|
|
|
|
3. **Install dependencies**:
|
|
```bash
|
|
pip install -r requirements/development.txt
|
|
```
|
|
|
|
4. **Set up environment variables**:
|
|
```bash
|
|
cp .env.example .env
|
|
# Edit .env with your configuration
|
|
```
|
|
|
|
5. **Initialize database**:
|
|
```bash
|
|
python scripts/init_db.py
|
|
```
|
|
|
|
6. **Seed sample data** (optional):
|
|
```bash
|
|
python -m scripts.seed_data
|
|
```
|
|
|
|
7. **Run the application**:
|
|
```bash
|
|
flask run
|
|
# Or in debug mode
|
|
FLASK_ENV=development flask run --debug --port 5000
|
|
|
|
# Or using the WSGI file
|
|
python wsgi.py
|
|
|
|
```
|
|
|
|
The API will be available at `http://localhost:5000`
|
|
|
|
|
|
## Docker Deployment
|
|
|
|
### Prerequisites
|
|
- Docker 20.10+
|
|
- Docker Compose 2.0+
|
|
|
|
### Quick Start
|
|
|
|
1. **Start all services**:
|
|
```bash
|
|
docker-compose up -d
|
|
```
|
|
|
|
2. **Initialize the database** (run migrations):
|
|
```bash
|
|
docker-compose exec api python manage.py db upgrade
|
|
```
|
|
|
|
3. **Seed sample data** (optional):
|
|
```bash
|
|
docker-compose exec api python scripts/seed_data.py
|
|
```
|
|
|
|
4. **Verify health**:
|
|
```bash
|
|
curl http://localhost:5000/api/health
|
|
```
|
|
|
|
### Useful Commands
|
|
|
|
```bash
|
|
# View logs
|
|
docker-compose logs -f api
|
|
|
|
# Run migrations
|
|
docker-compose exec api python manage.py db upgrade
|
|
|
|
# Open shell in container
|
|
docker-compose exec api /bin/bash
|
|
|
|
# Rebuild after changes
|
|
docker-compose up -d --build
|
|
|
|
# Stop all services
|
|
docker-compose down
|
|
```
|
|
|
|
### Environment Variables
|
|
|
|
Copy `.env.example` to `.env` and configure:
|
|
- `POSTGRES_USER` / `POSTGRES_PASSWORD` - Database credentials
|
|
- `SECRET_KEY` - Flask secret key (required in production)
|
|
- `ENCRYPTION_KEY` - Data encryption key
|
|
- `CA_ENCRYPTION_KEY` - CA private key encryption
|
|
- `CORS_ORIGINS` - Allowed CORS origins (comma-separated)
|
|
|
|
### Production Considerations
|
|
|
|
- Use a strong `SECRET_KEY` (256-bit random)
|
|
- Enable HTTPS via nginx (configure SSL certificates)
|
|
- Set `BCRYPT_LOG_ROUNDS=13` for stronger password hashing
|
|
- Use Redis persistence (`--appendonly yes`)
|
|
- Configure log aggregation as needed
|
|
|
|
|
|
## API Endpoints
|
|
|
|
### Authentication
|
|
- `POST /api/v1/auth/register` - Register new user
|
|
- `POST /api/v1/auth/login` - Login
|
|
- `POST /api/v1/auth/logout` - Logout
|
|
- `GET /api/v1/auth/me` - Get current user
|
|
- `GET /api/v1/auth/sessions` - Get user sessions
|
|
- `POST /api/v1/auth/sessions/refresh` - Extend session idle window
|
|
- `DELETE /api/v1/auth/sessions/:id` - Revoke session
|
|
|
|
### Users
|
|
- `GET /api/v1/users/me` - Get current user profile
|
|
- `PATCH /api/v1/users/me` - Update profile
|
|
- `DELETE /api/v1/users/me` - Delete account
|
|
- `POST /api/v1/users/me/password` - Change password
|
|
- `GET /api/v1/users/me/organizations` - Get user organizations
|
|
|
|
### Organizations
|
|
- `POST /api/v1/organizations` - Create organization
|
|
- `GET /api/v1/organizations/:id` - Get organization
|
|
- `PATCH /api/v1/organizations/:id` - Update organization
|
|
- `DELETE /api/v1/organizations/:id` - Delete organization
|
|
- `GET /api/v1/organizations/:id/members` - Get members
|
|
- `POST /api/v1/organizations/:id/members` - Add member
|
|
- `DELETE /api/v1/organizations/:id/members/:userId` - Remove member
|
|
- `PATCH /api/v1/organizations/:id/members/:userId/role` - Update role
|
|
|
|
|
|
### Contact (Public — No Auth Required)
|
|
- `POST /api/v1/contact` - Submit a contact enquiry (demo request, sales enquiry, general, or support). Rate limited to 5 requests per IP per hour. Sends an email to info@secuird.tech.
|
|
|
|
### Health
|
|
- `GET /api/health` - Health check
|
|
|
|
|
|
## O-auth Setup
|
|
|
|
- Redirect URI
|
|
|
|
```http://localhost:5000/api/v1/auth/external/[google|microsoft]/callback```
|
|
|
|
|
|
## API Response Format
|
|
|
|
All API responses follow the standardized envelope format:
|
|
|
|
```json
|
|
{
|
|
"version": "1.0",
|
|
"success": true,
|
|
"code": 200,
|
|
"message": "Success message",
|
|
"request_id": "uuid-v4",
|
|
"data": {},
|
|
"meta": {}
|
|
}
|
|
```
|
|
|
|
Error responses:
|
|
|
|
```json
|
|
{
|
|
"version": "1.0",
|
|
"success": false,
|
|
"code": 400,
|
|
"message": "Error message",
|
|
"request_id": "uuid-v4",
|
|
"error": {
|
|
"type": "VALIDATION_ERROR",
|
|
"details": {}
|
|
}
|
|
}
|
|
```
|
|
|
|
## Database Migrations
|
|
|
|
Create a new migration:
|
|
```bash
|
|
flask db migrate -m "Description of changes"
|
|
```
|
|
|
|
Apply migrations:
|
|
```bash
|
|
flask db upgrade
|
|
```
|
|
|
|
Rollback:
|
|
```bash
|
|
flask db downgrade
|
|
```
|
|
|
|
### Environment Configuration
|
|
|
|
- **Development**: `FLASK_ENV=development`
|
|
- **Testing**: `FLASK_ENV=testing`
|
|
- **Production**: `FLASK_ENV=production`
|
|
|
|
## Production Deployment
|
|
|
|
### Using Gunicorn
|
|
|
|
```bash
|
|
pip install -r requirements/production.txt
|
|
gunicorn -w 4 -b 0.0.0.0:8000 wsgi:app
|
|
```
|
|
|
|
|
|
## Security Considerations
|
|
|
|
- All passwords hashed with Bcrypt (12+ rounds in production)
|
|
- CORS configured for allowed origins
|
|
- Security headers enabled (CSP, HSTS, etc.)
|
|
- Rate limiting on sensitive endpoints
|
|
- SQL injection protection via SQLAlchemy ORM
|
|
- Session management with secure cookies
|
|
- Request ID tracking for audit trails
|
|
|
|
|
|
## Session Management
|
|
|
|
Sessions are database-backed bearer tokens stored in PostgreSQL. Each session is created at login and validated on every authenticated request via the `login_required` decorator.
|
|
|
|
### Sliding Timeout
|
|
|
|
Sessions use a **sliding window** model with two independent limits:
|
|
|
|
| Timeout | Default | Env Var | Behaviour |
|
|
|---------|---------|---------|-----------|
|
|
| **Idle** | 15 min | `SESSION_IDLE_TIMEOUT` | Extends automatically on every request. If no request is made within this window the session expires. |
|
|
| **Absolute** | 8 h | `SESSION_ABSOLUTE_TIMEOUT` | Hard cap measured from session creation. Activity cannot extend a session beyond this point. |
|
|
|
|
Every authenticated request resets the idle clock by calling `Session.refresh()`, which sets `expires_at = now + idle_timeout` — but never past `created_at + absolute_timeout`. This means:
|
|
|
|
- An active user stays logged in indefinitely **up to** the absolute cap.
|
|
- An idle user is logged out after the idle timeout.
|
|
- No session can survive longer than the absolute timeout regardless of activity.
|
|
|
|
### Configuration
|
|
|
|
Override defaults via environment variables:
|
|
|
|
```bash
|
|
SESSION_IDLE_TIMEOUT=900 # seconds (15 min)
|
|
SESSION_ABSOLUTE_TIMEOUT=28800 # seconds (8 h)
|
|
```
|
|
|
|
### Cleanup
|
|
|
|
Expired sessions are soft-marked as `EXPIRED` by the `cleanup_sessions` job. Run it periodically via the job runner:
|
|
|
|
```bash
|
|
python manage.py cleanup_sessions
|
|
|
|
# Or via the job runner (Docker):
|
|
JOB_NAME=cleanup_sessions JOB_INTERVAL_SECONDS=300
|
|
```
|
|
|
|
### Session Endpoints
|
|
|
|
- `GET /api/v1/auth/sessions` — List active sessions for the current user
|
|
- `POST /api/v1/auth/sessions/refresh` — Extend the current session's idle window (returns new `expires_at`)
|
|
- `DELETE /api/v1/auth/sessions/:id` — Revoke a specific session
|
|
|
|
|
|
# Boostrap db
|
|
python manage.py db upgrade
|
|
|
|
|
|
|
|
## Development Commands
|
|
|
|
### Run Flask in Development
|
|
```bash
|
|
FLASK_ENV=development flask run --debug --port 8888
|
|
```
|
|
|
|
### Seed Sample Data
|
|
```bash
|
|
python -m scripts.seed_data
|
|
# Or with Docker:
|
|
docker-compose exec api python scripts/seed_data.py
|
|
```
|
|
|
|
### Database Migration
|
|
```bash
|
|
# Apply migrations
|
|
flask db upgrade
|
|
|
|
# With Docker:
|
|
docker-compose exec api python manage.py db upgrade
|
|
```
|
|
|
|
### SQLite Browser (Development)
|
|
```bash
|
|
sqlite_web instance/db_file.db --port 9999 --host 0.0.0.0
|
|
```
|
|
|
|
|
|
## Test Credentials
|
|
|
|
### OIDC Client
|
|
| Field | Value |
|
|
|-------|-------|
|
|
| client_id | `acme-portal-001` |
|
|
| client_secret | `acme_secret_portal_2024` |
|
|
|
|
### Test User
|
|
| Field | Value |
|
|
|-------|-------|
|
|
| email | `bob@acme-corp.com` |
|
|
| password | `UserPass123!` | |