Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions backend/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.env
config.prod.yml
*.tar.gz
51 changes: 33 additions & 18 deletions backend/controllers/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ func SignUp(ctx *gin.Context) {
return
}

// normalize email FIRST
request.Email = strings.ToLower(strings.TrimSpace(request.Email))


// Check if user already exists
dbCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
Expand Down Expand Up @@ -212,6 +216,10 @@ func VerifyEmail(ctx *gin.Context) {
return
}

// normalize email FIRST
request.Email = strings.ToLower(strings.TrimSpace(request.Email))


// Find user with matching email and verification code
dbCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
Expand Down Expand Up @@ -274,54 +282,61 @@ func Login(ctx *gin.Context) {

var request structs.LoginRequest
if err := ctx.ShouldBindJSON(&request); err != nil {
ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid input", "message": "Check email and password format"})
ctx.JSON(http.StatusBadRequest, gin.H{
"error": "Invalid input",
"message": "Check email and password format",
})
return
}

// Find user in MongoDB
request.Email = strings.ToLower(strings.TrimSpace(request.Email))

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}).Decode(&user)
err := db.MongoDatabase.
Collection("users").
FindOne(dbCtx, bson.M{"email": request.Email}).
Decode(&user)

if err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid email or password"})
return
}

// Normalize stats if needed
if normalizeUserStats(&user) {
if err := persistUserStats(dbCtx, &user); err != nil {
}
// 🔐 Verify password FIRST
if err := bcrypt.CompareHashAndPassword(
[]byte(user.Password),
[]byte(request.Password),
); err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid email or password"})
return
}

// Check if user is verified
// 📧 Check verification AFTER password
if !user.IsVerified {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Email not verified"})
return
}

// Verify password
err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(request.Password))
if err != nil {
ctx.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid email or password"})
return
}

// Generate JWT
token, err := generateJWT(user.Email, cfg.JWT.Secret, cfg.JWT.Expiry)
if err != nil {
ctx.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate token", "message": err.Error()})
ctx.JSON(http.StatusInternalServerError, gin.H{
"error": "Failed to generate token",
"message": err.Error(),
})
return
}

// Return user details
ctx.JSON(http.StatusOK, gin.H{
"message": "Sign-in successful",
"accessToken": token,
"user": buildUserResponse(user),
})
}


func normalizeUserStats(user *models.User) bool {
updated := false
if math.IsNaN(user.Rating) || math.IsInf(user.Rating, 0) {
Expand Down
5 changes: 0 additions & 5 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,6 @@ function AppRoutes() {
path='coach/strengthen-argument'
element={<StrengthenArgument />}
/>
<Route path='/coach' element={<CoachPage />} />
<Route
path='coach/strengthen-argument'
element={<StrengthenArgument />}
/>{' '}
{/* Add this route */}
<Route path='coach/pros-cons' element={<ProsConsChallenge />} />
</Route>
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/Pages/Authentication/forms.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@ const handleGoogleLogin = useCallback(
google.accounts.id.renderButton(buttonElement, {
theme: 'outline',
size: 'large',
text: 'signin_with',
width: '100%',
text: 'signin_with'
});
}

return () => {
google.accounts.id.cancel();
};
}, [handleGoogleLogin]);


return (
<form className="w-full" onSubmit={handleSubmit}>
Expand Down
Loading