feat: implement user profile management and oauth/traefik integration

User Profile (US-004):
- Add authenticated profile endpoints (GET/PUT /users/me)
- Add avatar upload with file validation (PNG/JPEG, max 2MB)
- Create frontend profile page with edit form and avatar upload
- Update app shell to link to profile page

OAuth/Traefik Integration:
- Externalize all Authentik URLs to environment variables
- Add domain configuration (API_DOMAIN, WEB_DOMAIN, AUTHENTIK_DOMAIN)
- Create docker-compose.traefik.yml for reverse proxy deployment
- Update OAuth redirect/callback URLs to use configured domains
- Add VITE_APP_URL for frontend public URL configuration

Quality gates: pytest (50 passed), ruff, mypy, npm test (12 passed), typecheck, lint, build
This commit is contained in:
Fusion
2026-05-17 23:17:10 +02:00
parent 56f440db1b
commit 577b052c05
32 changed files with 1153 additions and 16 deletions
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-05-17
@@ -0,0 +1,65 @@
## Context
The current `docker-compose.yml` is a standalone development setup without reverse proxy support. The API has hardcoded Authentik URLs in `config.py` (`https://authentik.local/...`) which makes it impossible to deploy in real environments without code changes. The frontend also hardcodes `VITE_API_URL=http://localhost:8000`.
For production deployment, the platform needs to work behind an existing Traefik reverse proxy (common in self-hosted stacks) and have all external service endpoints fully configurable.
## Goals / Non-Goals
**Goals:**
- Make all Authentik URLs configurable via environment variables (no hardcoded defaults).
- Make OAuth redirect/callback URLs configurable and domain-aware.
- Create `docker-compose.traefik.yml` for deployment behind an existing Traefik instance.
- Support configuring the public web domain, API domain, and Authentik domain via env vars.
- Ensure both development (`docker-compose.yml`) and traefik modes work correctly.
**Non-Goals:**
- Setting up Traefik itself (assumes existing Traefik instance).
- Authentik installation/configuration (assumes existing Authentik instance).
- SSL certificate management (handled by Traefik).
- Changing the authentication flow or token logic.
## Decisions
1. **Remove all hardcoded URLs from `config.py` and require env vars**
- Rationale: Deployment environments have different domains. Hardcoded values are a deployment blocker.
- Alternative: Keep defaults and override in prod. Rejected because defaults mask configuration errors.
2. **Use `API_DOMAIN` and `WEB_DOMAIN` env vars for constructing public URLs**
- Rationale: Centralizes domain configuration and makes it easy to switch between dev/prod.
- `API_PUBLIC_URL` will default to `http://${API_DOMAIN}` or can be overridden.
- `WEB_PUBLIC_URL` will default to `http://${WEB_DOMAIN}` or can be overridden.
3. **Create a separate `docker-compose.traefik.yml` instead of modifying the existing one**
- Rationale: The existing `docker-compose.yml` is for standalone development. Traefik deployment is a different topology.
- Alternative: Use compose profiles or overrides. Rejected to keep each file simple and explicit.
4. **Add `VITE_APP_URL` for the frontend so it knows its public URL**
- Rationale: OAuth redirect URI needs to be absolute and must match the public web URL.
- Frontend will use this for login redirect if needed.
5. **Use Traefik labels for routing instead of ports**
- Rationale: Standard Traefik pattern - services are discovered via Docker labels.
- No port mappings exposed; Traefik handles all ingress.
## Risks / Trade-offs
- **[Missing env vars cause startup failures]** -> Document all required variables in `.env.example` and add validation in config.py.
- **[OAuth redirect URI mismatch]** -> Ensure the redirect URI configured in Authentik matches the env-configured callback URL exactly.
- **[Local development still works]** -> Keep `docker-compose.yml` unchanged for dev; traefik file is additive.
- **[Cookie secure flag]** -> Ensure `cookie_secure` property in config reads from env properly for HTTPS deployments.
## Migration Plan
1. Update `config.py` to read all Authentik URLs from environment with no defaults.
2. Update `auth.py` to construct redirect/callback URLs from env-configured domains.
3. Update `.env.example` with all new variables.
4. Create `docker-compose.traefik.yml` with Traefik labels.
5. Test that both `docker-compose.yml` (dev) and `docker-compose.traefik.yml` (prod) work.
Rollback:
- Revert config.py and auth.py changes; remove `docker-compose.traefik.yml`.
## Open Questions
- Should we add a startup health check that validates all required env vars are set?
@@ -0,0 +1,28 @@
## Why
The current setup hardcodes Authentik URLs in the API config and provides only a basic docker-compose.yml without reverse proxy support. For production deployment, the platform needs to integrate with an existing Traefik reverse proxy and have all external service URLs fully configurable via environment variables.
## What Changes
- **Externalize all Authentik and domain configuration** to environment variables (no more hardcoded URLs in config.py).
- **Add `docker-compose.traefik.yml`** for deployment behind an existing Traefik instance with all domain names as env vars.
- **Update `.env.example`** to document all new environment variables for both development and traefik modes.
- **Add proxy web name configuration** for the frontend to know its public URL.
- **Verify OAuth callback URLs work correctly** with configurable domains.
## Capabilities
### New Capabilities
- `traefik-deployment`: Docker Compose setup for deploying behind an existing Traefik reverse proxy with environment-based domain configuration.
### Modified Capabilities
- `docker-infrastructure`: Add traefik deployment mode and externalize all domain/service URLs.
- `auth-oauth`: Make Authentik URLs and callback URLs fully environment-configurable instead of hardcoded.
## Impact
- `apps/api/src/config.py`: Remove hardcoded Authentik URLs, read from environment.
- `apps/api/src/api/auth.py`: Use configurable redirect/callback URLs.
- `.env.example`: Add all new environment variables.
- `docker-compose.traefik.yml`: New file for traefik deployment.
- Frontend may need `VITE_APP_URL` or similar for OAuth redirect.
@@ -0,0 +1,29 @@
## MODIFIED Requirements
### Requirement: OAuth2/OIDC Flow
The system SHALL support OAuth2/OIDC authentication via Authentik with fully configurable endpoints.
#### Scenario: User login
- GIVEN a user clicks the login button
- WHEN the frontend redirects to Authentik authorization endpoint
- THEN the redirect URI SHALL be constructed from environment-configured domains
- AND the Authentik authorize URL SHALL be read from environment variables
#### Scenario: Token exchange and validation
- GIVEN Authentik has redirected with authorization code
- WHEN the callback endpoint receives the code
- THEN it exchanges the code for provider tokens at the configured token URL
- AND verifies token signature using the configured JWKS URL
- AND validates the issuer and audience from environment configuration
### Requirement: Session Security
The system SHALL protect sessions using httpOnly cookies with environment-aware secure defaults.
#### Scenario: Cookie attributes in production
- GIVEN successful authentication behind Traefik with HTTPS
- WHEN cookies are set
- THEN access_token cookie SHALL be httpOnly
- AND access_token cookie SHALL have Secure flag based on environment
- AND access_token cookie SHALL have SameSite based on environment
@@ -0,0 +1,24 @@
## MODIFIED Requirements
### Requirement: Docker Compose Setup
The system SHALL provide Docker Compose configurations for both development and traefik deployment.
#### Scenario: Development compose file
- GIVEN the development environment
- THEN `docker-compose.yml` SHALL define all platform services for local development
#### Scenario: Traefik compose file
- GIVEN the production deployment
- THEN `docker-compose.traefik.yml` SHALL define all platform services behind Traefik
- AND no ports SHALL be exposed directly (all traffic through Traefik)
### Requirement: Environment Configuration
The system SHALL document all required environment variables for both development and traefik deployment modes.
#### Scenario: Environment setup
- GIVEN a new developer or operator
- WHEN they set up the project
- THEN `.env.example` SHALL document all variables for both modes
- AND variables SHALL include domain configuration for traefik mode
@@ -0,0 +1,31 @@
## ADDED Requirements
### Requirement: Traefik Docker Compose
The system SHALL provide a `docker-compose.traefik.yml` for deployment behind an existing Traefik reverse proxy.
#### Scenario: Service labels
- GIVEN the traefik deployment configuration
- WHEN services are started
- THEN `docker-compose.traefik.yml` SHALL define Traefik Docker labels for each service
- AND all routing rules SHALL use configurable domain names
#### Scenario: Environment variables
- GIVEN the traefik deployment configuration
- WHEN configuring the deployment
- THEN all domain names SHALL be configurable via environment variables
- AND the proxy web name SHALL be configurable via environment variable
### Requirement: Environment Configuration
The system SHALL document all required environment variables for traefik deployment.
#### Scenario: Required variables
- GIVEN a new deployment
- WHEN setting up environment variables
- THEN `.env.example` SHALL document:
- `API_DOMAIN` - domain for API service
- `WEB_DOMAIN` - domain for web frontend
- `AUTHENTIK_DOMAIN` - domain for Authentik instance
- `PROXY_WEB_NAME` - name for web proxy service
- All Authentik OIDC configuration variables
@@ -0,0 +1,25 @@
## 1. Externalize Authentik and domain configuration
- [x] 1.1 Update `apps/api/src/config.py` to read all Authentik URLs from environment variables with no hardcoded defaults.
- [x] 1.2 Add `API_PUBLIC_URL`, `WEB_PUBLIC_URL`, and related domain env vars to config.py.
- [x] 1.3 Update `apps/api/src/api/auth.py` to construct OAuth redirect/callback URLs from configured domains.
- [x] 1.4 Update `.env.example` with all new environment variables for Authentik and domain configuration.
## 2. Create Traefik deployment compose file
- [x] 2.1 Create `docker-compose.traefik.yml` with all services configured for Traefik reverse proxy.
- [x] 2.2 Add Traefik Docker labels to all services with configurable domain-based routing rules.
- [x] 2.3 Ensure no ports are exposed directly in traefik mode (all through Traefik).
- [x] 2.4 Add `PROXY_WEB_NAME` and other traefik-specific env vars to `.env.example`.
## 3. Frontend configuration
- [x] 3.1 Update frontend to support configurable public URL for OAuth redirect.
- [x] 3.2 Update `apps/web/.env.example` or relevant config with `VITE_APP_URL`.
## 4. Verification and testing
- [x] 4.1 Run backend quality gates (`pytest`, `ruff`, `mypy`).
- [x] 4.2 Run frontend quality gates (`npm test`, `typecheck`, `lint`, `build`).
- [x] 4.3 Validate `docker-compose config` works for both compose files.
- [x] 4.4 Update this tasks file with completed checkboxes.