CustomOAuth2UserService.java
package com.ctrlbuy.webshop.service;
import com.ctrlbuy.webshop.security.entity.User;
import com.ctrlbuy.webshop.security.repository.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.DefaultOAuth2UserService;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.user.DefaultOAuth2User;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Optional;
import java.util.UUID;
@Service
public class CustomOAuth2UserService extends DefaultOAuth2UserService {
private static final Logger logger = LoggerFactory.getLogger(CustomOAuth2UserService.class);
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
public CustomOAuth2UserService(UserRepository userRepository, @Lazy PasswordEncoder passwordEncoder) {
this.userRepository = userRepository;
this.passwordEncoder = passwordEncoder;
}
@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2User oAuth2User = super.loadUser(userRequest);
String provider = userRequest.getClientRegistration().getRegistrationId();
String providerId = oAuth2User.getAttribute("sub");
String email = oAuth2User.getAttribute("email");
String firstName = oAuth2User.getAttribute("given_name");
String lastName = oAuth2User.getAttribute("family_name");
String pictureUrl = oAuth2User.getAttribute("picture");
logger.info("OAuth2 login attempt: provider={}, email={}", provider, email);
User user = findOrCreateUser(provider, providerId, email, firstName, lastName, pictureUrl);
return new DefaultOAuth2User(
user.getAuthorities(),
oAuth2User.getAttributes(),
"sub"
);
}
private User findOrCreateUser(String provider, String providerId, String email,
String firstName, String lastName, String pictureUrl) {
// 1. Look up by OAuth provider + provider ID
Optional<User> existingByProvider = userRepository.findByOauthProviderAndOauthProviderId(provider, providerId);
if (existingByProvider.isPresent()) {
User user = existingByProvider.get();
user.setProfilePictureUrl(pictureUrl);
user.setLastLoginAt(LocalDateTime.now());
logger.info("Existing OAuth2 user logged in: {}", user.getUsername());
return userRepository.save(user);
}
// 2. Look up by email — link OAuth to existing account
Optional<User> existingByEmail = userRepository.findByEmail(email);
if (existingByEmail.isPresent()) {
User user = existingByEmail.get();
user.setOauthProvider(provider);
user.setOauthProviderId(providerId);
user.setProfilePictureUrl(pictureUrl);
user.setLastLoginAt(LocalDateTime.now());
logger.info("Linked OAuth2 provider to existing user: {}", user.getUsername());
return userRepository.save(user);
}
// 3. Create new user
String username = generateUniqueUsername(email);
User newUser = User.builder()
.username(username)
.email(email)
.password(passwordEncoder.encode(UUID.randomUUID().toString()))
.firstName(firstName != null ? firstName : "")
.lastName(lastName != null ? lastName : "")
.role(User.Role.USER)
.enabled(true)
.emailVerified(true)
.oauthProvider(provider)
.oauthProviderId(providerId)
.profilePictureUrl(pictureUrl)
.createdAt(LocalDateTime.now())
.lastLoginAt(LocalDateTime.now())
.build();
logger.info("Created new OAuth2 user: {} ({})", username, email);
return userRepository.save(newUser);
}
private String generateUniqueUsername(String email) {
String base = email.split("@")[0];
String username = base;
int counter = 1;
while (userRepository.existsByUsername(username)) {
username = base + counter;
counter++;
}
return username;
}
}