Skip to content

Commit

Permalink
google oauth2
Browse files Browse the repository at this point in the history
  • Loading branch information
cankurttekin committed Nov 11, 2024
1 parent 79efc47 commit 7f50514
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 1 deletion.
4 changes: 4 additions & 0 deletions backend/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.kurttekin.can.job_track.application.service;

import com.kurttekin.can.job_track.domain.model.user.User;
import com.kurttekin.can.job_track.domain.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
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.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class GoogleOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {

@Autowired
private UserService userService; // Declare UserService

@Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
// You can directly access the user attributes from the OAuth2UserRequest
Map<String, Object> attributes = userRequest.getAdditionalParameters();

// Extract necessary user info (email, name, etc.) from the attributes
String email = (String) attributes.get("email");

// Register or retrieve the user based on the Google login
User user = userService.registerGoogleUser(email);

// Create a set of authorities (roles) for the user, wrapping them in SimpleGrantedAuthority
Set<GrantedAuthority> authorities = Set.of("ROLE_USER") // Assign a default role to the user
.stream()
.map(SimpleGrantedAuthority::new) // Convert role strings to SimpleGrantedAuthority objects
.collect(Collectors.toSet());

// Create a custom OAuth2User with authorities and user attributes
return new DefaultOAuth2User(authorities, attributes, "email"); // "email" is the key for the principal
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.springframework.stereotype.Service;

import java.util.Optional;
import java.util.UUID;

@Service
public class UserServiceImpl implements UserService {
Expand Down Expand Up @@ -49,6 +50,28 @@ public void registerUser(UserRegistrationRequest userRequest) {
emailService.sendVerificationEmail(user.getEmail(), user.getUsername(), token);
}

public User registerGoogleUser(String email) {
// Create a new user instance
User user = new User();
user.setEmail(email);

// Generate a random username based on the email or name (you could use UUID or similar)
user.setUsername("google_" + UUID.randomUUID().toString());

// Set a random password (will not be used, as Google users don't log in with password)
user.setPassword(passwordEncoder.encode(UUID.randomUUID().toString()));

// Mark the user as verified since they authenticated through Google
user.setVerified(true);

// Assign a default role (e.g., "ROLE_USER")
user.setRole(Role.USER);
//user.setRoles(Collections.singleton(new Role("ROLE_USER")));

// Save the new user to the database
return userRepository.save(user);
}

@Override
public Optional<User> findUserById(Long userId) {
return userRepository.findById(userId);
Expand All @@ -59,5 +82,8 @@ public Optional<User> findUserByUsername(String username) {
return userRepository.findByUsername(username);
}


@Override
public Optional<User> findUserByEmail(String email) {
return userRepository.findByEmail(email);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,7 @@ public interface UserService {
void registerUser(UserRegistrationRequest userRegistrationRequest);
Optional<User> findUserById(Long userId);
Optional<User> findUserByUsername(String username);
Optional<User> findUserByEmail(String email);
User registerGoogleUser(String email);
}

Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ public interface UserRepository extends JpaRepository<User, Long> {
boolean existsByEmail(String email);

Optional<User> findByUsername(String username);
Optional<User> findByEmail(String email);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.kurttekin.can.job_track.infrastructure.security.config;

import com.kurttekin.can.job_track.application.service.GoogleOAuth2UserService;
import com.kurttekin.can.job_track.infrastructure.security.jwt.JwtAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
Expand All @@ -10,6 +11,9 @@
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserRequest;
import org.springframework.security.oauth2.client.userinfo.OAuth2UserService;
import org.springframework.security.oauth2.core.user.OAuth2User;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfigurationSource;
Expand Down Expand Up @@ -49,6 +53,17 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
"/swagger-ui.html"
).permitAll()
.anyRequest().authenticated()
)
.oauth2Login(oauth2Login -> oauth2Login
.authorizationEndpoint(authorization -> authorization
.baseUri("/oauth2/authorization") // Default endpoint for initiating OAuth2 login
)
.redirectionEndpoint(redirection -> redirection
.baseUri("/login/oauth2/code/*") // Google redirect URI
)
.userInfoEndpoint(userInfo -> userInfo
.userService(oAuth2UserService()) // Custom user service for processing user info
)
);
// Add JWT token filter
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
Expand All @@ -64,4 +79,10 @@ public JwtAuthenticationFilter jwtAuthenticationFilter() {
public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception {
return authenticationConfiguration.getAuthenticationManager();
}

// Custom OAuth2UserService to handle Google user info
@Bean
public OAuth2UserService<OAuth2UserRequest, OAuth2User> oAuth2UserService() {
return new GoogleOAuth2UserService();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,20 @@ public String generateToken(Authentication authentication) {
.compact();
}

// Overloaded method for generating token with username directly for Google Auth
public String generateToken(String username) {
Date now = new Date();
Date expire = new Date(now.getTime() + jwtExpirationInMs);
log.info("Creating JWT for Google user: " + username);
log.info("Signing with key: " + secretKey);
return Jwts.builder()
.subject(username)
.issuedAt(now)
.expiration(expire)
.signWith(secretKey)
.compact();
}

public String getUsernameFromToken(String token) {
Claims claims = Jwts.parser()
.verifyWith(secretKey)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,12 @@
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.web.bind.annotation.*;

import java.util.Map;
import java.util.Optional;

@RestController
@RequestMapping("/api/auth")
public class AuthController {
Expand Down Expand Up @@ -78,4 +82,24 @@ public String verifyEmail(@RequestParam("token") String token) {
boolean isValid = verificationService.verifyUser(token);
return isValid ? "Email verified successfully. You can now log in." : "Invalid or expired verification token.";
}

@GetMapping("/oauth2/callback/google")
public ResponseEntity<?> googleLogin(OAuth2AuthenticationToken authenticationToken) {
// Extract the email from the OAuth2 token
String email = authenticationToken.getPrincipal().getAttribute("email");

// Check if the user already exists
Optional<User> optionalUser = userService.findUserByEmail(email);
User user;
if (optionalUser.isEmpty()) {
// Register new user with information from Google
user = userService.registerGoogleUser(email);
} else {
user = optionalUser.get();
}

// Generate a JWT token for the authenticated user
String jwt = jwtProvider.generateToken(user.getUsername());
return ResponseEntity.ok(new JwtResponse(jwt));
}
}
9 changes: 9 additions & 0 deletions backend/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,12 @@ server.port=${PORT:8080}
#server.ssl.key-store-password=${KEY_STORE_PASS}
#server.ssl.keyStoreType=PKCS12
#server.ssl.key-alias=${KEY_STORE_ALIAS}

# GOOGLE OAUTH2
spring.security.oauth2.client.registration.google.client-id=${GOOGLE_CLIENT_ID}
spring.security.oauth2.client.registration.google.client-secret=${GOOGLE_CLIENT_SECRET}
spring.security.oauth2.client.registration.google.redirect-uri=${GOOGLE_REDIRECT_URI}
spring.security.oauth2.client.registration.google.scope=${GOOGLE_SCOPE}
spring.security.oauth2.client.provider.google.authorization-uri=${GOOGLE_AUTHORIZATION_URI}
spring.security.oauth2.client.provider.google.token-uri=${GOOGLE_TOKEN_URI}
spring.security.oauth2.client.provider.google.user-info-uri=${GOOGLE_USER_INFO_URI}

0 comments on commit 7f50514

Please sign in to comment.