Files
dance-lessons-coach/adr/0018-user-management-auth-system.md
Gabriel Radureau a24b4fdb3b 📝 docs(adr): homogenize 23 ADRs + rewrite README (Tâche 7 migration) (#18)
## Summary

Homogenize all 23 ADRs to a single canonical header format, and rewrite `adr/README.md` to match the actual state of the corpus.

This is **Tâche 7** of the ARCODANGE Phase 1 migration (Claude Code → Mistral Vibe). Independent from PR #17 (Tâche 6 — restructure AGENTS.md) — both can merge in any order. No code changes; only documentation.

## Changes

### 1. Homogenize 21 ADR headers (commit `db09d0a`)

The audit (Tâche 6 Phase A, Mistral intent-router agent, 2026-05-02) had identified **3 inconsistent header formats** :

- **F1** — list bullets (`* Status:` / `* Date:` / `* Deciders:`) : 11 ADRs (0001-0008, 0011, 0014, 0023)
- **F2** — bold fields (`**Status:**` / `**Date:**` / `**Authors:**`) : 9 ADRs (0009, 0010, 0012, 0013, 0015, 0016, 0017, 0018, 0019)
- **F3** — dedicated section (`## Status\n**Value** `) : 5 ADRs (0020, 0021, 0022, 0024, 0025)

Plus mixed metadata names (Authors / Deciders / Decision Date / Implementation Date / Implementation Status / Last Updated) and decorative emojis on status values made the corpus hard to scan or template against.

**Canonical format adopted** (see `adr/README.md` for full template) :

```markdown
# NN. Title

**Status:** <Proposed | Accepted | Implemented | Partially Implemented | Approved | Rejected | Deferred | Deprecated | Superseded by ADR-NNNN>
**Date:** YYYY-MM-DD
**Authors:** Name(s)

[optional **Field:** ... lines]

## Context...
```

**Transformations applied** (via `/tmp/homogenize-adrs.py` script, 23 files scanned, 21 modified — 0010 and 0012 were already conform) :

- F1 list bullets → bold fields
- F2 cleanup : `**Deciders:**` → `**Authors:**`, strip status emojis
- F3 sections : `## Status\n**Value** ` → `**Status:** Value` (single line)
- Strip decorative emojis from `**Status:**` and `**Implementation Status:**`
- Convert `* Last Updated:` / `* Implementation Status:` / `* Decision Drivers:` / `* Decision Date:` to bold
- Date typo fix : `2024-04-XX` → `2026-04-XX` for ADRs 0018, 0019 (off-by-2-years in original)
- Normalize multiple blank lines after header (max 1)

**ADR body content is preserved unchanged.** Only headers transformed.

### 2. Rewrite `adr/README.md` (commit `d64ab02`)

Previous README had multiple inconsistencies :

- Index table listed wrong titles for ADRs 0010-0021 (looked like an aspirational forecast that never matched reality — e.g. "0011 = Trunk-Based Development" but real 0011 is absent and Trunk-Based Development is actually 0017)
- Listed entries for ADRs 0011 (validation library) and 0014 (gRPC) but **these files do not exist** in the repo
- 0024 (BDD Test Organization) was missing from the detail list
- Template still showed the obsolete F1 format (`* Status:`)
- Decorative emojis on every status entry

Rewrite :

- Index table **regenerated from actual file contents** (title from H1, status from `**Status:**` line) — emoji-free, accurate
- Notes that 0011 / 0014 are not currently in use (reserved)
- Updated template block matches the canonical format
- Status Legend extended with `Approved`, `Partially Implemented`, `Deferred`
- Added note that 0026 is the next free number for new ADRs

## Test plan

- [x] All 23 ADRs follow `**Status:**` / `**Date:**` / `**Authors:**` (verified via grep)
- [x] No more occurrences of `* Status:` (F1) or `## Status` (F3) in any ADR header
- [x] No more emojis on `**Status:**` lines
- [x] `adr/README.md` index links resolve to existing files (no more 0011 / 0014 dead links)
- [x] Pre-commit hooks pass (`go mod tidy`, `go fmt`, `swag fmt`)

## Migration context

Part of Phase 1 of the ARCODANGE migration from Claude Code to Mistral Vibe. Tâche 7 of the curriculum.

Independent from PR #17 (which restructures `AGENTS.md`). The two PRs touch disjoint files — no merge conflict expected when both are merged.

🤖 Generated with [Claude Code](https://claude.com/claude-code) (Opus 4.7, 1M context). Mistral Vibe (intent-router agent / mistral-medium-3.5) did the original audit identifying the 3 formats during Tâche 6 Phase A.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Mistral Vibe (devstral-2 / mistral-medium-3.5)
Reviewed-on: #18
Co-authored-by: Gabriel Radureau <arcodange@gmail.com>
Co-committed-by: Gabriel Radureau <arcodange@gmail.com>
2026-05-03 11:01:13 +02:00

16 KiB

18. User Management and Authentication System

Date: 2026-04-06 Status: Proposed Authors: Product Owner Decision Drivers: Security, User Personalization, Admin Functionality

Context

The dance-lessons-coach application currently lacks user management and authentication capabilities. To provide personalized experiences and administrative functions, we need to implement a secure user authentication system with PostgreSQL persistence.

Decision

We will implement a user management and authentication system with the following characteristics:

Core Features

  1. User Model

    • Username-based identification (no email/personal info)
    • Password authentication with secure hashing
    • User profile fields: description and current goal
    • Admin flag for privileged users
  2. Authentication System

    • JWT-based authentication
    • Secure password hashing (bcrypt)
    • Session management
    • Admin master password for non-persisted admin access
  3. Password Reset Workflow

    • Admin-assisted password reset (no email/phone required)
    • Allow password reset flag for users
    • Unauthenticated password reset endpoint for flagged users
  4. Integration Points

    • Greet service personalization based on authenticated user
    • Admin-only endpoints for user management
    • BDD test coverage for all authentication flows
    • CI/CD pipeline updates for new dependencies

Technical Implementation

Database Schema (PostgreSQL)

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    created_at TIMESTAMP WITH TIME ZONE,
    updated_at TIMESTAMP WITH TIME ZONE,
    deleted_at TIMESTAMP WITH TIME ZONE,
    username VARCHAR(50) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    description TEXT,
    current_goal TEXT,
    is_admin BOOLEAN DEFAULT FALSE,
    allow_password_reset BOOLEAN DEFAULT FALSE,
    last_login TIMESTAMP WITH TIME ZONE
);

Technology Stack

  • ORM: GORM (for PostgreSQL integration) - aligns with existing interface-based design
  • Authentication: JWT with HS256 signing - stateless, scalable
  • Password Hashing: bcrypt - industry standard for password storage
  • Validation: go-playground/validator - consistent with existing validation approach
  • Database: PostgreSQL (containerized) - production-ready database
  • Configuration: Viper integration - consistent with existing config management
  • Logging: Zerolog integration - consistent with existing logging approach
  • Telemetry: OpenTelemetry support - consistent with existing observability

Architecture Alignment

The user management system follows the established dance-lessons-coach patterns:

  1. Interface-based Design:

    type UserRepository interface {
        CreateUser(user *User) error
        GetUserByUsername(username string) (*User, error)
        // ... other methods
    }
    
    type AuthService interface {
        Authenticate(username, password string) (*User, error)
        GenerateJWT(user *User) (string, error)
        ValidateJWT(token string) (*User, error)
    }
    
  2. Context-aware Services:

    func (s *AuthService) Authenticate(ctx context.Context, username, password string) (*User, error) {
        log.Trace().Ctx(ctx).Str("username", username).Msg("Authenticating user")
        // ... authentication logic
    }
    
  3. Configuration Integration:

    type AuthConfig struct {
        JWTSecret       string        `mapstructure:"jwt_secret"`
        JWTExpiration   time.Duration `mapstructure:"jwt_expiration"`
        AdminUsername   string        `mapstructure:"admin_username"`
        AdminMasterPassword string    `mapstructure:"admin_master_password"`
    }
    
  4. Chi Router Integration:

    func (h *AuthHandler) RegisterRoutes(router chi.Router) {
        router.Post("/register", h.handleRegister)
        router.Post("/login", h.handleLogin)
        // ... other routes
    }
    

Security Considerations

  1. Password Storage: bcrypt with work factor 12
  2. JWT Security:
    • 30-minute expiration for access tokens
    • Secure random signing key
    • HTTPS-only cookies
    • Secret Rotation: Multiple valid secrets with retention policy (see Issue #8)
  3. Admin Access:
    • Master password from environment variable
    • Non-persisted admin user
    • Full access to user management endpoints
    • Password Reset Control: Only admins can flag users for reset
  4. Password Reset Security:
    • Admin-Only Flagging: Only authenticated admins can set allow_password_reset flag
    • Flag-Based Access: Unauthenticated reset only works for admin-flagged users
    • Automatic Flag Clearing: Flag is cleared after successful password reset
    • No Self-Service: Users cannot flag themselves or others for reset
  5. Rate Limiting: On authentication endpoints (3 attempts/hour for password reset)
  6. Input Validation: Strict username validation (alphanumeric, 3-50 chars)

API Endpoints

Public Endpoints

  • POST /api/v1/auth/register - User registration
  • POST /api/v1/auth/login - User login
  • POST /api/v1/auth/reset-password - Password reset (for flagged users)

Authenticated Endpoints

  • GET /api/v1/users/me - Get current user profile
  • PUT /api/v1/users/me - Update current user profile
  • PUT /api/v1/users/me/password - Change own password

Admin-Only Endpoints

  • GET /api/v1/admin/users - List all users
  • POST /api/v1/admin/users/{username}/allow-reset - Allow password reset
  • DELETE /api/v1/admin/users/{username} - Delete user

Integration with Existing Services

Greet Service Enhancement

The authentication system integrates seamlessly with the existing greet service by leveraging Go's context package:

// Updated greet service with authentication support
func (s *Service) Greet(ctx context.Context, name string) string {
    log.Trace().Ctx(ctx).Str("name", name).Msg("Greet function called")
    
    // Extract authenticated username from context using existing auth package
    if username := auth.GetUsernameFromContext(ctx); username != "" {
        log.Trace().Ctx(ctx).Str("authenticated_user", username).Msg("Using authenticated username")
        return "Hello " + username + "!"
    }
    
    // Fallback to original behavior for backward compatibility
    if name == "" {
        return "Hello world!"
    }
    return "Hello " + name + "!"
}

Authentication Middleware

The system includes Chi middleware for JWT validation:

func (s *AuthService) Middleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ctx := r.Context()
        
        // Extract and validate JWT token
        token := auth.ExtractTokenFromRequest(r)
        if token == "" {
            next.ServeHTTP(w, r)
            return // Continue without authentication
        }
        
        // Validate token and extract user
        if user, err := s.ValidateJWT(ctx, token); err == nil {
            // Add user to context for downstream services
            ctx = auth.SetUserInContext(ctx, user)
            r = r.WithContext(ctx)
        }
        
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

Server Integration

The authentication middleware is added to the Chi router stack:

// In server.go
func (s *Server) getAllMiddlewares() []func(http.Handler) http.Handler {
    middlewares := []func(http.Handler) http.Handler{
        middleware.StripSlashes,
        middleware.Recoverer,
        s.authService.Middleware, // Add auth middleware
    }
    
    if s.withOTEL {
        middlewares = append(middlewares, func(next http.Handler) http.Handler {
            return otelhttp.NewHandler(next, "")
        })
    }
    
    return middlewares
}

Configuration Extension

The existing Config struct is extended with authentication settings:

// In config.go
type AuthConfig struct {
    JWTSecret        string        `mapstructure:"jwt_secret"`
    JWTExpiration    time.Duration `mapstructure:"jwt_expiration"`
    AdminUsername    string        `mapstructure:"admin_username"`
    AdminMasterPassword string     `mapstructure:"admin_master_password"`
}

type DatabaseConfig struct {
    Host     string `mapstructure:"host"`
    Port     int    `mapstructure:"port"`
    User     string `mapstructure:"user"`
    Password string `mapstructure:"password"`
    Name     string `mapstructure:"name"`
    SSLMode  string `mapstructure:"ssl_mode"`
}

// Extended Config struct
type Config struct {
    Server    ServerConfig    `mapstructure:"server"`
    Shutdown  ShutdownConfig  `mapstructure:"shutdown"`
    Logging   LoggingConfig   `mapstructure:"logging"`
    Telemetry TelemetryConfig `mapstructure:"telemetry"`
    API       APIConfig       `mapstructure:"api"`
    Auth      AuthConfig      `mapstructure:"auth"`
    Database  DatabaseConfig  `mapstructure:"database"`
}

Consequences

Positive

  1. Personalized Experience: Users see their username in greetings
  2. Admin Capabilities: Secure administrative functions
  3. Password Recovery: Admin-assisted workflow without contact info
  4. Security: Proper authentication and authorization
  5. Extensibility: Foundation for future user-based features

Negative

  1. Increased Complexity: Additional dependencies and configuration
  2. Database Requirement: PostgreSQL container needed for development
  3. Migration Complexity: Existing tests need updates
  4. CI/CD Changes: Pipeline needs adjustment for new dependencies
  5. Performance Impact: Authentication middleware adds overhead

Neutral

  1. Learning Curve: Team needs to learn GORM and JWT
  2. Testing Overhead: Additional BDD scenarios required
  3. Documentation Needs: Comprehensive API documentation required

Alternatives Considered

Alternative 1: No Authentication

  • Pros: Simpler, no database needed
  • Cons: No personalization, no admin functions
  • Rejected: Doesn't meet product requirements

Alternative 2: Session-based Auth

  • Pros: Traditional approach, well-understood
  • Cons: State management complexity, scaling issues
  • Rejected: JWT provides better scalability

Alternative 3: OAuth/OIDC

  • Pros: Industry standard, delegation to identity providers
  • Cons: Complex setup, external dependencies
  • Rejected: Overkill for current requirements

Alternative 4: Pure SQL (no ORM)

  • Pros: No ORM overhead, direct control
  • Cons: More boilerplate, manual query building
  • Rejected: GORM provides good balance of control and convenience

Implementation Plan

This implementation builds upon the completed phases and follows the established dance-lessons-coach patterns.

Phase 10: User Management Foundation (Next Phase)

Objective: Establish database models and basic user management

  1. Add Dependencies:

    • github.com/golang-jwt/jwt/v5 for JWT authentication
    • golang.org/x/crypto for bcrypt password hashing
    • gorm.io/gorm and gorm.io/driver/postgres for ORM
  2. Create User Package:

    • pkg/user/models.go - User model and GORM repository
    • pkg/user/repository.go - Interface-based repository
    • pkg/user/service.go - User management service
  3. Database Setup:

    • Add PostgreSQL container to docker-compose.yml
    • Create database migration scripts
    • Implement GORM database connection
  4. Configuration Extension:

    • Extend Config struct with AuthConfig and DatabaseConfig
    • Add environment variable bindings with DLC_ prefix
    • Update LoadConfig() function

Phase 11: Authentication System

Objective: Implement secure authentication with JWT

  1. Auth Service:

    • pkg/auth/service.go - JWT generation/validation
    • pkg/auth/middleware.go - Chi authentication middleware
    • pkg/auth/context.go - Context utilities for user data
  2. Authentication Endpoints:

    • POST /api/v1/auth/register - User registration
    • POST /api/v1/auth/login - User login
    • POST /api/v1/auth/refresh - Token refresh
  3. Password Security:

    • Implement bcrypt password hashing (work factor 12)
    • Add password validation rules
    • Implement secure password comparison
  4. Admin Authentication:

    • Master password configuration
    • Non-persisted admin user
    • Admin middleware for privileged endpoints

Phase 12: Integration & Personalization

Objective: Integrate authentication with existing services

  1. Greet Service Enhancement:

    • Update pkg/greet/greet.go to check authenticated username
    • Maintain backward compatibility
    • Add context-based username extraction
  2. User Profile Endpoints:

    • GET /api/v1/users/me - Get current user profile
    • PUT /api/v1/users/me - Update profile (description, goal)
    • PUT /api/v1/users/me/password - Change password
  3. Admin Endpoints:

    • GET /api/v1/admin/users - List all users
    • POST /api/v1/admin/users/{username}/allow-reset - Allow password reset
    • DELETE /api/v1/admin/users/{username} - Delete user

Phase 13: Password Reset & Testing

Objective: Implement password reset workflow and comprehensive testing

  1. Password Reset Workflow:

    • POST /api/v1/auth/reset-password - Unauthenticated reset for flagged users
    • Admin flag management
    • Rate limiting for reset endpoints
  2. BDD Test Scenarios:

    • User registration and login
    • Personalized greetings for authenticated users
    • Admin password reset workflow
    • Error handling and validation
  3. Unit & Integration Tests:

    • Password hashing/verification
    • JWT token generation/validation
    • Repository methods
    • Authentication middleware

Phase 14: CI/CD & Documentation

Objective: Update pipeline and create comprehensive documentation

  1. CI/CD Updates:

    • Add PostgreSQL service to CI environment
    • Update dependency management
    • Add authentication test suite
    • Implement security scanning
  2. Documentation:

    • Update AGENTS.md with new architecture
    • Create user management wiki page
    • Update API documentation with Swagger
    • Add configuration examples
  3. Deployment Preparation:

    • Create database migration guide
    • Update Docker configuration
    • Add environment variable documentation
    • Create rollback plan

Alignment with Existing Phases

This implementation builds upon the completed phases:

  • Phase 1-3: Uses existing Go 1.26.1, Chi router, Zerolog, and interface-based design
  • Phase 5: Extends Viper configuration management
  • Phase 6: Integrates with graceful shutdown patterns
  • Phase 7: Maintains OpenTelemetry compatibility
  • Phase 8: Follows existing build system patterns
  • Phase 9: Preserves trace-level logging approach

Backward Compatibility

The implementation maintains full backward compatibility:

  1. Greet Service: Falls back to original behavior when no authentication
  2. API Endpoints: Existing /api/v1/greet/* endpoints unchanged
  3. Configuration: All existing config options preserved
  4. Logging: Maintains existing Zerolog integration
  5. Telemetry: OpenTelemetry continues to work unchanged

Success Metrics

  1. Security: No authentication bypass vulnerabilities
  2. Performance: <50ms auth middleware overhead
  3. Reliability: 99.9% uptime for auth endpoints
  4. Adoption: 80% of API calls authenticated within 3 months
  5. Satisfaction: User feedback on personalization features

Open Questions

  1. Should we implement refresh tokens for longer sessions?
  2. What should be the maximum session duration?
  3. Should we add IP-based rate limiting for auth endpoints?
  4. What username characters should be allowed beyond alphanumeric?
  5. Should we implement account lockout after failed attempts?

Future Considerations

  1. Multi-factor Authentication: For enhanced security
  2. OAuth Integration: For third-party identity providers
  3. User Activity Logging: For audit trails
  4. Password Strength Meter: For better user experience
  5. Account Recovery: Email/phone-based recovery options
  6. JWT Secret Rotation: Implement secret persistence and rotation mechanism (Issue #8)

References

Approved by: [Product Owner] Approval Date: [To be determined] Implementation Target: Q2 2024