package handler import ( "net/http" "github.com/gin-gonic/gin" "github.com/user-management-system/internal/service" ) // TOTPHandler handles TOTP 2FA requests type TOTPHandler struct { authService *service.AuthService totpService *service.TOTPService } // NewTOTPHandler creates a new TOTPHandler func NewTOTPHandler(authService *service.AuthService, totpService *service.TOTPService) *TOTPHandler { return &TOTPHandler{ authService: authService, totpService: totpService, } } func (h *TOTPHandler) GetTOTPStatus(c *gin.Context) { userID, ok := getUserIDFromContext(c) if !ok { c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) return } enabled, err := h.totpService.GetTOTPStatus(c.Request.Context(), userID) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"enabled": enabled}) } func (h *TOTPHandler) SetupTOTP(c *gin.Context) { userID, ok := getUserIDFromContext(c) if !ok { c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) return } resp, err := h.totpService.SetupTOTP(c.Request.Context(), userID) if err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{ "secret": resp.Secret, "qr_code_base64": resp.QRCodeBase64, "recovery_codes": resp.RecoveryCodes, }) } func (h *TOTPHandler) EnableTOTP(c *gin.Context) { userID, ok := getUserIDFromContext(c) if !ok { c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) return } var req struct { Code string `json:"code" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := h.totpService.EnableTOTP(c.Request.Context(), userID, req.Code); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"message": "TOTP enabled"}) } func (h *TOTPHandler) DisableTOTP(c *gin.Context) { userID, ok := getUserIDFromContext(c) if !ok { c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) return } var req struct { Code string `json:"code" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := h.totpService.DisableTOTP(c.Request.Context(), userID, req.Code); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"message": "TOTP disabled"}) } func (h *TOTPHandler) VerifyTOTP(c *gin.Context) { userID, ok := getUserIDFromContext(c) if !ok { c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) return } var req struct { Code string `json:"code" binding:"required"` DeviceID string `json:"device_id,omitempty"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } if err := h.authService.VerifyTOTP(c.Request.Context(), userID, req.Code, req.DeviceID); err != nil { handleError(c, err) return } c.JSON(http.StatusOK, gin.H{"verified": true}) }