package handlers import ( "encoding/json" "fmt" "log/slog" "net/http" "strings" "gitlab.com/unboundsoftware/auth0mock/store" ) // ManagementHandler handles Auth0 Management API endpoints type ManagementHandler struct { userStore *store.UserStore logger *slog.Logger } // NewManagementHandler creates a new management handler func NewManagementHandler(userStore *store.UserStore, logger *slog.Logger) *ManagementHandler { return &ManagementHandler{ userStore: userStore, logger: logger, } } // UserResponse represents the user response format type UserResponse struct { Email string `json:"email,omitempty"` GivenName string `json:"given_name,omitempty"` FamilyName string `json:"family_name,omitempty"` UserID string `json:"user_id"` Picture string `json:"picture,omitempty"` } // GetUsersByEmail handles GET /api/v2/users-by-email func (h *ManagementHandler) GetUsersByEmail(w http.ResponseWriter, r *http.Request) { email := r.URL.Query().Get("email") h.logger.Debug("getting user by email", "email", email) user, ok := h.userStore.GetByEmail(email) if !ok { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode([]interface{}{}) return } response := []UserResponse{ { Email: user.Email, GivenName: user.GivenName, FamilyName: user.FamilyName, UserID: fmt.Sprintf("auth0|%s", user.UserID), Picture: user.Picture, }, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(response) } // CreateUser handles POST /api/v2/users func (h *ManagementHandler) CreateUser(w http.ResponseWriter, r *http.Request) { var req struct { Email string `json:"email"` GivenName string `json:"given_name"` FamilyName string `json:"family_name"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } if req.Email == "" { http.Error(w, "Email is required", http.StatusBadRequest) return } user := &store.User{ Email: req.Email, GivenName: req.GivenName, FamilyName: req.FamilyName, UserID: req.Email, } // Set defaults if not provided if user.GivenName == "" { user.GivenName = "Given" } if user.FamilyName == "" { user.FamilyName = "Last" } h.userStore.Create(req.Email, user) h.logger.Info("created user", "email", req.Email) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "user_id": fmt.Sprintf("auth0|%s", req.Email), }) } // UpdateUser handles PATCH /api/v2/users/{userid} func (h *ManagementHandler) UpdateUser(w http.ResponseWriter, r *http.Request) { // Extract user ID from path - format: /api/v2/users/auth0|email@example.com path := r.URL.Path userID := strings.TrimPrefix(path, "/api/v2/users/") // Strip "auth0|" prefix to get email email := strings.TrimPrefix(userID, "auth0|") h.logger.Debug("patching user", "userid", userID, "email", email) var req struct { GivenName string `json:"given_name"` FamilyName string `json:"family_name"` Picture string `json:"picture"` } if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid request body", http.StatusBadRequest) return } updates := &store.User{ GivenName: req.GivenName, FamilyName: req.FamilyName, Picture: req.Picture, } _, ok := h.userStore.Update(email, updates) if !ok { http.Error(w, "User not found", http.StatusNotFound) return } h.logger.Info("updated user", "email", email) w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "user_id": fmt.Sprintf("auth0|%s", email), }) } // PasswordChangeTicket handles POST /api/v2/tickets/password-change func (h *ManagementHandler) PasswordChangeTicket(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(map[string]string{ "ticket": "https://some-url", }) }