Files
osdb-1-claude-opusplan/docs/planning/specs/08-authorize-user.md
T
juliaandClaude Sonnet 4.6 1070ba3ac1 docs: add sprint planning — dependency diagram + specs for all 8 stories
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>
2026-06-16 11:53:24 +02:00

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 — .env file maps username → hashed_password.
  • Password hashed with bcrypt where the username is used as the pepper/salt input alongside the password (e.g. hash username:password together or prepend username to password before hashing).
  • Helper script scripts/create_user.sh (or scripts/create_user.py) generates the bcrypt entry.
  • Session: JWT token (short-lived, e.g. 1 h) stored in localStorage or httpOnly cookie.
  • 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.

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...

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