diff --git a/backend/controllers/auth.go b/backend/controllers/auth.go index a8d594d..c404fbf 100644 --- a/backend/controllers/auth.go +++ b/backend/controllers/auth.go @@ -157,7 +157,7 @@ func SignUp(ctx *gin.Context) { // Generate verification code verificationCode := utils.GenerateRandomCode(6) - // Create new user + // Create new user (unverified) now := time.Now() newUser := models.User{ Email: request.Email, @@ -172,9 +172,9 @@ func SignUp(ctx *gin.Context) { Password: string(hashedPassword), IsVerified: false, VerificationCode: verificationCode, - Score: 0, // Initialize gamification score - Badges: []string{}, // Initialize badges array - CurrentStreak: 0, // Initialize streak + Score: 0, + Badges: []string{}, + CurrentStreak: 0, CreatedAt: now, UpdatedAt: now, } @@ -194,10 +194,9 @@ func SignUp(ctx *gin.Context) { return } - // Return user details + // Return success response ctx.JSON(200, gin.H{ "message": "Sign-up successful. Please verify your email.", - "user": buildUserResponse(newUser), }) } @@ -213,15 +212,25 @@ func VerifyEmail(ctx *gin.Context) { return } + // Find user with matching email and verification code dbCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() var user models.User - err := db.MongoDatabase.Collection("users").FindOne(dbCtx, bson.M{"email": request.Email, "verificationCode": request.ConfirmationCode}).Decode(&user) + err := db.MongoDatabase.Collection("users").FindOne(dbCtx, bson.M{ + "email": request.Email, + "verificationCode": request.ConfirmationCode, + }).Decode(&user) if err != nil { ctx.JSON(400, gin.H{"error": "Invalid email or verification code"}) return } + // Check if verification code is expired (24 hours) + if time.Since(user.CreatedAt) > 24*time.Hour { + ctx.JSON(400, gin.H{"error": "Verification code expired. Please sign up again."}) + return + } + // Update user verification status now := time.Now() update := bson.M{ @@ -237,15 +246,23 @@ func VerifyEmail(ctx *gin.Context) { return } - // Return updated user details + // Update local user object + user.IsVerified = true + user.VerificationCode = "" + user.UpdatedAt = now + + // Generate JWT for immediate login + token, err := generateJWT(user.Email, cfg.JWT.Secret, cfg.JWT.Expiry) + if err != nil { + ctx.JSON(500, gin.H{"error": "Failed to generate token", "message": err.Error()}) + return + } + + // Return user details and access token ctx.JSON(200, gin.H{ - "message": "Email verification successful", - "user": func() gin.H { - response := buildUserResponse(user) - response["isVerified"] = true - response["updatedAt"] = now.Format(time.RFC3339) - return response - }(), + "message": "Email verification successful. You are now logged in.", + "accessToken": token, + "user": buildUserResponse(user), }) } @@ -278,7 +295,7 @@ func Login(ctx *gin.Context) { } // Check if user is verified - if !user.IsVerified && user.Password == "" { + if !user.IsVerified { ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Email not verified"}) return } diff --git a/backend/utils/email.go b/backend/utils/email.go index 7700d3d..3e0be3d 100644 --- a/backend/utils/email.go +++ b/backend/utils/email.go @@ -41,7 +41,9 @@ func SendVerificationEmail(email, code string) error { "MIME-Version: 1.0\r\n"+ "Content-Type: text/html; charset=\"UTF-8\"\r\n"+ "\r\n"+ - "
Your verification code is: %s
\r\n", + "Your verification code is: %s
\r\n"+ + "This code will expire in 24 hours.
\r\n", email, cfg.SMTP.SenderName, cfg.SMTP.SenderEmail, code)) addr := fmt.Sprintf("%s:%d", cfg.SMTP.Host, cfg.SMTP.Port) diff --git a/frontend/src/Pages/Authentication/forms.tsx b/frontend/src/Pages/Authentication/forms.tsx index d2cf147..6345d93 100644 --- a/frontend/src/Pages/Authentication/forms.tsx +++ b/frontend/src/Pages/Authentication/forms.tsx @@ -118,7 +118,7 @@ export const SignUpForm: React.FC