Analyzes all Taiga stories, maps hard/soft dependencies, and writes full specs (Goal, Data Model, Components, Verification, Open Questions) for every story before any implementation begins. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.6 KiB
4.6 KiB
Spec: 08 · Authorize User
Taiga Story: #8 · 08-authorize-user
Status: Ready
Branch: feature/08-authorize-user
Depends on: 01-Bootstrap (needs full feature set to protect; implement last)
1. Goal
Protect all data-modifying API endpoints behind login. Hide CRUD controls and admin nav links for unauthenticated visitors. Simple .env-based user/password map; password hashed with username as salt.
2. Background / Scope / Context / Constraints
- No OAuth, LDAP, or DB-stored users —
.envfile mapsusername → hashed_password. - Password hashed with bcrypt where the username is used as the pepper/salt input alongside the password (e.g. hash
username:passwordtogether or prepend username to password before hashing). - Helper script
scripts/create_user.sh(orscripts/create_user.py) generates the bcrypt entry. - Session: JWT token (short-lived, e.g. 1 h) stored in
localStorageorhttpOnlycookie. - Frontend reads auth state from Pinia and conditionally renders CRUD buttons + admin link.
- Read-only endpoints (
GET *) remain public. - Write endpoints (
POST,PUT,DELETE) require valid JWT. - Admin backfill endpoint from story #7 must also be protected.
3. Necessary Refactorings
- Echo middleware: JWT auth middleware applied to all non-GET routes.
- All existing CRUD handlers continue to work unchanged — middleware handles auth, not handlers.
FruitList.vue,FruitDetail.vue,PublicationList.vue,PublicationDetail.vue: conditionally show/hide edit, delete, upload, create buttons.- Nav/header: hide "Administration" link if not logged in.
4. Related Data Model
No DB changes. Auth is stateless (JWT) backed by .env.
.env addition:
USERS_MAP=admin:$2a$12$...<bcrypt_hash>,...
Or a separate file users.env:
# username:bcrypt_hash
admin:$2a$12$abc...
julia:$2a$12$xyz...
5. Affected Places / Related Components
Backend
| File | Purpose |
|---|---|
backend/internal/auth/auth.go |
LoadUsers(path), VerifyPassword(user, pass), GenerateJWT(), ValidateJWT() |
backend/internal/middleware/jwt_auth.go |
Echo middleware — checks Authorization: Bearer <token> header |
backend/cmd/server/router.go |
Apply middleware to write-route groups |
backend/internal/handler/auth_handler.go |
POST /api/v1/auth/login → returns JWT |
scripts/create_user.sh (or .py) |
./create_user.sh <username> <password> → prints .env line with bcrypt hash |
.env.example |
Add USERS_FILE=./users.env |
API Endpoints
| Method | Path | Auth required |
|---|---|---|
| POST | /api/v1/auth/login |
No |
| GET | /api/v1/fruits |
No |
| GET | /api/v1/fruits/:id |
No |
| POST/PUT/DELETE | /api/v1/fruits/* |
Yes |
| POST/PUT/DELETE | /api/v1/publications/* |
Yes |
| POST | /api/v1/admin/* |
Yes |
Frontend
| File | Change |
|---|---|
frontend/src/stores/authStore.ts |
Pinia: isLoggedIn, token, login(), logout() |
frontend/src/api/auth.ts |
POST /api/v1/auth/login |
frontend/src/views/LoginView.vue |
Login form |
frontend/src/router/index.ts |
Admin route guard (redirect if not logged in) |
| All CRUD views | Wrap buttons with v-if="authStore.isLoggedIn" |
frontend/src/components/NavBar.vue |
Conditionally show admin link + login/logout button |
6. Out of Scope
- Role-based access control (admin vs. read-only)
- Password reset / forgot password flow
- Session revocation / token blacklist
- Multiple auth backends (OAuth, SSO)
- User management UI
7. Verification
| Check | Expected |
|---|---|
POST /api/v1/fruits without token |
401 Unauthorized |
POST /api/v1/auth/login with valid creds |
200 + JWT in response |
POST /api/v1/fruits with valid JWT |
201 Created |
| Expired JWT | 401 |
GET /api/v1/fruits without token |
200 (public) |
| Frontend: not logged in → no CRUD buttons visible | Pass |
| Frontend: logged in → CRUD buttons visible | Pass |
| Frontend: not logged in → admin nav link hidden | Pass |
create_user.sh admin secret → entry verifiable with bcrypt |
Pass |
go test ./... auth middleware tests |
Green |
8. Open Questions
| # | Question | Impact |
|---|---|---|
| 1 | JWT stored in localStorage (simpler) or httpOnly cookie (more secure)? |
XSS risk vs complexity |
| 2 | Token expiry: 1h with no refresh, or sliding expiry? | UX |
| 3 | Username used as bcrypt cost salt or prepended to password string? | Auth security |
| 4 | Should login endpoint be rate-limited? | Brute-force protection |
| 5 | Is users.env committed to git or only .env.example? |
Secrets management |