Full pass over all 25 ADRs to align documentation with actual implementation state. Changes by ADR: README index: completely rewritten — previous table mapped numbers to wrong titles from 0010 onward. 0008 (BDD Testing): added note that flat features/ structure and godog CLI invocation are superseded by ADR-0024; framework decision stands. 0009 (Hybrid Testing): renamed from "Combine BDD and Swagger-based testing" to "BDD Testing with OpenAPI Documentation"; clarified that the SDK-testing layer was never built and has no open issue. 0013 (OpenAPI/Swagger): removed leftover merge conflict artifact (=======) and duplicated 60-line block. 0015 (Cobra CLI): fixed status contradiction — body said "Implemented" while footer said "Proposed". Now Accepted. 0018 (User Management): status Proposed → Accepted; system is fully implemented (JWT, bcrypt, GORM repos all present). 0019 (PostgreSQL): status Proposed → Accepted (Partial); added warning that sqlite_repository.go and gorm/driver/sqlite still present contrary to ADR intent. 0021 (JWT Retention): fixed wrong cross-reference (previously cited ADR-0009 "Hybrid Testing" as source of JWT multi-secret support); fixed title number from "10" to "21"; clarified that base JWT is implemented but the retention cleanup job is not. 0022 (Rate Limiting/Cache): added warning block linking to open Gitea issue #13; changed all 20 false ✅ implementation checkboxes to ❌. 0023 (Config Hot Reloading): added note that BDD scenarios exist for this feature but the feature itself is not yet implemented. 0024 (BDD Organization): status Proposed → Accepted; modular domain structure is fully built. 0025 (BDD Scenario Isolation): status Proposed → Accepted (Partial); Phase 1 done, Phase 2 blocked on ADR-0022. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
16 KiB
18. User Management and Authentication System
Date: 2024-04-06 Status: Accepted Implementation Date: 2026-04-08 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
-
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
-
Authentication System
- JWT-based authentication
- Secure password hashing (bcrypt)
- Session management
- Admin master password for non-persisted admin access
-
Password Reset Workflow
- Admin-assisted password reset (no email/phone required)
- Allow password reset flag for users
- Unauthenticated password reset endpoint for flagged users
-
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:
-
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) } -
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 } -
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"` } -
Chi Router Integration:
func (h *AuthHandler) RegisterRoutes(router chi.Router) { router.Post("/register", h.handleRegister) router.Post("/login", h.handleLogin) // ... other routes }
Security Considerations
- Password Storage: bcrypt with work factor 12
- 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)
- 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
- Password Reset Security:
- Admin-Only Flagging: Only authenticated admins can set
allow_password_resetflag - 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
- Admin-Only Flagging: Only authenticated admins can set
- Rate Limiting: On authentication endpoints (3 attempts/hour for password reset)
- Input Validation: Strict username validation (alphanumeric, 3-50 chars)
API Endpoints
Public Endpoints
POST /api/v1/auth/register- User registrationPOST /api/v1/auth/login- User loginPOST /api/v1/auth/reset-password- Password reset (for flagged users)
Authenticated Endpoints
GET /api/v1/users/me- Get current user profilePUT /api/v1/users/me- Update current user profilePUT /api/v1/users/me/password- Change own password
Admin-Only Endpoints
GET /api/v1/admin/users- List all usersPOST /api/v1/admin/users/{username}/allow-reset- Allow password resetDELETE /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
- Personalized Experience: Users see their username in greetings
- Admin Capabilities: Secure administrative functions
- Password Recovery: Admin-assisted workflow without contact info
- Security: Proper authentication and authorization
- Extensibility: Foundation for future user-based features
Negative
- Increased Complexity: Additional dependencies and configuration
- Database Requirement: PostgreSQL container needed for development
- Migration Complexity: Existing tests need updates
- CI/CD Changes: Pipeline needs adjustment for new dependencies
- Performance Impact: Authentication middleware adds overhead
Neutral
- Learning Curve: Team needs to learn GORM and JWT
- Testing Overhead: Additional BDD scenarios required
- 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
-
Add Dependencies:
github.com/golang-jwt/jwt/v5for JWT authenticationgolang.org/x/cryptofor bcrypt password hashinggorm.io/gormandgorm.io/driver/postgresfor ORM
-
Create User Package:
pkg/user/models.go- User model and GORM repositorypkg/user/repository.go- Interface-based repositorypkg/user/service.go- User management service
-
Database Setup:
- Add PostgreSQL container to
docker-compose.yml - Create database migration scripts
- Implement GORM database connection
- Add PostgreSQL container to
-
Configuration Extension:
- Extend
Configstruct withAuthConfigandDatabaseConfig - Add environment variable bindings with
DLC_prefix - Update
LoadConfig()function
- Extend
Phase 11: Authentication System
Objective: Implement secure authentication with JWT
-
Auth Service:
pkg/auth/service.go- JWT generation/validationpkg/auth/middleware.go- Chi authentication middlewarepkg/auth/context.go- Context utilities for user data
-
Authentication Endpoints:
POST /api/v1/auth/register- User registrationPOST /api/v1/auth/login- User loginPOST /api/v1/auth/refresh- Token refresh
-
Password Security:
- Implement bcrypt password hashing (work factor 12)
- Add password validation rules
- Implement secure password comparison
-
Admin Authentication:
- Master password configuration
- Non-persisted admin user
- Admin middleware for privileged endpoints
Phase 12: Integration & Personalization
Objective: Integrate authentication with existing services
-
Greet Service Enhancement:
- Update
pkg/greet/greet.goto check authenticated username - Maintain backward compatibility
- Add context-based username extraction
- Update
-
User Profile Endpoints:
GET /api/v1/users/me- Get current user profilePUT /api/v1/users/me- Update profile (description, goal)PUT /api/v1/users/me/password- Change password
-
Admin Endpoints:
GET /api/v1/admin/users- List all usersPOST /api/v1/admin/users/{username}/allow-reset- Allow password resetDELETE /api/v1/admin/users/{username}- Delete user
Phase 13: Password Reset & Testing
Objective: Implement password reset workflow and comprehensive testing
-
Password Reset Workflow:
POST /api/v1/auth/reset-password- Unauthenticated reset for flagged users- Admin flag management
- Rate limiting for reset endpoints
-
BDD Test Scenarios:
- User registration and login
- Personalized greetings for authenticated users
- Admin password reset workflow
- Error handling and validation
-
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
-
CI/CD Updates:
- Add PostgreSQL service to CI environment
- Update dependency management
- Add authentication test suite
- Implement security scanning
-
Documentation:
- Update
AGENTS.mdwith new architecture - Create user management wiki page
- Update API documentation with Swagger
- Add configuration examples
- Update
-
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:
- Greet Service: Falls back to original behavior when no authentication
- API Endpoints: Existing
/api/v1/greet/*endpoints unchanged - Configuration: All existing config options preserved
- Logging: Maintains existing Zerolog integration
- Telemetry: OpenTelemetry continues to work unchanged
Success Metrics
- Security: No authentication bypass vulnerabilities
- Performance: <50ms auth middleware overhead
- Reliability: 99.9% uptime for auth endpoints
- Adoption: 80% of API calls authenticated within 3 months
- Satisfaction: User feedback on personalization features
Open Questions
- Should we implement refresh tokens for longer sessions?
- What should be the maximum session duration?
- Should we add IP-based rate limiting for auth endpoints?
- What username characters should be allowed beyond alphanumeric?
- Should we implement account lockout after failed attempts?
Future Considerations
- Multi-factor Authentication: For enhanced security
- OAuth Integration: For third-party identity providers
- User Activity Logging: For audit trails
- Password Strength Meter: For better user experience
- Account Recovery: Email/phone-based recovery options
- 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