mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-06-06 05:51:19 +08:00
refactor(auth): remove OTP flows from login/register/reset
This commit is contained in:
142
api/server.go
142
api/server.go
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user