Update .env.example and README.md for passkey authentication

- .env.example: Add WEBAUTHN_RP_ID, WEBAUTHN_RP_NAME, WEBAUTHN_ORIGIN,
  ENVIRONMENT, and UMBRA_URL with documentation comments
- README.md: Full rewrite — remove outdated PIN/bcrypt references, document
  current auth stack (Argon2id + TOTP + passkeys), all 17 API route groups,
  security features, and Docker deployment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kyle 2026-03-17 23:25:27 +08:00
parent 9234880648
commit 57d400c6de
2 changed files with 85 additions and 148 deletions

View File

@ -1,2 +1,14 @@
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/umbra
SECRET_KEY=your-secret-key-change-in-production
ENVIRONMENT=development
# Public-facing URL (used for ntfy click links, CORS derivation)
UMBRA_URL=http://localhost
# WebAuthn / Passkey authentication
# RP_ID must be the eTLD+1 domain of the live site (e.g. umbra.ghost6.xyz)
# ORIGIN must include the scheme (https://)
# These defaults work for local development; override in production .env
WEBAUTHN_RP_ID=localhost
WEBAUTHN_RP_NAME=UMBRA
WEBAUTHN_ORIGIN=http://localhost

View File

@ -1,39 +1,37 @@
# UMBRA Backend
A complete FastAPI backend for the UMBRA application with async SQLAlchemy, PostgreSQL, authentication, and comprehensive CRUD operations.
FastAPI backend for the UMBRA life management application with async SQLAlchemy, PostgreSQL, multi-user RBAC, and comprehensive security.
## Features
- **FastAPI** with async/await support
- **SQLAlchemy 2.0** with async engine
- **PostgreSQL** with asyncpg driver
- **Alembic** for database migrations
- **bcrypt** for password hashing
- **itsdangerous** for session management
- **PIN-based authentication** with secure session cookies
- **Full CRUD operations** for all entities
- **Dashboard** with aggregated data
- **CORS enabled** for frontend integration
- **FastAPI** with async/await and Pydantic v2
- **SQLAlchemy 2.0** async engine with `Mapped[]` types
- **PostgreSQL 16** via asyncpg
- **Alembic** database migrations (001-061)
- **Authentication**: Argon2id passwords + signed httpOnly cookies + optional TOTP MFA + passkey (WebAuthn/FIDO2)
- **Multi-user RBAC**: admin/standard roles, per-user resource scoping
- **Session management**: DB-backed sessions, sliding window expiry, concurrent session cap
- **Account security**: Account lockout (10 failures = 30-min lock), CSRF protection, rate limiting
- **APScheduler** for background notification dispatch
## Project Structure
```
backend/
├── alembic/ # Database migrations
│ ├── versions/ # Migration files
│ ├── env.py # Alembic environment
│ └── script.py.mako # Migration template
├── alembic/versions/ # 61 database migrations
├── app/
│ ├── models/ # SQLAlchemy models
│ ├── schemas/ # Pydantic schemas
│ ├── routers/ # API route handlers
│ ├── config.py # Configuration
│ ├── database.py # Database setup
│ └── main.py # FastAPI application
├── requirements.txt # Python dependencies
├── Dockerfile # Docker configuration
├── alembic.ini # Alembic configuration
└── start.sh # Startup script
│ ├── models/ # 21 SQLAlchemy 2.0 models
│ ├── schemas/ # 14 Pydantic v2 schema modules
│ ├── routers/ # 17 API routers
│ ├── services/ # Auth, session, passkey, TOTP, audit, recurrence, etc.
│ ├── jobs/ # APScheduler notification dispatch
│ ├── config.py # Pydantic Settings (env vars)
│ ├── database.py # Async engine + session factory
│ └── main.py # FastAPI app + CSRF middleware
├── requirements.txt
├── Dockerfile
├── alembic.ini
└── start.sh
```
## Setup
@ -41,160 +39,87 @@ backend/
### 1. Install Dependencies
```bash
cd backend
pip install -r requirements.txt
```
### 2. Configure Environment
Create a `.env` file:
Copy `.env.example` to `.env` and configure:
```bash
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/umbra
SECRET_KEY=your-secret-key-change-in-production
DATABASE_URL=postgresql+asyncpg://postgres:postgres@db:5432/umbra
SECRET_KEY=generate-a-strong-random-key
ENVIRONMENT=production
# WebAuthn / Passkeys (required for passkey auth)
WEBAUTHN_RP_ID=your-domain.com
WEBAUTHN_RP_NAME=UMBRA
WEBAUTHN_ORIGIN=https://your-domain.com
```
### 3. Create Database
```bash
createdb umbra
```
### 4. Run Migrations
### 3. Run Migrations
```bash
alembic upgrade head
```
### 5. Start Server
### 4. Start Server
```bash
# Using the start script
chmod +x start.sh
./start.sh
# Or directly with uvicorn
uvicorn app.main:app --reload
uvicorn app.main:app --host 0.0.0.0 --port 8000
```
The API will be available at `http://localhost:8000`
## API Routes
## API Documentation
All routes require authentication (signed session cookie) except `/api/auth/*` and `/health`.
Interactive API documentation is available at:
- **Swagger UI**: http://localhost:8000/docs
- **ReDoc**: http://localhost:8000/redoc
| Prefix | Description |
|--------|-------------|
| `/api/auth` | Login, logout, register, setup, status, password, TOTP, passkeys |
| `/api/admin` | User management, system config, audit logs (admin only) |
| `/api/todos` | Task management with categories and priorities |
| `/api/events` | Calendar events with recurrence support |
| `/api/event-invitations` | Event invitation RSVP and management |
| `/api/event-templates` | Reusable event templates |
| `/api/calendars` | Calendar CRUD |
| `/api/shared-calendars` | Calendar sharing with permission levels |
| `/api/reminders` | Reminder management with snooze |
| `/api/projects` | Projects with tasks, comments, and collaboration |
| `/api/people` | Contact management |
| `/api/locations` | Location management |
| `/api/connections` | User connections (friend requests) |
| `/api/notifications` | In-app notification centre |
| `/api/settings` | User preferences and ntfy configuration |
| `/api/dashboard` | Aggregated dashboard data |
| `/api/weather` | Weather widget data |
## API Endpoints
## Authentication
### Authentication
- `POST /api/auth/setup` - Initial PIN setup
- `POST /api/auth/login` - Login with PIN
- `POST /api/auth/logout` - Logout
- `GET /api/auth/status` - Check auth status
UMBRA supports three authentication methods:
### Todos
- `GET /api/todos` - List todos (with filters)
- `POST /api/todos` - Create todo
- `GET /api/todos/{id}` - Get todo
- `PUT /api/todos/{id}` - Update todo
- `DELETE /api/todos/{id}` - Delete todo
- `PATCH /api/todos/{id}/toggle` - Toggle completion
1. **Password** (Argon2id) - Primary login method
2. **TOTP MFA** - Optional second factor via authenticator apps
3. **Passkeys** (WebAuthn/FIDO2) - Optional passwordless login via biometrics, security keys, or password managers
### Calendar Events
- `GET /api/events` - List events (with date range)
- `POST /api/events` - Create event
- `GET /api/events/{id}` - Get event
- `PUT /api/events/{id}` - Update event
- `DELETE /api/events/{id}` - Delete event
Passkey login bypasses TOTP (a passkey is inherently two-factor: possession + biometric/PIN).
### Reminders
- `GET /api/reminders` - List reminders (with filters)
- `POST /api/reminders` - Create reminder
- `GET /api/reminders/{id}` - Get reminder
- `PUT /api/reminders/{id}` - Update reminder
- `DELETE /api/reminders/{id}` - Delete reminder
- `PATCH /api/reminders/{id}/dismiss` - Dismiss reminder
## Security
### Projects
- `GET /api/projects` - List projects
- `POST /api/projects` - Create project
- `GET /api/projects/{id}` - Get project
- `PUT /api/projects/{id}` - Update project
- `DELETE /api/projects/{id}` - Delete project
- `GET /api/projects/{id}/tasks` - List project tasks
- `POST /api/projects/{id}/tasks` - Create project task
- `PUT /api/projects/{id}/tasks/{task_id}` - Update task
- `DELETE /api/projects/{id}/tasks/{task_id}` - Delete task
### People
- `GET /api/people` - List people (with search)
- `POST /api/people` - Create person
- `GET /api/people/{id}` - Get person
- `PUT /api/people/{id}` - Update person
- `DELETE /api/people/{id}` - Delete person
### Locations
- `GET /api/locations` - List locations (with category filter)
- `POST /api/locations` - Create location
- `GET /api/locations/{id}` - Get location
- `PUT /api/locations/{id}` - Update location
- `DELETE /api/locations/{id}` - Delete location
### Settings
- `GET /api/settings` - Get settings
- `PUT /api/settings` - Update settings
- `PUT /api/settings/pin` - Change PIN
### Dashboard
- `GET /api/dashboard` - Get dashboard data
- `GET /api/upcoming?days=7` - Get upcoming items
## Database Schema
The application uses the following tables:
- `settings` - Application settings and PIN
- `todos` - Task items
- `calendar_events` - Calendar events
- `reminders` - Reminders
- `projects` - Projects
- `project_tasks` - Tasks within projects
- `people` - Contacts/people
- `locations` - Physical locations
- CSRF protection via `X-Requested-With` header middleware
- All Pydantic schemas use `extra="forbid"` (mass-assignment prevention)
- Nginx rate limiting on auth, registration, and admin endpoints
- DB-backed account lockout after 10 failed attempts
- Timing-safe dummy hash for non-existent users (prevents enumeration)
- SSRF validation on ntfy webhook URLs
- Naive datetimes throughout (Docker runs UTC)
## Docker
Build and run with Docker:
The backend runs as non-root `appuser` in `python:3.12-slim`:
```bash
docker build -t umbra-backend .
docker run -p 8000:8000 -e DATABASE_URL=... -e SECRET_KEY=... umbra-backend
docker run -p 8000:8000 --env-file .env umbra-backend
```
## Development
### Create New Migration
```bash
alembic revision --autogenerate -m "Description of changes"
```
### Apply Migrations
```bash
alembic upgrade head
```
### Rollback Migration
```bash
alembic downgrade -1
```
## Security Notes
- Change `SECRET_KEY` in production
- Use strong PINs (minimum 4 digits recommended)
- Session cookies are httpOnly and last 30 days
- All API endpoints (except auth) require authentication
- PINs are hashed with bcrypt before storage
In production, use Docker Compose (see root `docker-compose.yaml`).