tracks/internal/handlers/auth_handler.go
Claude 4e9e0b4efa
Add default admin user and admin-only user creation
Features added:
- Automatic creation of default admin user on first startup (login: admin, password: admin)
- Admin-only endpoint POST /api/admin/users for creating new users
- Admin users can set is_admin flag when creating users
- Non-admin users are blocked from accessing admin endpoints

Implementation:
- Added CreateDefaultAdmin() function in internal/database/database.go
  - Checks if any users exist, creates admin only if database is empty
  - Admin user: login "admin", password "admin", is_admin true
- Added CreateUser() method to auth service for admin user creation
- Added CreateUser() handler to auth handler
- Added /api/admin/users endpoint with AuthMiddleware + AdminMiddleware
- Updated README_GOLANG.md with:
  - Default admin credentials
  - Instructions for creating additional users
  - Admin API documentation

Security:
- Default admin password should be changed after first login
- AdminMiddleware ensures only users with is_admin=true can access admin routes
- Non-admin users receive 403 Forbidden when accessing admin endpoints

Tested:
- Default admin creation on startup ✓
- Admin login with default credentials ✓
- Admin can create new users ✓
- New users can login ✓
- Non-admin users blocked from admin endpoints ✓
2025-11-05 11:35:36 +00:00

113 lines
2.7 KiB
Go

package handlers
import (
"net/http"
"github.com/TracksApp/tracks/internal/middleware"
"github.com/TracksApp/tracks/internal/services"
"github.com/gin-gonic/gin"
)
// AuthHandler handles authentication endpoints
type AuthHandler struct {
authService *services.AuthService
}
// NewAuthHandler creates a new AuthHandler
func NewAuthHandler(authService *services.AuthService) *AuthHandler {
return &AuthHandler{
authService: authService,
}
}
// Login handles POST /api/login
func (h *AuthHandler) Login(c *gin.Context) {
var req services.LoginRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
resp, err := h.authService.Login(req)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()})
return
}
// Set cookie
c.SetCookie("tracks_token", resp.Token, 60*60*24*7, "/", "", false, true)
c.JSON(http.StatusOK, resp)
}
// Register handles POST /api/register
func (h *AuthHandler) Register(c *gin.Context) {
var req services.RegisterRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
resp, err := h.authService.Register(req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Set cookie
c.SetCookie("tracks_token", resp.Token, 60*60*24*7, "/", "", false, true)
c.JSON(http.StatusCreated, resp)
}
// Logout handles POST /api/logout
func (h *AuthHandler) Logout(c *gin.Context) {
// Clear cookie
c.SetCookie("tracks_token", "", -1, "/", "", false, true)
c.JSON(http.StatusOK, gin.H{"message": "Logged out successfully"})
}
// Me handles GET /api/me
func (h *AuthHandler) Me(c *gin.Context) {
user, err := middleware.GetCurrentUser(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"})
return
}
c.JSON(http.StatusOK, user)
}
// RefreshToken handles POST /api/refresh-token
func (h *AuthHandler) RefreshToken(c *gin.Context) {
user, err := middleware.GetCurrentUser(c)
if err != nil {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Not authenticated"})
return
}
token, err := h.authService.RefreshToken(user.ID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to refresh token"})
return
}
c.JSON(http.StatusOK, gin.H{"token": token})
}
// CreateUser handles POST /api/admin/users (admin only)
func (h *AuthHandler) CreateUser(c *gin.Context) {
var req services.CreateUserRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
user, err := h.authService.CreateUser(req)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
c.JSON(http.StatusCreated, user)
}