feat(auth): add DB-backed login, token management, and idempotent auth migration

This commit is contained in:
2026-04-03 12:55:34 +00:00
parent 5168e52e4b
commit 06b1805b92
28 changed files with 1609 additions and 140 deletions
+59 -1
View File
@@ -72,10 +72,15 @@ Configuration precedence (high to low):
Core settings:
- `PORTFOLIOHUB_ALLOWED_ORIGINS`: comma-separated CORS origins
- `PORTFOLIOHUB_BOOTSTRAP_USERNAME`: initial username created on startup when users table is empty
- `PORTFOLIOHUB_BOOTSTRAP_EMAIL`: optional email for the bootstrap user
- `PORTFOLIOHUB_BOOTSTRAP_PASSWORD`: initial password for the bootstrap admin user
- `PORTFOLIOHUB_READ_TOKENS`: comma-separated read tokens
- `PORTFOLIOHUB_TRADE_WRITE_TOKENS`: comma-separated write tokens
- `PORTFOLIOHUB_ADMIN_TOKENS`: comma-separated admin tokens
- `PORTFOLIOHUB_DB_ENABLED`: enables DB repository selection when `true`
- `PORTFOLIOHUB_SESSION_TOKEN_TTL_HOURS`: login session token TTL in hours
- `PORTFOLIOHUB_CLIENT_TOKEN_DEFAULT_TTL_DAYS`: default client token TTL in days
Database settings (active source):
@@ -121,6 +126,7 @@ Failure responses:
Base routes:
- Health: `/health`
- Auth: `/auth`
- Portfolio: `/portfolios`
- Users: `/users`
@@ -247,7 +253,38 @@ Note: Since strategy sets are identified by (name, config) uniqueness, updating
- See `docs/api-examples.md` for maintained request/response examples.
### 7.5 User Endpoints
### 7.5 Auth Endpoints
#### Login
- `POST /auth/login`
- Auth: none
- Body: `LoginRequest`
- Response `200`: `TokenResponse`
- Response `401`: invalid username/password
#### List Current Tokens
- `GET /auth/tokens`
- Auth: `read` or higher with DB-issued token/session
- Response `200`: array of `AuthTokenResponse`
#### Create Client Token
- `POST /auth/tokens`
- Auth: `read` or higher with DB-issued token/session
- Body: `CreateClientTokenRequest`
- Response `201`: `IssuedClientTokenResponse`
- Response `403`: requested token permission exceeds current permission
#### Revoke Client Token
- `DELETE /auth/tokens/{token_id}`
- Auth: `read` or higher with DB-issued token/session
- Response `204`: no content
- Response `404`: token not found
### 7.6 User Endpoints
All user management endpoints are admin-only.
@@ -272,6 +309,10 @@ All user management endpoints are admin-only.
- Response `201`: `UserResponse`
- Response `409`: duplicate username or email
Bootstrap rule:
- If DB-backed persistence is enabled and both `PORTFOLIOHUB_BOOTSTRAP_USERNAME` and `PORTFOLIOHUB_BOOTSTRAP_PASSWORD` are configured, application startup creates the bootstrap admin user only when the `users` table is empty.
#### Update User
- `PATCH /users/{user_id}`
@@ -326,11 +367,28 @@ All user management endpoints are admin-only.
- `username: str`
- `email: str | null`
- `password: str`
- `permission: "read" | "trade-write" | "admin"`
- `is_active: bool`
`UpdateUserRequest`
- `username: str | null`
- `email: str | null`
- `password: str | null`
- `permission: "read" | "trade-write" | "admin" | null`
- `is_active: bool | null`
`LoginRequest`
- `username: str`
- `password: str`
`CreateClientTokenRequest`
- `name: str`
- `permission: "read" | "trade-write" | "admin" | null`
- `expires_in_days: int | null`
- `price: float > 0`
- `fee: float >= 0`
- `tax: float >= 0`