@@ -2,24 +2,30 @@ package handlers
2
2
3
3
import (
4
4
"bullet-cloud-api/internal/auth"
5
+ "bullet-cloud-api/internal/models"
5
6
"bullet-cloud-api/internal/users"
6
7
"bullet-cloud-api/internal/webutils"
8
+ "context"
9
+ "encoding/json"
7
10
"errors"
8
11
"net/http"
12
+ "strings"
9
13
"time"
10
14
)
11
15
12
16
// AuthHandler handles authentication requests.
13
17
type AuthHandler struct {
14
18
UserRepo users.UserRepository
19
+ Hasher auth.PasswordHasher
15
20
JwtSecret string
16
21
TokenExpiryDuration time.Duration
17
22
}
18
23
19
24
// NewAuthHandler creates a new AuthHandler.
20
- func NewAuthHandler (userRepo users.UserRepository , jwtSecret string , tokenExpiry time.Duration ) * AuthHandler {
25
+ func NewAuthHandler (userRepo users.UserRepository , hasher auth. PasswordHasher , jwtSecret string , tokenExpiry time.Duration ) * AuthHandler {
21
26
return & AuthHandler {
22
27
UserRepo : userRepo ,
28
+ Hasher : hasher ,
23
29
JwtSecret : jwtSecret ,
24
30
TokenExpiryDuration : tokenExpiry ,
25
31
}
@@ -46,79 +52,100 @@ type LoginResponse struct {
46
52
47
53
// Register handles new user registration.
48
54
func (h * AuthHandler ) Register (w http.ResponseWriter , r * http.Request ) {
49
- var req RegisterRequest
50
- if err := webutils .ReadJSON (r , & req ); err != nil {
55
+ var req struct {
56
+ Name string `json:"name"`
57
+ Email string `json:"email"`
58
+ Password string `json:"password"`
59
+ }
60
+
61
+ if err := json .NewDecoder (r .Body ).Decode (& req ); err != nil {
51
62
webutils .ErrorJSON (w , errors .New ("invalid request body" ), http .StatusBadRequest )
52
63
return
53
64
}
54
65
55
- // Basic validation
66
+ req .Name = strings .TrimSpace (req .Name )
67
+ req .Email = strings .TrimSpace (strings .ToLower (req .Email ))
68
+
56
69
if req .Name == "" || req .Email == "" || req .Password == "" {
57
70
webutils .ErrorJSON (w , errors .New ("name, email, and password are required" ), http .StatusBadRequest )
58
71
return
59
72
}
60
- // TODO: Add more robust validation (email format, password strength)
61
73
62
- // Hash password
63
- hashedPassword , err := auth .HashPassword (req .Password )
74
+ // Check if email already exists
75
+ _ , err := h .UserRepo .FindByEmail (context .Background (), req .Email )
76
+ if err == nil {
77
+ webutils .ErrorJSON (w , errors .New ("email already registered" ), http .StatusConflict )
78
+ return
79
+ } else if ! errors .Is (err , users .ErrUserNotFound ) {
80
+ webutils .ErrorJSON (w , errors .New ("failed to check email existence" ), http .StatusInternalServerError )
81
+ return
82
+ }
83
+
84
+ // Hash the password using the injected hasher
85
+ hashedPassword , err := h .Hasher .HashPassword (req .Password )
64
86
if err != nil {
65
- webutils .ErrorJSON (w , errors .New ("failed to hash password " ), http .StatusInternalServerError )
87
+ webutils .ErrorJSON (w , errors .New ("failed to register user " ), http .StatusInternalServerError )
66
88
return
67
89
}
68
90
69
- // Create user
70
- newUser , err := h .UserRepo .Create (r .Context (), req .Name , req .Email , hashedPassword )
91
+ user := & models.User {
92
+ Name : req .Name ,
93
+ Email : req .Email ,
94
+ PasswordHash : hashedPassword ,
95
+ }
96
+
97
+ // Call UserRepo.Create with individual fields as per current repo signature
98
+ createdUser , err := h .UserRepo .Create (context .Background (), user .Name , user .Email , user .PasswordHash )
71
99
if err != nil {
72
- if errors .Is (err , users .ErrEmailAlreadyExists ) {
73
- webutils .ErrorJSON (w , err , http .StatusConflict )
74
- } else {
75
- webutils .ErrorJSON (w , errors .New ("failed to create user" ), http .StatusInternalServerError )
76
- }
100
+ webutils .ErrorJSON (w , errors .New ("failed to register user" ), http .StatusInternalServerError )
77
101
return
78
102
}
79
103
80
- // Return created user (ensure password hash is not included)
81
- newUser .PasswordHash = "" // Already done in repository, but good practice to double-check
82
- webutils .WriteJSON (w , http .StatusCreated , newUser )
104
+ webutils .WriteJSON (w , http .StatusCreated , createdUser )
83
105
}
84
106
85
107
// Login handles user login and JWT generation.
86
108
func (h * AuthHandler ) Login (w http.ResponseWriter , r * http.Request ) {
87
- var req LoginRequest
88
- if err := webutils .ReadJSON (r , & req ); err != nil {
109
+ var req struct {
110
+ Email string `json:"email"`
111
+ Password string `json:"password"`
112
+ }
113
+
114
+ if err := json .NewDecoder (r .Body ).Decode (& req ); err != nil {
89
115
webutils .ErrorJSON (w , errors .New ("invalid request body" ), http .StatusBadRequest )
90
116
return
91
117
}
92
118
119
+ req .Email = strings .TrimSpace (strings .ToLower (req .Email ))
120
+
93
121
if req .Email == "" || req .Password == "" {
94
122
webutils .ErrorJSON (w , errors .New ("email and password are required" ), http .StatusBadRequest )
95
123
return
96
124
}
97
125
98
- // Find user by email
99
- user , err := h .UserRepo .FindByEmail (r .Context (), req .Email )
126
+ user , err := h .UserRepo .FindByEmail (context .Background (), req .Email )
100
127
if err != nil {
101
128
if errors .Is (err , users .ErrUserNotFound ) {
102
129
webutils .ErrorJSON (w , errors .New ("invalid email or password" ), http .StatusUnauthorized )
103
130
} else {
104
- webutils .ErrorJSON (w , errors .New ("failed to find user " ), http .StatusInternalServerError )
131
+ webutils .ErrorJSON (w , errors .New ("login failed " ), http .StatusInternalServerError )
105
132
}
106
133
return
107
134
}
108
135
109
- // Check password
110
- if ! auth .CheckPasswordHash (req .Password , user .PasswordHash ) {
136
+ // Check the password using the injected hasher
137
+ err = h .Hasher .CheckPassword (user .PasswordHash , req .Password )
138
+ if err != nil { // bcrypt.CompareHashAndPassword returns error on mismatch
111
139
webutils .ErrorJSON (w , errors .New ("invalid email or password" ), http .StatusUnauthorized )
112
140
return
113
141
}
114
142
115
- // Generate JWT
116
- tokenString , err := auth .GenerateToken (user .ID , h .JwtSecret , h .TokenExpiryDuration )
143
+ // Generate JWT using the correct function name
144
+ token , err := auth .GenerateToken (user .ID , h .JwtSecret , h .TokenExpiryDuration )
117
145
if err != nil {
118
- webutils .ErrorJSON (w , errors .New ("failed to generate token " ), http .StatusInternalServerError )
146
+ webutils .ErrorJSON (w , errors .New ("login failed " ), http .StatusInternalServerError )
119
147
return
120
148
}
121
149
122
- // Return token
123
- webutils .WriteJSON (w , http .StatusOK , LoginResponse {Token : tokenString })
150
+ webutils .WriteJSON (w , http .StatusOK , map [string ]string {"token" : token })
124
151
}
0 commit comments