Google Sign in added

This commit is contained in:
Sidharth Prabhu
2026-07-24 11:21:35 +05:30
parent 93e375dc7d
commit f8bd97229b
19 changed files with 2614 additions and 278 deletions

View File

@@ -44,6 +44,8 @@ public class SecurityConfig {
.requestMatchers(HttpMethod.POST, "/api/auth/check").permitAll()
.requestMatchers(HttpMethod.POST, "/api/auth/register").permitAll()
.requestMatchers(HttpMethod.POST, "/api/auth/login").permitAll()
.requestMatchers(HttpMethod.POST, "/api/auth/firebase-login").permitAll()
.requestMatchers(HttpMethod.POST, "/api/auth/check-email").permitAll()
.requestMatchers(HttpMethod.POST, "/api/auth/logout").permitAll()
// ── PUBLIC: Real-time stock updates (SSE — read-only, ordering app listens) ──

View File

@@ -126,6 +126,40 @@ public class UserController {
}
}
@PostMapping("/check-email")
public ResponseEntity<?> checkEmailExists(@RequestBody Map<String, String> body) {
String email = body.get("email");
if (email == null || email.trim().isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("error", "Email is required"));
}
boolean exists = userService.existsByEmail(email);
return ResponseEntity.ok(Map.of("exists", exists));
}
@PostMapping("/firebase-login")
public ResponseEntity<?> firebaseLogin(@RequestBody Map<String, String> body) {
String email = body.get("email");
String name = body.get("name");
String mobileNumber = body.get("mobileNumber"); // optional, supplied on first login
if (email == null || email.trim().isEmpty()) {
return ResponseEntity.badRequest().body(Map.of("error", "Email is required"));
}
LoginResponse response = userService.firebaseLoginOrCreate(email, name, mobileNumber);
if (response.isSuccess()) {
Long userId = response.getUser() != null ? response.getUser().getId() : null;
String userMobile = response.getUser() != null ? response.getUser().getMobileNumber() : "";
if (userId != null) {
String token = jwtUtil.generateUserToken(userId, userMobile);
response.setToken(token);
}
return ResponseEntity.ok(response);
} else {
return ResponseEntity.badRequest().body(response);
}
}
@PostMapping("/logout")
public ResponseEntity<LoginResponse> logout(@Valid @RequestBody LoginRequest request) {
LoginResponse response = userService.logout(request.getMobileNumber());

View File

@@ -49,6 +49,7 @@ public class LoginResponse {
private boolean isLoggedIn;
private boolean isSuspended;
private java.math.BigDecimal ritzTokenBalance;
private String membership;
public UserDto() {}
@@ -61,6 +62,16 @@ public class LoginResponse {
this.ritzTokenBalance = ritzTokenBalance;
}
public UserDto(Long id, String mobileNumber, String name, boolean isLoggedIn, boolean isSuspended, java.math.BigDecimal ritzTokenBalance, String membership) {
this.id = id;
this.mobileNumber = mobileNumber;
this.name = name;
this.isLoggedIn = isLoggedIn;
this.isSuspended = isSuspended;
this.ritzTokenBalance = ritzTokenBalance;
this.membership = membership;
}
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
@@ -74,9 +85,12 @@ public class LoginResponse {
public void setLoggedIn(boolean loggedIn) { this.isLoggedIn = loggedIn; }
public boolean isSuspended() { return isSuspended; }
public void setSuspended(boolean suspended) { isSuspended = suspended; }
public void setSuspended(boolean suspended) { this.isSuspended = suspended; }
public java.math.BigDecimal getRitzTokenBalance() { return ritzTokenBalance; }
public void setRitzTokenBalance(java.math.BigDecimal ritzTokenBalance) { this.ritzTokenBalance = ritzTokenBalance; }
public String getMembership() { return membership; }
public void setMembership(String membership) { this.membership = membership; }
}
}

View File

@@ -40,6 +40,12 @@ public class User {
@Column(nullable = false, columnDefinition = "boolean default false")
private boolean isSuspended = false;
@Column(unique = true, nullable = true)
private String email;
@Column(nullable = true)
private String membership;
@Column(nullable = true)
private LocalDateTime lastLoginAt;
@@ -75,6 +81,12 @@ public class User {
public LocalDateTime getLastLoginAt() { return lastLoginAt; }
public void setLastLoginAt(LocalDateTime lastLoginAt) { this.lastLoginAt = lastLoginAt; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public String getMembership() { return membership; }
public void setMembership(String membership) { this.membership = membership; }
@PrePersist
protected void onCreate() {
createdAt = LocalDateTime.now();

View File

@@ -11,6 +11,8 @@ import java.util.Optional;
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByMobileNumber(String mobileNumber);
boolean existsByMobileNumber(String mobileNumber);
Optional<User> findByEmail(String email);
boolean existsByEmail(String email);
@Query("SELECT u FROM User u WHERE LOWER(u.name) LIKE LOWER(CONCAT('%', :search, '%')) " +
"OR u.mobileNumber LIKE CONCAT('%', :search, '%')")

View File

@@ -61,7 +61,7 @@ public class UserService {
userRepository.save(user);
LoginResponse.UserDto userDto = new LoginResponse.UserDto(
user.getId(), user.getMobileNumber(), user.getName(), user.isLoggedIn(), user.isSuspended(), user.getRitzTokenBalance()
user.getId(), user.getMobileNumber(), user.getName(), user.isLoggedIn(), user.isSuspended(), user.getRitzTokenBalance(), user.getMembership()
);
return new LoginResponse(true, "Registration successful. You are now logged in.", userDto);
@@ -81,7 +81,7 @@ public class UserService {
if (user.isSuspended()) {
LoginResponse.UserDto userDto = new LoginResponse.UserDto(
user.getId(), user.getMobileNumber(), user.getName(), user.isLoggedIn(), user.isSuspended(), user.getRitzTokenBalance()
user.getId(), user.getMobileNumber(), user.getName(), user.isLoggedIn(), user.isSuspended(), user.getRitzTokenBalance(), user.getMembership()
);
return new LoginResponse(false, "Your account has been suspended. Please contact the administrator.", userDto);
}
@@ -95,7 +95,7 @@ public class UserService {
userRepository.save(user);
LoginResponse.UserDto userDto = new LoginResponse.UserDto(
user.getId(), user.getMobileNumber(), user.getName(), user.isLoggedIn(), user.isSuspended(), user.getRitzTokenBalance()
user.getId(), user.getMobileNumber(), user.getName(), user.isLoggedIn(), user.isSuspended(), user.getRitzTokenBalance(), user.getMembership()
);
return new LoginResponse(true, "Login successful.", userDto);
@@ -161,7 +161,7 @@ public class UserService {
return null;
}
User user = userOpt.get();
return new LoginResponse.UserDto(user.getId(), user.getMobileNumber(), user.getName(), user.isLoggedIn(), user.isSuspended(), user.getRitzTokenBalance());
return new LoginResponse.UserDto(user.getId(), user.getMobileNumber(), user.getName(), user.isLoggedIn(), user.isSuspended(), user.getRitzTokenBalance(), user.getMembership());
}
/**
@@ -181,7 +181,8 @@ public class UserService {
user.getName(),
user.isLoggedIn(),
user.isSuspended(),
user.getRitzTokenBalance()
user.getRitzTokenBalance(),
user.getMembership()
));
}
@@ -220,7 +221,7 @@ public class UserService {
}
userRepository.save(user);
return new LoginResponse.UserDto(user.getId(), user.getMobileNumber(), user.getName(), user.isLoggedIn(), user.isSuspended(), user.getRitzTokenBalance());
return new LoginResponse.UserDto(user.getId(), user.getMobileNumber(), user.getName(), user.isLoggedIn(), user.isSuspended(), user.getRitzTokenBalance(), user.getMembership());
}
/**
@@ -241,7 +242,7 @@ public class UserService {
}
userRepository.save(user);
return new LoginResponse.UserDto(user.getId(), user.getMobileNumber(), user.getName(), user.isLoggedIn(), user.isSuspended(), user.getRitzTokenBalance());
return new LoginResponse.UserDto(user.getId(), user.getMobileNumber(), user.getName(), user.isLoggedIn(), user.isSuspended(), user.getRitzTokenBalance(), user.getMembership());
}
/**
@@ -264,5 +265,60 @@ public class UserService {
public void deleteUser(Long userId) {
userRepository.deleteById(userId);
}
}
/**
* Authenticate or create a customer user via Firebase/Google login.
*/
public boolean existsByEmail(String email) {
if (email == null) return false;
return userRepository.existsByEmail(email.trim().toLowerCase());
}
public LoginResponse firebaseLoginOrCreate(String email, String name, String mobileNumber) {
if (email == null || email.isBlank()) {
return new LoginResponse(false, "Email is required for Google authentication.");
}
String cleanEmail = email.trim().toLowerCase();
String finalName = name != null ? name : "Google User";
String membership = "Member"; // Everyone is classified as a normal Member now
Optional<User> userOpt = userRepository.findByEmail(cleanEmail);
User user;
if (userOpt.isPresent()) {
user = userOpt.get();
if (user.isSuspended()) {
return new LoginResponse(false, "Your account has been suspended. Please contact the administrator.");
}
user.setName(finalName);
user.setMembership(membership);
user.setLoggedIn(true);
user.setLastLoginAt(LocalDateTime.now());
userRepository.save(user);
} else {
if (mobileNumber == null || !mobileNumber.matches("^[0-9]{10}$")) {
return new LoginResponse(false, "A valid 10-digit mobile number is required for first-time registration.");
}
if (userRepository.existsByMobileNumber(mobileNumber)) {
return new LoginResponse(false, "This mobile number is already registered to another account.");
}
user = new User();
user.setEmail(cleanEmail);
user.setName(finalName);
user.setMembership(membership);
user.setMobileNumber(mobileNumber);
user.setPinHash(passwordEncoder.encode(java.util.UUID.randomUUID().toString()));
user.setLoggedIn(true);
user.setLastLoginAt(LocalDateTime.now());
userRepository.save(user);
}
LoginResponse.UserDto userDto = new LoginResponse.UserDto(
user.getId(), user.getMobileNumber(), user.getName(), user.isLoggedIn(), user.isSuspended(), user.getRitzTokenBalance(), user.getMembership()
);
return new LoginResponse(true, "Login successful.", userDto);
}
}