refactor(auth): remove OTP flows from login/register/reset

This commit is contained in:
tinkle-community
2026-03-05 18:55:36 +08:00
parent fa664ccae3
commit 73f1fe105d
11 changed files with 210 additions and 1207 deletions

View File

@@ -142,8 +142,6 @@ func (s *Server) setupRoutes() {
// Authentication related routes (no authentication required)
api.POST("/register", s.handleRegister)
api.POST("/login", s.handleLogin)
api.POST("/verify-otp", s.handleVerifyOTP)
api.POST("/complete-registration", s.handleCompleteRegistration)
// Routes requiring authentication
protected := api.Group("/", s.authMiddleware())
@@ -3095,29 +3093,9 @@ func (s *Server) handleRegister(c *gin.Context) {
return
}
// Check if email already exists (must check before maxUsers to allow incomplete OTP users)
existingUser, err := s.store.User().GetByEmail(req.Email)
// Check if email already exists
_, err := s.store.User().GetByEmail(req.Email)
if err == nil {
// User exists, check OTP verification status
if !existingUser.OTPVerified {
// OTP not verified, verify password first for security
if !auth.CheckPassword(req.Password, existingUser.PasswordHash) {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Email or password incorrect"})
return
}
// Password correct, allow user to continue OTP setup
// Return existing OTP information
qrCodeURL := auth.GetOTPQRCodeURL(existingUser.OTPSecret, req.Email)
c.JSON(http.StatusOK, gin.H{
"user_id": existingUser.ID,
"email": existingUser.Email,
"otp_secret": existingUser.OTPSecret,
"qr_code_url": qrCodeURL,
"message": "Incomplete registration detected, please continue OTP setup",
})
return
}
// OTP already verified, reject duplicate registration
c.JSON(http.StatusConflict, gin.H{"error": "Email already registered"})
return
}
@@ -3143,21 +3121,12 @@ func (s *Server) handleRegister(c *gin.Context) {
return
}
// Generate OTP secret
otpSecret, err := auth.GenerateOTPSecret()
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "OTP secret generation failed"})
return
}
// Create user (unverified OTP status)
// Create user
userID := uuid.New().String()
user := &store.User{
ID: userID,
Email: req.Email,
PasswordHash: passwordHash,
OTPSecret: otpSecret,
OTPVerified: false,
}
err = s.store.User().Create(user)
@@ -3166,49 +3135,6 @@ func (s *Server) handleRegister(c *gin.Context) {
return
}
// Return OTP setup information
qrCodeURL := auth.GetOTPQRCodeURL(otpSecret, req.Email)
c.JSON(http.StatusOK, gin.H{
"user_id": userID,
"email": req.Email,
"otp_secret": otpSecret,
"qr_code_url": qrCodeURL,
"message": "Please scan the QR code with Google Authenticator and verify OTP",
})
}
// handleCompleteRegistration Complete registration (verify OTP)
func (s *Server) handleCompleteRegistration(c *gin.Context) {
var req struct {
UserID string `json:"user_id" binding:"required"`
OTPCode string `json:"otp_code" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
SafeBadRequest(c, "Invalid request parameters")
return
}
// Get user information
user, err := s.store.User().GetByID(req.UserID)
if err != nil {
SafeNotFound(c, "User")
return
}
// Verify OTP
if !auth.VerifyOTP(user.OTPSecret, req.OTPCode) {
c.JSON(http.StatusBadRequest, gin.H{"error": "OTP code error"})
return
}
// Update user OTP verified status
err = s.store.User().UpdateOTPVerified(req.UserID, true)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update user status"})
return
}
// Generate JWT token
token, err := auth.GenerateJWT(user.ID, user.Email)
if err != nil {
@@ -3226,7 +3152,7 @@ func (s *Server) handleCompleteRegistration(c *gin.Context) {
"token": token,
"user_id": user.ID,
"email": user.Email,
"message": "Registration completed",
"message": "Registration successful",
})
}
@@ -3255,56 +3181,7 @@ func (s *Server) handleLogin(c *gin.Context) {
return
}
// Check if OTP is verified
if !user.OTPVerified {
// Return OTP info so user can complete setup
qrCodeURL := auth.GetOTPQRCodeURL(user.OTPSecret, user.Email)
c.JSON(http.StatusOK, gin.H{
"user_id": user.ID,
"email": user.Email,
"otp_secret": user.OTPSecret,
"qr_code_url": qrCodeURL,
"requires_otp_setup": true,
"message": "Please complete OTP setup first",
})
return
}
// Return status requiring OTP verification
c.JSON(http.StatusOK, gin.H{
"user_id": user.ID,
"email": user.Email,
"message": "Please enter Google Authenticator code",
"requires_otp": true,
})
}
// handleVerifyOTP Verify OTP and complete login
func (s *Server) handleVerifyOTP(c *gin.Context) {
var req struct {
UserID string `json:"user_id" binding:"required"`
OTPCode string `json:"otp_code" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
SafeBadRequest(c, "Invalid request parameters")
return
}
// Get user information
user, err := s.store.User().GetByID(req.UserID)
if err != nil {
SafeNotFound(c, "User")
return
}
// Verify OTP
if !auth.VerifyOTP(user.OTPSecret, req.OTPCode) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Verification code error"})
return
}
// Generate JWT token
// Issue token directly after password verification.
token, err := auth.GenerateJWT(user.ID, user.Email)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token"})
@@ -3319,12 +3196,11 @@ func (s *Server) handleVerifyOTP(c *gin.Context) {
})
}
// handleResetPassword Reset password (via email + OTP verification)
// handleResetPassword Reset password via email and new password
func (s *Server) handleResetPassword(c *gin.Context) {
var req struct {
Email string `json:"email" binding:"required,email"`
NewPassword string `json:"new_password" binding:"required,min=6"`
OTPCode string `json:"otp_code" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
@@ -3339,12 +3215,6 @@ func (s *Server) handleResetPassword(c *gin.Context) {
return
}
// Verify OTP
if !auth.VerifyOTP(user.OTPSecret, req.OTPCode) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Google Authenticator code error"})
return
}
// Generate new password hash
newPasswordHash, err := auth.HashPassword(req.NewPassword)
if err != nil {