From cc1979a06896a5808ea277b6141d81f617f572c2 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 21 May 2025 10:33:30 +0200 Subject: [PATCH 1/9] feat: add GitHub OAuth2 authentication support --- ...uth2AuthenticationProcessingException.java | 9 + .../szut/casino/security/AuthController.java | 6 +- .../casino/security/GitHubController.java | 61 ++++++ .../szut/casino/security/GitHubService.java | 196 ++++++++++++++++++ .../szut/casino/security/SecurityConfig.java | 10 +- .../security/dto/GithubCallbackDto.java | 8 + .../de/szut/casino/security/jwt/JwtUtils.java | 30 ++- .../oauth2/CustomOAuth2UserService.java | 108 ++++++++++ .../security/oauth2/GitHubOAuth2UserInfo.java | 30 +++ .../OAuth2AuthenticationSuccessHandler.java | 61 ++++++ .../casino/security/oauth2/OAuth2Config.java | 15 ++ .../security/oauth2/OAuth2UserInfo.java | 23 ++ .../oauth2/OAuth2UserInfoFactory.java | 17 ++ .../casino/security/oauth2/UserPrincipal.java | 110 ++++++++++ .../casino/security/service/AuthService.java | 1 - .../de/szut/casino/user/AuthProvider.java | 6 + .../java/de/szut/casino/user/UserEntity.java | 17 ++ .../de/szut/casino/user/UserRepository.java | 2 + .../src/main/resources/application.properties | 13 ++ frontend/src/app/app.routes.ts | 7 + .../feature/auth/login/login.component.html | 20 +- .../app/feature/auth/login/login.component.ts | 6 + .../auth/oauth2/oauth2-callback.component.ts | 62 ++++++ frontend/src/app/service/auth.service.ts | 35 +++- 24 files changed, 845 insertions(+), 8 deletions(-) create mode 100644 backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/OAuth2AuthenticationProcessingException.java create mode 100644 backend/src/main/java/de/szut/casino/security/GitHubController.java create mode 100644 backend/src/main/java/de/szut/casino/security/GitHubService.java create mode 100644 backend/src/main/java/de/szut/casino/security/dto/GithubCallbackDto.java create mode 100644 backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java create mode 100644 backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java create mode 100644 backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java create mode 100644 backend/src/main/java/de/szut/casino/security/oauth2/OAuth2Config.java create mode 100644 backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfo.java create mode 100644 backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfoFactory.java create mode 100644 backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java create mode 100644 backend/src/main/java/de/szut/casino/user/AuthProvider.java create mode 100644 frontend/src/app/feature/auth/oauth2/oauth2-callback.component.ts diff --git a/backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/OAuth2AuthenticationProcessingException.java b/backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/OAuth2AuthenticationProcessingException.java new file mode 100644 index 0000000..3a1447e --- /dev/null +++ b/backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/OAuth2AuthenticationProcessingException.java @@ -0,0 +1,9 @@ +package de.szut.casino.exceptionHandling.exceptions; + +import org.springframework.security.core.AuthenticationException; + +public class OAuth2AuthenticationProcessingException extends AuthenticationException { + public OAuth2AuthenticationProcessingException(String msg) { + super(msg); + } +} \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/security/AuthController.java b/backend/src/main/java/de/szut/casino/security/AuthController.java index d22de30..27949c5 100644 --- a/backend/src/main/java/de/szut/casino/security/AuthController.java +++ b/backend/src/main/java/de/szut/casino/security/AuthController.java @@ -3,6 +3,7 @@ package de.szut.casino.security; import de.szut.casino.exceptionHandling.ErrorDetails; import de.szut.casino.exceptionHandling.exceptions.EmailNotVerifiedException; import de.szut.casino.security.dto.AuthResponseDto; +import de.szut.casino.security.dto.GithubCallbackDto; import de.szut.casino.security.dto.LoginRequestDto; import de.szut.casino.security.dto.ResetPasswordDto; import de.szut.casino.security.service.AuthService; @@ -15,14 +16,17 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; import java.io.IOException; -import java.util.Date; @RestController @RequestMapping("/auth") public class AuthController { + @Autowired private AuthService authService; + + @Autowired + private GitHubService githubService; @PostMapping("/login") public ResponseEntity authenticateUser(@Valid @RequestBody LoginRequestDto loginRequest) throws EmailNotVerifiedException { diff --git a/backend/src/main/java/de/szut/casino/security/GitHubController.java b/backend/src/main/java/de/szut/casino/security/GitHubController.java new file mode 100644 index 0000000..9c2f2bf --- /dev/null +++ b/backend/src/main/java/de/szut/casino/security/GitHubController.java @@ -0,0 +1,61 @@ +package de.szut.casino.security; + +import de.szut.casino.security.dto.AuthResponseDto; +import de.szut.casino.security.dto.GithubCallbackDto; +import de.szut.casino.security.jwt.JwtUtils; +import de.szut.casino.user.AuthProvider; +import de.szut.casino.user.UserEntity; +import de.szut.casino.user.UserRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.servlet.view.RedirectView; + +import java.math.BigDecimal; +import java.util.*; + +@RestController +@RequestMapping("/oauth2/github") +public class GitHubController { + private static final Logger logger = LoggerFactory.getLogger(GitHubController.class); + + @Value("${spring.security.oauth2.client.registration.github.client-id}") + private String clientId; + + @Value("${spring.security.oauth2.client.provider.github.authorization-uri}") + private String authorizationUri; + + @Value("${spring.security.oauth2.client.registration.github.redirect-uri}") + private String redirectUri; + + @Autowired + private GitHubService githubService; + + @GetMapping("/authorize") + public RedirectView authorizeGithub() { + logger.info("Redirecting to GitHub for authorization"); + + String authUrl = authorizationUri + + "?client_id=" + clientId + + "&redirect_uri=" + redirectUri + + "&scope=user:email,read:user"; + + return new RedirectView(authUrl); + } + + + @PostMapping("/callback") + public ResponseEntity githubCallback(@RequestBody GithubCallbackDto githubCallbackDto) { + String code = githubCallbackDto.getCode(); + AuthResponseDto response = githubService.processGithubCode(code); + return ResponseEntity.ok(response); + } +} \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/security/GitHubService.java b/backend/src/main/java/de/szut/casino/security/GitHubService.java new file mode 100644 index 0000000..d6572d8 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/security/GitHubService.java @@ -0,0 +1,196 @@ +package de.szut.casino.security; + +import de.szut.casino.security.dto.AuthResponseDto; +import de.szut.casino.security.jwt.JwtUtils; +import de.szut.casino.user.AuthProvider; +import de.szut.casino.user.UserEntity; +import de.szut.casino.user.UserRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.math.BigDecimal; +import java.util.*; + +@Service +public class GitHubService { + private static final Logger logger = LoggerFactory.getLogger(GitHubService.class); + + @Value("${spring.security.oauth2.client.registration.github.client-id}") + private String clientId; + + @Value("${spring.security.oauth2.client.registration.github.client-secret}") + private String clientSecret; + + @Autowired + private AuthenticationManager authenticationManager; + + @Autowired + private UserRepository userRepository; + + @Autowired + private JwtUtils jwtUtils; + + @Autowired + private PasswordEncoder oauth2PasswordEncoder; + + public AuthResponseDto processGithubCode(String code) { + try { + // Exchange code for access token + RestTemplate restTemplate = new RestTemplate(); + + // Create request body for token endpoint + Map requestBody = new HashMap<>(); + requestBody.put("client_id", clientId); + requestBody.put("client_secret", clientSecret); + requestBody.put("code", code); + + // Set headers + HttpHeaders headers = new HttpHeaders(); + headers.set("Accept", "application/json"); + + HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); + + // Get access token + ResponseEntity response = restTemplate.exchange( + "https://github.com/login/oauth/access_token", + HttpMethod.POST, + requestEntity, + Map.class + ); + + Map responseBody = response.getBody(); + logger.info("GitHub token response: {}", responseBody); + + // Check if there's an error in the response + if (responseBody.containsKey("error")) { + String error = (String) responseBody.get("error"); + String errorDescription = (String) responseBody.get("error_description"); + logger.error("GitHub OAuth error: {} - {}", error, errorDescription); + throw new RuntimeException("GitHub OAuth error: " + errorDescription); + } + + String accessToken = (String) responseBody.get("access_token"); + if (accessToken == null || accessToken.isEmpty()) { + logger.error("No access token received from GitHub"); + throw new RuntimeException("Failed to receive access token from GitHub"); + } + + logger.info("Received access token from GitHub"); + + // Get user info + HttpHeaders userInfoHeaders = new HttpHeaders(); + userInfoHeaders.set("Authorization", "Bearer " + accessToken); + + HttpEntity userInfoRequestEntity = new HttpEntity<>(null, userInfoHeaders); + + logger.info("Making request to GitHub API with token: {}", accessToken.substring(0, 5) + "..."); + + ResponseEntity userResponse = restTemplate.exchange( + "https://api.github.com/user", + HttpMethod.GET, + userInfoRequestEntity, + Map.class + ); + + Map userAttributes = userResponse.getBody(); + logger.info("Retrieved user info from GitHub: {}", userAttributes.get("login")); + + // Get user emails + HttpHeaders emailsHeaders = new HttpHeaders(); + emailsHeaders.set("Authorization", "Bearer " + accessToken); + + HttpEntity emailsRequestEntity = new HttpEntity<>(null, emailsHeaders); + + ResponseEntity emailsResponse = restTemplate.exchange( + "https://api.github.com/user/emails", + HttpMethod.GET, + emailsRequestEntity, + List.class + ); + + List> emails = emailsResponse.getBody(); + String email = null; + + // Find primary email + for (Map emailInfo : emails) { + Boolean primary = (Boolean) emailInfo.get("primary"); + if (primary != null && primary) { + email = (String) emailInfo.get("email"); + break; + } + } + + // If no primary email, just use the first one + if (email == null && !emails.isEmpty()) { + email = (String) emails.get(0).get("email"); + } + + logger.info("Using email: {}", email); + + // Process user data + String githubId = userAttributes.get("id").toString(); + String username = (String) userAttributes.get("login"); + + // Check if user exists by provider ID + Optional userOptional = userRepository.findByProviderId(githubId); + UserEntity user; + + if (userOptional.isPresent()) { + // Update existing user + user = userOptional.get(); + logger.info("Found existing user with providerId: {}", githubId); + } else { + // Check if email exists + userOptional = userRepository.findByEmail(email); + + if (userOptional.isPresent()) { + user = userOptional.get(); + user.setProvider(AuthProvider.GITHUB); + user.setProviderId(githubId); + logger.info("Updating existing user with email: {}", email); + } else { + // Create new user + user = new UserEntity(); + user.setEmail(email); + user.setUsername(username); + user.setProvider(AuthProvider.GITHUB); + user.setProviderId(githubId); + user.setEmailVerified(true); + + user.setBalance(new BigDecimal("1000.00")); + logger.info("Creating new user for: {}", username); + } + } + + String randomPassword = UUID.randomUUID().toString(); + user.setPassword(oauth2PasswordEncoder.encode(randomPassword)); + + userRepository.save(user); + + Authentication authentication = this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), randomPassword)); + + // Generate JWT token + String token = jwtUtils.generateToken(authentication); + logger.info("Generated JWT token"); + + return new AuthResponseDto(token); + + } catch (Exception e) { + logger.error("Error processing GitHub code", e); + throw new RuntimeException("Failed to process GitHub authentication", e); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/security/SecurityConfig.java b/backend/src/main/java/de/szut/casino/security/SecurityConfig.java index 155de8d..3efbec7 100644 --- a/backend/src/main/java/de/szut/casino/security/SecurityConfig.java +++ b/backend/src/main/java/de/szut/casino/security/SecurityConfig.java @@ -1,6 +1,8 @@ package de.szut.casino.security; import de.szut.casino.security.jwt.JwtAuthenticationFilter; +import de.szut.casino.security.oauth2.CustomOAuth2UserService; +import de.szut.casino.security.oauth2.OAuth2AuthenticationSuccessHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -31,12 +33,16 @@ public class SecurityConfig { @Value("${app.frontend-host}") private String frontendHost; + + @Value("${app.oauth2.authorizedRedirectUris}") + private String authorizedRedirectUri; @Autowired private UserDetailsService userDetailsService; @Autowired private JwtAuthenticationFilter jwtAuthenticationFilter; + @Bean public DaoAuthenticationProvider authenticationProvider() { @@ -65,10 +71,12 @@ public class SecurityConfig { .csrf(csrf -> csrf.disable()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> { - auth.requestMatchers("/auth/**", "/webhook", "/swagger/**", "/swagger-ui/**", "/health", "/error").permitAll() + auth.requestMatchers("/auth/**", "/webhook", "/swagger/**", "/swagger-ui/**", "/health", "/error", "/oauth2/**").permitAll() .requestMatchers(org.springframework.http.HttpMethod.OPTIONS, "/**").permitAll() .anyRequest().authenticated(); }) + // Disable Spring's built-in OAuth2 login since we're implementing a custom flow + // We're using our own GitHubController for OAuth2 login .authenticationProvider(authenticationProvider()) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); diff --git a/backend/src/main/java/de/szut/casino/security/dto/GithubCallbackDto.java b/backend/src/main/java/de/szut/casino/security/dto/GithubCallbackDto.java new file mode 100644 index 0000000..7d0315d --- /dev/null +++ b/backend/src/main/java/de/szut/casino/security/dto/GithubCallbackDto.java @@ -0,0 +1,8 @@ +package de.szut.casino.security.dto; + +import lombok.Data; + +@Data +public class GithubCallbackDto { + private String code; +} \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/security/jwt/JwtUtils.java b/backend/src/main/java/de/szut/casino/security/jwt/JwtUtils.java index 3c5ef86..1a7d08d 100644 --- a/backend/src/main/java/de/szut/casino/security/jwt/JwtUtils.java +++ b/backend/src/main/java/de/szut/casino/security/jwt/JwtUtils.java @@ -1,12 +1,16 @@ package de.szut.casino.security.jwt; +import de.szut.casino.security.oauth2.UserPrincipal; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import io.jsonwebtoken.security.Keys; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Component; import java.security.Key; @@ -17,6 +21,7 @@ import java.util.function.Function; @Component public class JwtUtils { + private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class); @Value("${jwt.secret}") private String jwtSecret; @@ -29,8 +34,26 @@ public class JwtUtils { } public String generateToken(Authentication authentication) { - UserDetails userDetails = (UserDetails) authentication.getPrincipal(); - return generateToken(userDetails.getUsername()); + String subject = null; + Map claims = new HashMap<>(); + + if (authentication.getPrincipal() instanceof UserPrincipal) { + UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal(); + subject = userPrincipal.getEmail(); + claims.put("id", userPrincipal.getId()); + claims.put("username", userPrincipal.getDisplayUsername()); + logger.info("Generating token for UserPrincipal: {}", subject); + } else if (authentication.getPrincipal() instanceof OAuth2User) { + OAuth2User oauth2User = (OAuth2User) authentication.getPrincipal(); + subject = (String) oauth2User.getAttributes().get("email"); + logger.info("Generating token for OAuth2User: {}", subject); + } else { + UserDetails userDetails = (UserDetails) authentication.getPrincipal(); + subject = userDetails.getUsername(); + logger.info("Generating token for UserDetails: {}", subject); + } + + return createToken(claims, subject); } public String generateToken(String username) { @@ -40,6 +63,9 @@ public class JwtUtils { private String createToken(Map claims, String subject) { Date now = new Date(); + logger.info("now: {}", now); + logger.info("jwtExpirationMs: {}", jwtExpirationMs); + logger.info("expiryDate: {}", new Date(now.getTime() + jwtExpirationMs)); Date expiryDate = new Date(now.getTime() + jwtExpirationMs); return Jwts.builder() diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java b/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java new file mode 100644 index 0000000..51f52d8 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java @@ -0,0 +1,108 @@ +package de.szut.casino.security.oauth2; + +import de.szut.casino.exceptionHandling.exceptions.OAuth2AuthenticationProcessingException; +import de.szut.casino.user.AuthProvider; +import de.szut.casino.user.UserEntity; +import de.szut.casino.user.UserRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.InternalAuthenticationServiceException; +import org.springframework.security.core.AuthenticationException; +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.OAuth2User; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import java.math.BigDecimal; +import java.util.Optional; +import java.util.UUID; + +@Service +public class CustomOAuth2UserService extends DefaultOAuth2UserService { + + @Autowired + private UserRepository userRepository; + + @Autowired + private PasswordEncoder oauth2PasswordEncoder; + + @Override + public OAuth2User loadUser(OAuth2UserRequest oAuth2UserRequest) throws OAuth2AuthenticationException { + OAuth2User oAuth2User = super.loadUser(oAuth2UserRequest); + + try { + return processOAuth2User(oAuth2UserRequest, oAuth2User); + } catch (AuthenticationException ex) { + throw ex; + } catch (Exception ex) { + throw new InternalAuthenticationServiceException(ex.getMessage(), ex.getCause()); + } + } + + private OAuth2User processOAuth2User(OAuth2UserRequest oAuth2UserRequest, OAuth2User oAuth2User) { + String registrationId = oAuth2UserRequest.getClientRegistration().getRegistrationId(); + OAuth2UserInfo oAuth2UserInfo = OAuth2UserInfoFactory.getOAuth2UserInfo(registrationId, oAuth2User.getAttributes()); + + // For GitHub, the email might not be directly available in attributes + String email = oAuth2UserInfo.getEmail(); + if (StringUtils.isEmpty(email)) { + email = oAuth2UserInfo.getName() + "@github.user"; + } + + Optional userOptional = userRepository.findByEmail(email); + UserEntity user; + + if(userOptional.isPresent()) { + user = userOptional.get(); + + if(!user.getProvider().equals(AuthProvider.valueOf(registrationId.toUpperCase()))) { + throw new OAuth2AuthenticationProcessingException("You're signed up with " + + user.getProvider() + ". Please use your " + user.getProvider() + + " account to login."); + } + + user = updateExistingUser(user, oAuth2UserInfo); + } else { + user = registerNewUser(oAuth2UserRequest, oAuth2UserInfo, email); + } + + return UserPrincipal.create(user, oAuth2User.getAttributes()); + } + + private UserEntity registerNewUser(OAuth2UserRequest oAuth2UserRequest, OAuth2UserInfo oAuth2UserInfo, String email) { + UserEntity user = new UserEntity(); + + String username = oAuth2UserInfo.getName(); + if (StringUtils.isEmpty(username)) { + username = "github_" + oAuth2UserInfo.getId(); + } + + // Check if username already exists and append a suffix if needed + if (userRepository.findByUsername(username).isPresent()) { + username = username + "_" + UUID.randomUUID().toString().substring(0, 8); + } + + user.setProvider(AuthProvider.valueOf(oAuth2UserRequest.getClientRegistration().getRegistrationId().toUpperCase())); + user.setProviderId(oAuth2UserInfo.getId()); + user.setUsername(username); + user.setEmail(email); + user.setEmailVerified(true); + + // Generate a random password for OAuth users (they won't use it) + String randomPassword = UUID.randomUUID().toString(); + user.setPassword(oauth2PasswordEncoder.encode(randomPassword)); + + user.setBalance(new BigDecimal("1000.00")); // Starting balance + + return userRepository.save(user); + } + + private UserEntity updateExistingUser(UserEntity existingUser, OAuth2UserInfo oAuth2UserInfo) { + if (!StringUtils.isEmpty(oAuth2UserInfo.getName())) { + existingUser.setUsername(oAuth2UserInfo.getName()); + } + return userRepository.save(existingUser); + } +} \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java b/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java new file mode 100644 index 0000000..2646956 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java @@ -0,0 +1,30 @@ +package de.szut.casino.security.oauth2; + +import java.util.Map; + +public class GitHubOAuth2UserInfo extends OAuth2UserInfo { + + public GitHubOAuth2UserInfo(Map attributes) { + super(attributes); + } + + @Override + public String getId() { + return ((Integer) attributes.get("id")).toString(); + } + + @Override + public String getName() { + return (String) attributes.get("name"); + } + + @Override + public String getEmail() { + return (String) attributes.get("email"); + } + + @Override + public String getImageUrl() { + return (String) attributes.get("avatar_url"); + } +} \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java new file mode 100644 index 0000000..00d0b3e --- /dev/null +++ b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java @@ -0,0 +1,61 @@ +package de.szut.casino.security.oauth2; + +import de.szut.casino.security.jwt.JwtUtils; +import de.szut.casino.user.UserEntity; +import de.szut.casino.user.UserRepository; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; +import org.springframework.stereotype.Component; +import org.springframework.web.util.UriComponentsBuilder; + +import java.io.IOException; + +@Component +public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { + private static final Logger logger = LoggerFactory.getLogger(OAuth2AuthenticationSuccessHandler.class); + + @Value("${app.oauth2.authorizedRedirectUris}") + private String redirectUri; + + @Autowired + private JwtUtils jwtUtils; + + @Autowired + private UserRepository userRepository; + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException { + String targetUrl = determineTargetUrl(authentication); + + logger.info("OAuth2 Authentication successful, redirecting to: {}", targetUrl); + + if (response.isCommitted()) { + logger.debug("Response has already been committed. Unable to redirect to " + targetUrl); + return; + } + + clearAuthenticationAttributes(request); + getRedirectStrategy().sendRedirect(request, response, targetUrl); + } + + private String determineTargetUrl(Authentication authentication) { + String token = jwtUtils.generateToken(authentication); + + if (authentication.getPrincipal() instanceof UserPrincipal) { + UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal(); + logger.info("User authenticated: ID={}, Email={}", userPrincipal.getId(), userPrincipal.getEmail()); + } + + return UriComponentsBuilder.fromUriString(redirectUri) + .queryParam("token", token) + .build().toUriString(); + } +} \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2Config.java b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2Config.java new file mode 100644 index 0000000..224d9c0 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2Config.java @@ -0,0 +1,15 @@ +package de.szut.casino.security.oauth2; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; + +@Configuration +public class OAuth2Config { + + @Bean + public PasswordEncoder oauth2PasswordEncoder() { + return new BCryptPasswordEncoder(); + } +} \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfo.java b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfo.java new file mode 100644 index 0000000..3acd762 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfo.java @@ -0,0 +1,23 @@ +package de.szut.casino.security.oauth2; + +import java.util.Map; + +public abstract class OAuth2UserInfo { + protected Map attributes; + + public OAuth2UserInfo(Map attributes) { + this.attributes = attributes; + } + + public Map getAttributes() { + return attributes; + } + + public abstract String getId(); + + public abstract String getName(); + + public abstract String getEmail(); + + public abstract String getImageUrl(); +} \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfoFactory.java b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfoFactory.java new file mode 100644 index 0000000..0d3c068 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfoFactory.java @@ -0,0 +1,17 @@ +package de.szut.casino.security.oauth2; + +import de.szut.casino.exceptionHandling.exceptions.OAuth2AuthenticationProcessingException; +import de.szut.casino.user.AuthProvider; + +import java.util.Map; + +public class OAuth2UserInfoFactory { + + public static OAuth2UserInfo getOAuth2UserInfo(String registrationId, Map attributes) { + if(registrationId.equalsIgnoreCase(AuthProvider.GITHUB.toString())) { + return new GitHubOAuth2UserInfo(attributes); + } else { + throw new OAuth2AuthenticationProcessingException("Sorry! Login with " + registrationId + " is not supported yet."); + } + } +} \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java b/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java new file mode 100644 index 0000000..113b945 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java @@ -0,0 +1,110 @@ +package de.szut.casino.security.oauth2; + +import de.szut.casino.user.UserEntity; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.oauth2.core.user.OAuth2User; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class UserPrincipal implements OAuth2User, UserDetails { + private Long id; + private String email; + private String username; + private String password; + private Collection authorities; + private Map attributes; + + public UserPrincipal(Long id, String email, String username, String password, Collection authorities) { + this.id = id; + this.email = email; + this.username = username; + this.password = password; + this.authorities = authorities; + } + + public static UserPrincipal create(UserEntity user) { + List authorities = Collections. + singletonList(new SimpleGrantedAuthority("ROLE_USER")); + + return new UserPrincipal( + user.getId(), + user.getEmail(), + user.getUsername(), + user.getPassword(), + authorities + ); + } + + public static UserPrincipal create(UserEntity user, Map attributes) { + UserPrincipal userPrincipal = UserPrincipal.create(user); + userPrincipal.setAttributes(attributes); + return userPrincipal; + } + + public Long getId() { + return id; + } + + public String getEmail() { + return email; + } + + @Override + public String getPassword() { + return password; + } + + @Override + public String getUsername() { + // We're using email as the username for authentication + return email; + } + + public String getDisplayUsername() { + return username; + } + + @Override + public boolean isAccountNonExpired() { + return true; + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public Collection getAuthorities() { + return authorities; + } + + @Override + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + @Override + public String getName() { + return String.valueOf(id); + } +} \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/security/service/AuthService.java b/backend/src/main/java/de/szut/casino/security/service/AuthService.java index 959a55a..ffe06ae 100644 --- a/backend/src/main/java/de/szut/casino/security/service/AuthService.java +++ b/backend/src/main/java/de/szut/casino/security/service/AuthService.java @@ -19,7 +19,6 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; -import javax.swing.text.html.Option; import java.io.IOException; import java.util.Optional; diff --git a/backend/src/main/java/de/szut/casino/user/AuthProvider.java b/backend/src/main/java/de/szut/casino/user/AuthProvider.java new file mode 100644 index 0000000..d7c7e05 --- /dev/null +++ b/backend/src/main/java/de/szut/casino/user/AuthProvider.java @@ -0,0 +1,6 @@ +package de.szut.casino.user; + +public enum AuthProvider { + LOCAL, + GITHUB +} \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/user/UserEntity.java b/backend/src/main/java/de/szut/casino/user/UserEntity.java index 2190867..fa53b9a 100644 --- a/backend/src/main/java/de/szut/casino/user/UserEntity.java +++ b/backend/src/main/java/de/szut/casino/user/UserEntity.java @@ -2,6 +2,8 @@ package de.szut.casino.user; import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import lombok.Getter; @@ -35,6 +37,11 @@ public class UserEntity { private String verificationToken; private String passwordResetToken; + + @Enumerated(EnumType.STRING) + private AuthProvider provider = AuthProvider.LOCAL; + + private String providerId; public UserEntity(String email, String username, String password, BigDecimal balance, String verificationToken) { this.email = email; @@ -42,6 +49,16 @@ public class UserEntity { this.password = password; this.balance = balance; this.verificationToken = verificationToken; + this.provider = AuthProvider.LOCAL; + } + + public UserEntity(String email, String username, AuthProvider provider, String providerId, BigDecimal balance) { + this.email = email; + this.username = username; + this.provider = provider; + this.providerId = providerId; + this.balance = balance; + this.emailVerified = true; // OAuth providers verify emails } public void addBalance(BigDecimal amountToAdd) { diff --git a/backend/src/main/java/de/szut/casino/user/UserRepository.java b/backend/src/main/java/de/szut/casino/user/UserRepository.java index 29790f5..36673b1 100644 --- a/backend/src/main/java/de/szut/casino/user/UserRepository.java +++ b/backend/src/main/java/de/szut/casino/user/UserRepository.java @@ -12,6 +12,8 @@ public interface UserRepository extends JpaRepository { Optional findByEmail(String email); + Optional findByProviderId(String providerId); + boolean existsByUsername(String username); boolean existsByEmail(String email); diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties index 0eaca18..e583c50 100644 --- a/backend/src/main/resources/application.properties +++ b/backend/src/main/resources/application.properties @@ -28,3 +28,16 @@ logging.level.org.springframework.security=DEBUG springdoc.swagger-ui.path=swagger springdoc.swagger-ui.try-it-out-enabled=true +# GitHub OAuth2 Configuration +spring.security.oauth2.client.registration.github.client-id=${GITHUB_CLIENT_ID:Ov23lingzZsPn1wwACoK} +spring.security.oauth2.client.registration.github.client-secret=${GITHUB_CLIENT_SECRET:4b327fb3b1ab67584a03bcb9d53fa6439fbccad7} +spring.security.oauth2.client.registration.github.redirect-uri=${app.frontend-host}/oauth2/callback/github +spring.security.oauth2.client.registration.github.scope=user:email,read:user +spring.security.oauth2.client.provider.github.authorization-uri=https://github.com/login/oauth/authorize +spring.security.oauth2.client.provider.github.token-uri=https://github.com/login/oauth/access_token +spring.security.oauth2.client.provider.github.user-info-uri=https://api.github.com/user +spring.security.oauth2.client.provider.github.user-name-attribute=login + +# OAuth Success and Failure URLs +app.oauth2.authorizedRedirectUris=${app.frontend-host}/auth/oauth2/callback + diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index b4bf818..39c5b1c 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -33,6 +33,13 @@ export const routes: Routes = [ (m) => m.RecoverPasswordComponent ), }, + { + path: 'oauth2/callback/github', + loadComponent: () => + import('./feature/auth/oauth2/oauth2-callback.component').then( + (m) => m.OAuth2CallbackComponent + ), + }, { path: 'game/blackjack', loadComponent: () => import('./feature/game/blackjack/blackjack.component'), diff --git a/frontend/src/app/feature/auth/login/login.component.html b/frontend/src/app/feature/auth/login/login.component.html index 04afd42..fee6ce9 100644 --- a/frontend/src/app/feature/auth/login/login.component.html +++ b/frontend/src/app/feature/auth/login/login.component.html @@ -82,8 +82,26 @@ + +
+
+ ODER +
+
+ +
+ +
-
+

Passwort vergessen?

+ `, +}) +export class OAuth2CallbackComponent implements OnInit { + error: string | null = null; + + constructor( + private route: ActivatedRoute, + private router: Router, + private authService: AuthService + ) {} + + ngOnInit(): void { + // Check for code in URL params + this.route.queryParams.subscribe(params => { + const code = params['code']; + + if (code) { + // Exchange GitHub code for a JWT token + this.authService.githubAuth(code).subscribe({ + next: () => { + // Redirect to home after successful authentication + this.router.navigate(['/home']); + }, + error: (err) => { + console.error('GitHub authentication error:', err); + this.error = err.error?.message || "Authentication failed. Please try again."; + console.log('Error details:', err); + + // Redirect back to landing page after showing error + setTimeout(() => { + this.router.navigate(['/']); + }, 3000); + } + }); + } else { + this.error = "Authentication failed. No authorization code received."; + + // Redirect back to landing page after showing error + setTimeout(() => { + this.router.navigate(['/']); + }, 3000); + } + }); + } +} diff --git a/frontend/src/app/service/auth.service.ts b/frontend/src/app/service/auth.service.ts index f1cfbe9..c517d88 100644 --- a/frontend/src/app/service/auth.service.ts +++ b/frontend/src/app/service/auth.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { BehaviorSubject, Observable, tap } from 'rxjs'; -import { Router } from '@angular/router'; +import { Router, ActivatedRoute } from '@angular/router'; import { LoginRequest } from '../model/auth/LoginRequest'; import { RegisterRequest } from '../model/auth/RegisterRequest'; import { AuthResponse } from '../model/auth/AuthResponse'; @@ -17,20 +17,41 @@ const USER_KEY = 'user'; export class AuthService { private authUrl = `${environment.apiUrl}/auth`; private userUrl = `${environment.apiUrl}/users`; + private oauthUrl = `${environment.apiUrl}/oauth2`; userSubject: BehaviorSubject; constructor( private http: HttpClient, - private router: Router + private router: Router, + private route: ActivatedRoute ) { this.userSubject = new BehaviorSubject(this.getUserFromStorage()); + // Check for token in URL (OAuth callback) on initialization + this.route.queryParams.subscribe(params => { + const token = params['token']; + if (token) { + this.handleOAuthCallback(token); + } + }); + if (this.getToken()) { this.loadCurrentUser(); } } + private handleOAuthCallback(token: string): void { + this.setToken(token); + this.loadCurrentUser(); + // Clean up the URL by removing the token + this.router.navigate([], { + relativeTo: this.route, + queryParams: {}, + replaceUrl: true + }); + } + public get currentUserValue(): User | null { return this.userSubject.value; } @@ -48,6 +69,16 @@ export class AuthService { return this.http.post(`${this.authUrl}/register`, registerRequest); } + githubAuth(code: string): Observable { + return this.http.post(`${this.oauthUrl}/github/callback`, { code }).pipe( + tap((response) => { + console.log(response.token); + this.setToken(response.token); + this.loadCurrentUser(); + }) + ); + } + logout(): void { localStorage.removeItem(TOKEN_KEY); localStorage.removeItem(USER_KEY); -- 2.47.2 From 74798949c652836c8735bff1740dc15f67ab66d3 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 21 May 2025 10:33:58 +0200 Subject: [PATCH 2/9] style: clean up whitespace in multiple files --- .../de/szut/casino/CasinoApplication.java | 3 -- .../casino/blackjack/BlackJackService.java | 1 - .../casino/config/OpenAPIConfiguration.java | 1 - .../java/de/szut/casino/config/WebConfig.java | 12 ++--- .../casino/deposit/DepositController.java | 4 +- .../exceptions/EmailNotVerifiedException.java | 2 - .../szut/casino/security/AuthController.java | 12 ++--- .../de/szut/casino/security/CorsFilter.java | 8 +-- .../CustomJwtAuthenticationConverter.java | 2 +- .../casino/security/GitHubController.java | 22 ++------ .../szut/casino/security/GitHubService.java | 51 +++++++++---------- .../szut/casino/security/SecurityConfig.java | 32 ++++++------ .../casino/security/dto/AuthResponseDto.java | 2 +- .../casino/security/dto/LoginRequestDto.java | 2 +- .../security/jwt/JwtAuthenticationFilter.java | 4 +- .../oauth2/CustomOAuth2UserService.java | 28 +++++----- .../security/oauth2/GitHubOAuth2UserInfo.java | 2 +- .../OAuth2AuthenticationSuccessHandler.java | 9 ++-- .../oauth2/OAuth2UserInfoFactory.java | 4 +- .../casino/security/oauth2/UserPrincipal.java | 2 +- .../service/UserDetailsServiceImpl.java | 12 ++--- .../java/de/szut/casino/user/UserEntity.java | 21 +++----- .../szut/casino/user/UserMappingService.java | 2 +- .../de/szut/casino/user/UserRepository.java | 8 +-- .../java/de/szut/casino/user/UserService.java | 16 +++--- .../szut/casino/user/dto/CreateUserDto.java | 4 +- .../transaction/TransactionController.java | 2 +- 27 files changed, 119 insertions(+), 149 deletions(-) diff --git a/backend/src/main/java/de/szut/casino/CasinoApplication.java b/backend/src/main/java/de/szut/casino/CasinoApplication.java index 9a5db34..9e1f3b2 100644 --- a/backend/src/main/java/de/szut/casino/CasinoApplication.java +++ b/backend/src/main/java/de/szut/casino/CasinoApplication.java @@ -8,9 +8,6 @@ import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; -import org.springframework.mail.MailException; -import org.springframework.mail.MailSender; -import org.springframework.mail.SimpleMailMessage; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.web.client.RestTemplate; diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java index 9bf591f..c417309 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java @@ -112,7 +112,6 @@ public class BlackJackService { dealCardToPlayer(game); dealCardToSplitHand(game); - return blackJackGameRepository.save(game); } private BlackJackGameEntity processGameBasedOnState(BlackJackGameEntity game) { diff --git a/backend/src/main/java/de/szut/casino/config/OpenAPIConfiguration.java b/backend/src/main/java/de/szut/casino/config/OpenAPIConfiguration.java index 7af90b9..75cac52 100644 --- a/backend/src/main/java/de/szut/casino/config/OpenAPIConfiguration.java +++ b/backend/src/main/java/de/szut/casino/config/OpenAPIConfiguration.java @@ -1,7 +1,6 @@ package de.szut.casino.config; - import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.info.Info; diff --git a/backend/src/main/java/de/szut/casino/config/WebConfig.java b/backend/src/main/java/de/szut/casino/config/WebConfig.java index bb11293..ecc21be 100644 --- a/backend/src/main/java/de/szut/casino/config/WebConfig.java +++ b/backend/src/main/java/de/szut/casino/config/WebConfig.java @@ -18,12 +18,12 @@ public class WebConfig { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") - .allowedOrigins(frontendHost) - .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") - .allowedHeaders("*") - .exposedHeaders("*") - .allowCredentials(true) - .maxAge(3600); + .allowedOrigins(frontendHost) + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*") + .exposedHeaders("*") + .allowCredentials(true) + .maxAge(3600); } }; } diff --git a/backend/src/main/java/de/szut/casino/deposit/DepositController.java b/backend/src/main/java/de/szut/casino/deposit/DepositController.java index 522e47d..e64e428 100644 --- a/backend/src/main/java/de/szut/casino/deposit/DepositController.java +++ b/backend/src/main/java/de/szut/casino/deposit/DepositController.java @@ -53,8 +53,8 @@ public class DepositController { .build()) .setQuantity(1L) .build()) - .setSuccessUrl(frontendHost+"/home?success=true") - .setCancelUrl(frontendHost+"/home?success=false") + .setSuccessUrl(frontendHost + "/home?success=true") + .setCancelUrl(frontendHost + "/home?success=false") .setMode(SessionCreateParams.Mode.PAYMENT) .build(); diff --git a/backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/EmailNotVerifiedException.java b/backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/EmailNotVerifiedException.java index ea08367..af97d4e 100644 --- a/backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/EmailNotVerifiedException.java +++ b/backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/EmailNotVerifiedException.java @@ -1,7 +1,5 @@ package de.szut.casino.exceptionHandling.exceptions; -import de.szut.casino.security.service.EmailService; - public class EmailNotVerifiedException extends Exception { public EmailNotVerifiedException() { super("Email not verified"); diff --git a/backend/src/main/java/de/szut/casino/security/AuthController.java b/backend/src/main/java/de/szut/casino/security/AuthController.java index 27949c5..13a238e 100644 --- a/backend/src/main/java/de/szut/casino/security/AuthController.java +++ b/backend/src/main/java/de/szut/casino/security/AuthController.java @@ -1,9 +1,7 @@ package de.szut.casino.security; -import de.szut.casino.exceptionHandling.ErrorDetails; import de.szut.casino.exceptionHandling.exceptions.EmailNotVerifiedException; import de.szut.casino.security.dto.AuthResponseDto; -import de.szut.casino.security.dto.GithubCallbackDto; import de.szut.casino.security.dto.LoginRequestDto; import de.szut.casino.security.dto.ResetPasswordDto; import de.szut.casino.security.service.AuthService; @@ -24,7 +22,7 @@ public class AuthController { @Autowired private AuthService authService; - + @Autowired private GitHubService githubService; @@ -42,11 +40,11 @@ public class AuthController { @PostMapping("/verify") public ResponseEntity verifyEmail(@RequestParam("token") String token) throws MessagingException, IOException { - if (authService.verifyEmail(token)) { - return ResponseEntity.badRequest().build(); - } + if (authService.verifyEmail(token)) { + return ResponseEntity.badRequest().build(); + } - return ResponseEntity.ok().build(); + return ResponseEntity.ok().build(); } @PostMapping("/recover-password") diff --git a/backend/src/main/java/de/szut/casino/security/CorsFilter.java b/backend/src/main/java/de/szut/casino/security/CorsFilter.java index 032088f..446864e 100644 --- a/backend/src/main/java/de/szut/casino/security/CorsFilter.java +++ b/backend/src/main/java/de/szut/casino/security/CorsFilter.java @@ -19,10 +19,10 @@ public class CorsFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { - + HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req; - + // Allow requests from the frontend response.setHeader("Access-Control-Allow-Origin", frontendHost); response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS"); @@ -30,12 +30,12 @@ public class CorsFilter implements Filter { response.setHeader("Access-Control-Expose-Headers", "*"); response.setHeader("Access-Control-Allow-Credentials", "true"); response.setHeader("Access-Control-Max-Age", "3600"); - + if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); return; } - + chain.doFilter(req, res); } } diff --git a/backend/src/main/java/de/szut/casino/security/CustomJwtAuthenticationConverter.java b/backend/src/main/java/de/szut/casino/security/CustomJwtAuthenticationConverter.java index 9f5304e..1997ac7 100644 --- a/backend/src/main/java/de/szut/casino/security/CustomJwtAuthenticationConverter.java +++ b/backend/src/main/java/de/szut/casino/security/CustomJwtAuthenticationConverter.java @@ -7,7 +7,7 @@ import org.springframework.security.oauth2.server.resource.authentication.JwtAut import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; public class CustomJwtAuthenticationConverter implements Converter { - + @Override public AbstractAuthenticationToken convert(Jwt source) { JwtGrantedAuthoritiesConverter authoritiesConverter = new JwtGrantedAuthoritiesConverter(); diff --git a/backend/src/main/java/de/szut/casino/security/GitHubController.java b/backend/src/main/java/de/szut/casino/security/GitHubController.java index 9c2f2bf..ea1f23e 100644 --- a/backend/src/main/java/de/szut/casino/security/GitHubController.java +++ b/backend/src/main/java/de/szut/casino/security/GitHubController.java @@ -2,26 +2,14 @@ package de.szut.casino.security; import de.szut.casino.security.dto.AuthResponseDto; import de.szut.casino.security.dto.GithubCallbackDto; -import de.szut.casino.security.jwt.JwtUtils; -import de.szut.casino.user.AuthProvider; -import de.szut.casino.user.UserEntity; -import de.szut.casino.user.UserRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; -import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; -import org.springframework.web.client.RestTemplate; import org.springframework.web.servlet.view.RedirectView; -import java.math.BigDecimal; -import java.util.*; - @RestController @RequestMapping("/oauth2/github") public class GitHubController { @@ -42,12 +30,12 @@ public class GitHubController { @GetMapping("/authorize") public RedirectView authorizeGithub() { logger.info("Redirecting to GitHub for authorization"); - - String authUrl = authorizationUri + - "?client_id=" + clientId + - "&redirect_uri=" + redirectUri + + + String authUrl = authorizationUri + + "?client_id=" + clientId + + "&redirect_uri=" + redirectUri + "&scope=user:email,read:user"; - + return new RedirectView(authUrl); } diff --git a/backend/src/main/java/de/szut/casino/security/GitHubService.java b/backend/src/main/java/de/szut/casino/security/GitHubService.java index d6572d8..9ef8274 100644 --- a/backend/src/main/java/de/szut/casino/security/GitHubService.java +++ b/backend/src/main/java/de/szut/casino/security/GitHubService.java @@ -16,7 +16,6 @@ import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @@ -50,19 +49,19 @@ public class GitHubService { try { // Exchange code for access token RestTemplate restTemplate = new RestTemplate(); - + // Create request body for token endpoint Map requestBody = new HashMap<>(); requestBody.put("client_id", clientId); requestBody.put("client_secret", clientSecret); requestBody.put("code", code); - + // Set headers HttpHeaders headers = new HttpHeaders(); headers.set("Accept", "application/json"); - + HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); - + // Get access token ResponseEntity response = restTemplate.exchange( "https://github.com/login/oauth/access_token", @@ -70,10 +69,10 @@ public class GitHubService { requestEntity, Map.class ); - + Map responseBody = response.getBody(); logger.info("GitHub token response: {}", responseBody); - + // Check if there's an error in the response if (responseBody.containsKey("error")) { String error = (String) responseBody.get("error"); @@ -81,49 +80,49 @@ public class GitHubService { logger.error("GitHub OAuth error: {} - {}", error, errorDescription); throw new RuntimeException("GitHub OAuth error: " + errorDescription); } - + String accessToken = (String) responseBody.get("access_token"); if (accessToken == null || accessToken.isEmpty()) { logger.error("No access token received from GitHub"); throw new RuntimeException("Failed to receive access token from GitHub"); } - + logger.info("Received access token from GitHub"); - + // Get user info HttpHeaders userInfoHeaders = new HttpHeaders(); userInfoHeaders.set("Authorization", "Bearer " + accessToken); HttpEntity userInfoRequestEntity = new HttpEntity<>(null, userInfoHeaders); - + logger.info("Making request to GitHub API with token: {}", accessToken.substring(0, 5) + "..."); - + ResponseEntity userResponse = restTemplate.exchange( "https://api.github.com/user", HttpMethod.GET, userInfoRequestEntity, Map.class ); - + Map userAttributes = userResponse.getBody(); logger.info("Retrieved user info from GitHub: {}", userAttributes.get("login")); - + // Get user emails HttpHeaders emailsHeaders = new HttpHeaders(); emailsHeaders.set("Authorization", "Bearer " + accessToken); HttpEntity emailsRequestEntity = new HttpEntity<>(null, emailsHeaders); - + ResponseEntity emailsResponse = restTemplate.exchange( "https://api.github.com/user/emails", HttpMethod.GET, emailsRequestEntity, List.class ); - + List> emails = emailsResponse.getBody(); String email = null; - + // Find primary email for (Map emailInfo : emails) { Boolean primary = (Boolean) emailInfo.get("primary"); @@ -132,22 +131,22 @@ public class GitHubService { break; } } - + // If no primary email, just use the first one if (email == null && !emails.isEmpty()) { email = (String) emails.get(0).get("email"); } - + logger.info("Using email: {}", email); - + // Process user data String githubId = userAttributes.get("id").toString(); String username = (String) userAttributes.get("login"); - + // Check if user exists by provider ID Optional userOptional = userRepository.findByProviderId(githubId); UserEntity user; - + if (userOptional.isPresent()) { // Update existing user user = userOptional.get(); @@ -155,7 +154,7 @@ public class GitHubService { } else { // Check if email exists userOptional = userRepository.findByEmail(email); - + if (userOptional.isPresent()) { user = userOptional.get(); user.setProvider(AuthProvider.GITHUB); @@ -169,7 +168,7 @@ public class GitHubService { user.setProvider(AuthProvider.GITHUB); user.setProviderId(githubId); user.setEmailVerified(true); - + user.setBalance(new BigDecimal("1000.00")); logger.info("Creating new user for: {}", username); } @@ -177,7 +176,7 @@ public class GitHubService { String randomPassword = UUID.randomUUID().toString(); user.setPassword(oauth2PasswordEncoder.encode(randomPassword)); - + userRepository.save(user); Authentication authentication = this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), randomPassword)); @@ -187,7 +186,7 @@ public class GitHubService { logger.info("Generated JWT token"); return new AuthResponseDto(token); - + } catch (Exception e) { logger.error("Error processing GitHub code", e); throw new RuntimeException("Failed to process GitHub authentication", e); diff --git a/backend/src/main/java/de/szut/casino/security/SecurityConfig.java b/backend/src/main/java/de/szut/casino/security/SecurityConfig.java index 3efbec7..9b3c880 100644 --- a/backend/src/main/java/de/szut/casino/security/SecurityConfig.java +++ b/backend/src/main/java/de/szut/casino/security/SecurityConfig.java @@ -1,8 +1,6 @@ package de.szut.casino.security; import de.szut.casino.security.jwt.JwtAuthenticationFilter; -import de.szut.casino.security.oauth2.CustomOAuth2UserService; -import de.szut.casino.security.oauth2.OAuth2AuthenticationSuccessHandler; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; @@ -33,7 +31,7 @@ public class SecurityConfig { @Value("${app.frontend-host}") private String frontendHost; - + @Value("${app.oauth2.authorizedRedirectUris}") private String authorizedRedirectUri; @@ -42,15 +40,15 @@ public class SecurityConfig { @Autowired private JwtAuthenticationFilter jwtAuthenticationFilter; - + @Bean public DaoAuthenticationProvider authenticationProvider() { DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(); - + authProvider.setUserDetailsService(userDetailsService); authProvider.setPasswordEncoder(passwordEncoder()); - + return authProvider; } @@ -67,18 +65,18 @@ public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http - .cors(cors -> cors.configurationSource(corsConfigurationSource())) - .csrf(csrf -> csrf.disable()) - .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) - .authorizeHttpRequests(auth -> { - auth.requestMatchers("/auth/**", "/webhook", "/swagger/**", "/swagger-ui/**", "/health", "/error", "/oauth2/**").permitAll() - .requestMatchers(org.springframework.http.HttpMethod.OPTIONS, "/**").permitAll() - .anyRequest().authenticated(); + .cors(cors -> cors.configurationSource(corsConfigurationSource())) + .csrf(csrf -> csrf.disable()) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(auth -> { + auth.requestMatchers("/auth/**", "/webhook", "/swagger/**", "/swagger-ui/**", "/health", "/error", "/oauth2/**").permitAll() + .requestMatchers(org.springframework.http.HttpMethod.OPTIONS, "/**").permitAll() + .anyRequest().authenticated(); }) - // Disable Spring's built-in OAuth2 login since we're implementing a custom flow - // We're using our own GitHubController for OAuth2 login - .authenticationProvider(authenticationProvider()) - .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); + // Disable Spring's built-in OAuth2 login since we're implementing a custom flow + // We're using our own GitHubController for OAuth2 login + .authenticationProvider(authenticationProvider()) + .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); } diff --git a/backend/src/main/java/de/szut/casino/security/dto/AuthResponseDto.java b/backend/src/main/java/de/szut/casino/security/dto/AuthResponseDto.java index 0dd03dd..4dfe79a 100644 --- a/backend/src/main/java/de/szut/casino/security/dto/AuthResponseDto.java +++ b/backend/src/main/java/de/szut/casino/security/dto/AuthResponseDto.java @@ -12,7 +12,7 @@ import lombok.Setter; public class AuthResponseDto { private String token; private String tokenType = "Bearer"; - + public AuthResponseDto(String token) { this.token = token; } diff --git a/backend/src/main/java/de/szut/casino/security/dto/LoginRequestDto.java b/backend/src/main/java/de/szut/casino/security/dto/LoginRequestDto.java index ace3508..767a6d3 100644 --- a/backend/src/main/java/de/szut/casino/security/dto/LoginRequestDto.java +++ b/backend/src/main/java/de/szut/casino/security/dto/LoginRequestDto.java @@ -13,7 +13,7 @@ import lombok.Setter; public class LoginRequestDto { @NotBlank(message = "Username or email is required") private String usernameOrEmail; - + @NotBlank(message = "Password is required") private String password; } diff --git a/backend/src/main/java/de/szut/casino/security/jwt/JwtAuthenticationFilter.java b/backend/src/main/java/de/szut/casino/security/jwt/JwtAuthenticationFilter.java index 502ba0a..38da0b3 100644 --- a/backend/src/main/java/de/szut/casino/security/jwt/JwtAuthenticationFilter.java +++ b/backend/src/main/java/de/szut/casino/security/jwt/JwtAuthenticationFilter.java @@ -35,11 +35,11 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userDetailsService.loadUserByUsername(username); - + if (jwtUtils.validateToken(jwt, userDetails)) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); - + authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authToken); } diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java b/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java index 51f52d8..de2de14 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java @@ -24,7 +24,7 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService { @Autowired private UserRepository userRepository; - + @Autowired private PasswordEncoder oauth2PasswordEncoder; @@ -44,7 +44,7 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService { private OAuth2User processOAuth2User(OAuth2UserRequest oAuth2UserRequest, OAuth2User oAuth2User) { String registrationId = oAuth2UserRequest.getClientRegistration().getRegistrationId(); OAuth2UserInfo oAuth2UserInfo = OAuth2UserInfoFactory.getOAuth2UserInfo(registrationId, oAuth2User.getAttributes()); - + // For GitHub, the email might not be directly available in attributes String email = oAuth2UserInfo.getEmail(); if (StringUtils.isEmpty(email)) { @@ -53,16 +53,16 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService { Optional userOptional = userRepository.findByEmail(email); UserEntity user; - - if(userOptional.isPresent()) { + + if (userOptional.isPresent()) { user = userOptional.get(); - - if(!user.getProvider().equals(AuthProvider.valueOf(registrationId.toUpperCase()))) { - throw new OAuth2AuthenticationProcessingException("You're signed up with " + - user.getProvider() + ". Please use your " + user.getProvider() + + + if (!user.getProvider().equals(AuthProvider.valueOf(registrationId.toUpperCase()))) { + throw new OAuth2AuthenticationProcessingException("You're signed up with " + + user.getProvider() + ". Please use your " + user.getProvider() + " account to login."); } - + user = updateExistingUser(user, oAuth2UserInfo); } else { user = registerNewUser(oAuth2UserRequest, oAuth2UserInfo, email); @@ -73,12 +73,12 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService { private UserEntity registerNewUser(OAuth2UserRequest oAuth2UserRequest, OAuth2UserInfo oAuth2UserInfo, String email) { UserEntity user = new UserEntity(); - + String username = oAuth2UserInfo.getName(); if (StringUtils.isEmpty(username)) { username = "github_" + oAuth2UserInfo.getId(); } - + // Check if username already exists and append a suffix if needed if (userRepository.findByUsername(username).isPresent()) { username = username + "_" + UUID.randomUUID().toString().substring(0, 8); @@ -89,13 +89,13 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService { user.setUsername(username); user.setEmail(email); user.setEmailVerified(true); - + // Generate a random password for OAuth users (they won't use it) String randomPassword = UUID.randomUUID().toString(); user.setPassword(oauth2PasswordEncoder.encode(randomPassword)); - + user.setBalance(new BigDecimal("1000.00")); // Starting balance - + return userRepository.save(user); } diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java b/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java index 2646956..baf8995 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java @@ -3,7 +3,7 @@ package de.szut.casino.security.oauth2; import java.util.Map; public class GitHubOAuth2UserInfo extends OAuth2UserInfo { - + public GitHubOAuth2UserInfo(Map attributes) { super(attributes); } diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java index 00d0b3e..6e4f1d0 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java @@ -1,7 +1,6 @@ package de.szut.casino.security.oauth2; import de.szut.casino.security.jwt.JwtUtils; -import de.szut.casino.user.UserEntity; import de.szut.casino.user.UserRepository; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; @@ -26,7 +25,7 @@ public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS @Autowired private JwtUtils jwtUtils; - + @Autowired private UserRepository userRepository; @@ -34,7 +33,7 @@ public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { String targetUrl = determineTargetUrl(authentication); - + logger.info("OAuth2 Authentication successful, redirecting to: {}", targetUrl); if (response.isCommitted()) { @@ -48,12 +47,12 @@ public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS private String determineTargetUrl(Authentication authentication) { String token = jwtUtils.generateToken(authentication); - + if (authentication.getPrincipal() instanceof UserPrincipal) { UserPrincipal userPrincipal = (UserPrincipal) authentication.getPrincipal(); logger.info("User authenticated: ID={}, Email={}", userPrincipal.getId(), userPrincipal.getEmail()); } - + return UriComponentsBuilder.fromUriString(redirectUri) .queryParam("token", token) .build().toUriString(); diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfoFactory.java b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfoFactory.java index 0d3c068..6f9c155 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfoFactory.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfoFactory.java @@ -6,9 +6,9 @@ import de.szut.casino.user.AuthProvider; import java.util.Map; public class OAuth2UserInfoFactory { - + public static OAuth2UserInfo getOAuth2UserInfo(String registrationId, Map attributes) { - if(registrationId.equalsIgnoreCase(AuthProvider.GITHUB.toString())) { + if (registrationId.equalsIgnoreCase(AuthProvider.GITHUB.toString())) { return new GitHubOAuth2UserInfo(attributes); } else { throw new OAuth2AuthenticationProcessingException("Sorry! Login with " + registrationId + " is not supported yet."); diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java b/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java index 113b945..6c993a0 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java @@ -64,7 +64,7 @@ public class UserPrincipal implements OAuth2User, UserDetails { // We're using email as the username for authentication return email; } - + public String getDisplayUsername() { return username; } diff --git a/backend/src/main/java/de/szut/casino/security/service/UserDetailsServiceImpl.java b/backend/src/main/java/de/szut/casino/security/service/UserDetailsServiceImpl.java index b473a35..8d4018b 100644 --- a/backend/src/main/java/de/szut/casino/security/service/UserDetailsServiceImpl.java +++ b/backend/src/main/java/de/szut/casino/security/service/UserDetailsServiceImpl.java @@ -20,17 +20,17 @@ public class UserDetailsServiceImpl implements UserDetailsService { @Override public UserDetails loadUserByUsername(String usernameOrEmail) throws UsernameNotFoundException { Optional user = userRepository.findByUsername(usernameOrEmail); - + if (user.isEmpty()) { user = userRepository.findByEmail(usernameOrEmail); } - - UserEntity userEntity = user.orElseThrow(() -> + + UserEntity userEntity = user.orElseThrow(() -> new UsernameNotFoundException("User not found with username or email: " + usernameOrEmail)); - + return new org.springframework.security.core.userdetails.User( - userEntity.getUsername(), - userEntity.getPassword(), + userEntity.getUsername(), + userEntity.getPassword(), new ArrayList<>()); } } diff --git a/backend/src/main/java/de/szut/casino/user/UserEntity.java b/backend/src/main/java/de/szut/casino/user/UserEntity.java index fa53b9a..c263d9c 100644 --- a/backend/src/main/java/de/szut/casino/user/UserEntity.java +++ b/backend/src/main/java/de/szut/casino/user/UserEntity.java @@ -1,11 +1,6 @@ package de.szut.casino.user; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.Id; +import jakarta.persistence.*; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; @@ -20,15 +15,15 @@ public class UserEntity { @Id @GeneratedValue private Long id; - + @Column(unique = true) private String email; - + @Column(unique = true) private String username; - + private String password; - + @Column(precision = 19, scale = 2) private BigDecimal balance; @@ -37,10 +32,10 @@ public class UserEntity { private String verificationToken; private String passwordResetToken; - + @Enumerated(EnumType.STRING) private AuthProvider provider = AuthProvider.LOCAL; - + private String providerId; public UserEntity(String email, String username, String password, BigDecimal balance, String verificationToken) { @@ -51,7 +46,7 @@ public class UserEntity { this.verificationToken = verificationToken; this.provider = AuthProvider.LOCAL; } - + public UserEntity(String email, String username, AuthProvider provider, String providerId, BigDecimal balance) { this.email = email; this.username = username; diff --git a/backend/src/main/java/de/szut/casino/user/UserMappingService.java b/backend/src/main/java/de/szut/casino/user/UserMappingService.java index e244646..c93c132 100644 --- a/backend/src/main/java/de/szut/casino/user/UserMappingService.java +++ b/backend/src/main/java/de/szut/casino/user/UserMappingService.java @@ -5,7 +5,7 @@ import org.springframework.stereotype.Service; @Service public class UserMappingService { - + public GetUserDto mapToGetUserDto(UserEntity user) { return new GetUserDto(user.getId(), user.getEmail(), user.getUsername(), user.getBalance()); } diff --git a/backend/src/main/java/de/szut/casino/user/UserRepository.java b/backend/src/main/java/de/szut/casino/user/UserRepository.java index 36673b1..db28f49 100644 --- a/backend/src/main/java/de/szut/casino/user/UserRepository.java +++ b/backend/src/main/java/de/szut/casino/user/UserRepository.java @@ -9,13 +9,13 @@ import java.util.Optional; @Service public interface UserRepository extends JpaRepository { Optional findByUsername(String username); - + Optional findByEmail(String email); - + Optional findByProviderId(String providerId); - + boolean existsByUsername(String username); - + boolean existsByEmail(String email); @Query("SELECT u FROM UserEntity u WHERE u.verificationToken = ?1") diff --git a/backend/src/main/java/de/szut/casino/user/UserService.java b/backend/src/main/java/de/szut/casino/user/UserService.java index baa2eab..03ad02d 100644 --- a/backend/src/main/java/de/szut/casino/user/UserService.java +++ b/backend/src/main/java/de/szut/casino/user/UserService.java @@ -27,15 +27,15 @@ public class UserService { if (userRepository.existsByEmail(createUserDto.getEmail())) { throw new EntityExistsException("Email is already in use"); } - + UserEntity user = new UserEntity( - createUserDto.getEmail(), - createUserDto.getUsername(), - passwordEncoder.encode(createUserDto.getPassword()), - BigDecimal.valueOf(100), - RandomStringUtils.randomAlphanumeric(64) + createUserDto.getEmail(), + createUserDto.getUsername(), + passwordEncoder.encode(createUserDto.getPassword()), + BigDecimal.valueOf(100), + RandomStringUtils.randomAlphanumeric(64) ); - + return userRepository.save(user); } @@ -50,7 +50,7 @@ public class UserService { } public void saveUser(UserEntity user) { - userRepository.save(user); + userRepository.save(user); } public boolean isVerified(String usernameOrEmail) { diff --git a/backend/src/main/java/de/szut/casino/user/dto/CreateUserDto.java b/backend/src/main/java/de/szut/casino/user/dto/CreateUserDto.java index f9969cc..1be6ae3 100644 --- a/backend/src/main/java/de/szut/casino/user/dto/CreateUserDto.java +++ b/backend/src/main/java/de/szut/casino/user/dto/CreateUserDto.java @@ -16,11 +16,11 @@ public class CreateUserDto { @NotBlank(message = "Email is required") @Email(message = "Email should be valid") private String email; - + @NotBlank(message = "Username is required") @Size(min = 3, max = 20, message = "Username must be between 3 and 20 characters") private String username; - + @NotBlank(message = "Password is required") @Size(min = 6, message = "Password must be at least 6 characters") private String password; diff --git a/backend/src/main/java/de/szut/casino/user/transaction/TransactionController.java b/backend/src/main/java/de/szut/casino/user/transaction/TransactionController.java index 10061fa..8aa74a6 100644 --- a/backend/src/main/java/de/szut/casino/user/transaction/TransactionController.java +++ b/backend/src/main/java/de/szut/casino/user/transaction/TransactionController.java @@ -19,7 +19,7 @@ public class TransactionController { @RequestHeader("Authorization") String authToken, @RequestParam(value = "limit", required = false) Integer limit, @RequestParam(value = "offset", required = false) Integer offset - ) { + ) { UserTransactionsDto transactionEntities = this.transactionService.getUserTransactionsDto(authToken, limit, offset); return ResponseEntity.ok(transactionEntities); -- 2.47.2 From d64b39fa693769cd9daa40dc824d65a62f21cf2f Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 21 May 2025 10:47:18 +0200 Subject: [PATCH 3/9] fix: fix email verify --- .../main/java/de/szut/casino/blackjack/BlackJackService.java | 1 + backend/src/main/resources/templates/email/verify.html | 2 +- .../src/app/feature/auth/verify-email/verify-email.component.ts | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java index c417309..5103bda 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java @@ -112,6 +112,7 @@ public class BlackJackService { dealCardToPlayer(game); dealCardToSplitHand(game); + return game; } private BlackJackGameEntity processGameBasedOnState(BlackJackGameEntity game) { diff --git a/backend/src/main/resources/templates/email/verify.html b/backend/src/main/resources/templates/email/verify.html index b7dc2a6..7076a7c 100644 --- a/backend/src/main/resources/templates/email/verify.html +++ b/backend/src/main/resources/templates/email/verify.html @@ -123,7 +123,7 @@

Klicken Sie auf den folgenden Button, um Ihre E-Mail-Adresse zu bestätigen:

diff --git a/frontend/src/app/feature/auth/verify-email/verify-email.component.ts b/frontend/src/app/feature/auth/verify-email/verify-email.component.ts index 4ccbbaa..6e04877 100644 --- a/frontend/src/app/feature/auth/verify-email/verify-email.component.ts +++ b/frontend/src/app/feature/auth/verify-email/verify-email.component.ts @@ -13,7 +13,7 @@ export class VerifyEmailComponent implements OnInit { authService: AuthService = inject(AuthService); ngOnInit(): void { - const token = this.route.snapshot.queryParamMap.get('token'); + const token = this.route.snapshot.queryParamMap.get('email-token'); if (!token) { this.router.navigate(['/']); -- 2.47.2 From 2b29ef81b2dd156d8205819ea16e3ab380513fb1 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 21 May 2025 10:48:46 +0200 Subject: [PATCH 4/9] fix: faulty rebase --- .../main/java/de/szut/casino/blackjack/BlackJackService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java b/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java index 5103bda..f791d19 100644 --- a/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java +++ b/backend/src/main/java/de/szut/casino/blackjack/BlackJackService.java @@ -112,7 +112,7 @@ public class BlackJackService { dealCardToPlayer(game); dealCardToSplitHand(game); - return game; + return processGameBasedOnState(game); } private BlackJackGameEntity processGameBasedOnState(BlackJackGameEntity game) { -- 2.47.2 From 0e150e9ded2fe995f949b59a0761ec074490be23 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 21 May 2025 10:54:25 +0200 Subject: [PATCH 5/9] refactor(GitHubService): remove unnecessary logging statements --- .../szut/casino/security/GitHubService.java | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/backend/src/main/java/de/szut/casino/security/GitHubService.java b/backend/src/main/java/de/szut/casino/security/GitHubService.java index 9ef8274..a345825 100644 --- a/backend/src/main/java/de/szut/casino/security/GitHubService.java +++ b/backend/src/main/java/de/szut/casino/security/GitHubService.java @@ -25,8 +25,6 @@ import java.util.*; @Service public class GitHubService { - private static final Logger logger = LoggerFactory.getLogger(GitHubService.class); - @Value("${spring.security.oauth2.client.registration.github.client-id}") private String clientId; @@ -71,32 +69,27 @@ public class GitHubService { ); Map responseBody = response.getBody(); - logger.info("GitHub token response: {}", responseBody); // Check if there's an error in the response if (responseBody.containsKey("error")) { String error = (String) responseBody.get("error"); String errorDescription = (String) responseBody.get("error_description"); - logger.error("GitHub OAuth error: {} - {}", error, errorDescription); + throw new RuntimeException("GitHub OAuth error: " + errorDescription); } String accessToken = (String) responseBody.get("access_token"); if (accessToken == null || accessToken.isEmpty()) { - logger.error("No access token received from GitHub"); + throw new RuntimeException("Failed to receive access token from GitHub"); } - logger.info("Received access token from GitHub"); - // Get user info HttpHeaders userInfoHeaders = new HttpHeaders(); userInfoHeaders.set("Authorization", "Bearer " + accessToken); HttpEntity userInfoRequestEntity = new HttpEntity<>(null, userInfoHeaders); - logger.info("Making request to GitHub API with token: {}", accessToken.substring(0, 5) + "..."); - ResponseEntity userResponse = restTemplate.exchange( "https://api.github.com/user", HttpMethod.GET, @@ -105,7 +98,6 @@ public class GitHubService { ); Map userAttributes = userResponse.getBody(); - logger.info("Retrieved user info from GitHub: {}", userAttributes.get("login")); // Get user emails HttpHeaders emailsHeaders = new HttpHeaders(); @@ -137,8 +129,6 @@ public class GitHubService { email = (String) emails.get(0).get("email"); } - logger.info("Using email: {}", email); - // Process user data String githubId = userAttributes.get("id").toString(); String username = (String) userAttributes.get("login"); @@ -150,7 +140,6 @@ public class GitHubService { if (userOptional.isPresent()) { // Update existing user user = userOptional.get(); - logger.info("Found existing user with providerId: {}", githubId); } else { // Check if email exists userOptional = userRepository.findByEmail(email); @@ -159,7 +148,6 @@ public class GitHubService { user = userOptional.get(); user.setProvider(AuthProvider.GITHUB); user.setProviderId(githubId); - logger.info("Updating existing user with email: {}", email); } else { // Create new user user = new UserEntity(); @@ -170,7 +158,6 @@ public class GitHubService { user.setEmailVerified(true); user.setBalance(new BigDecimal("1000.00")); - logger.info("Creating new user for: {}", username); } } @@ -183,12 +170,10 @@ public class GitHubService { // Generate JWT token String token = jwtUtils.generateToken(authentication); - logger.info("Generated JWT token"); return new AuthResponseDto(token); } catch (Exception e) { - logger.error("Error processing GitHub code", e); throw new RuntimeException("Failed to process GitHub authentication", e); } } -- 2.47.2 From 6f264dccf76ac6d0eea6ceabd265c5537e9bdcb6 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 21 May 2025 10:54:31 +0200 Subject: [PATCH 6/9] refactor: simplify UserPrincipal and OAuth2UserInfo classes --- .../security/oauth2/GitHubOAuth2UserInfo.java | 5 ----- .../casino/security/oauth2/OAuth2UserInfo.java | 9 +++------ .../casino/security/oauth2/UserPrincipal.java | 17 +++++------------ 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java b/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java index baf8995..77ac16c 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java @@ -22,9 +22,4 @@ public class GitHubOAuth2UserInfo extends OAuth2UserInfo { public String getEmail() { return (String) attributes.get("email"); } - - @Override - public String getImageUrl() { - return (String) attributes.get("avatar_url"); - } } \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfo.java b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfo.java index 3acd762..5019bb1 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfo.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfo.java @@ -1,7 +1,10 @@ package de.szut.casino.security.oauth2; +import lombok.Getter; + import java.util.Map; +@Getter public abstract class OAuth2UserInfo { protected Map attributes; @@ -9,15 +12,9 @@ public abstract class OAuth2UserInfo { this.attributes = attributes; } - public Map getAttributes() { - return attributes; - } - public abstract String getId(); public abstract String getName(); public abstract String getEmail(); - - public abstract String getImageUrl(); } \ No newline at end of file diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java b/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java index 6c993a0..1460982 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java @@ -1,6 +1,8 @@ package de.szut.casino.security.oauth2; import de.szut.casino.user.UserEntity; +import lombok.Getter; +import lombok.Setter; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; @@ -12,11 +14,14 @@ import java.util.List; import java.util.Map; public class UserPrincipal implements OAuth2User, UserDetails { + @Getter private Long id; + @Getter private String email; private String username; private String password; private Collection authorities; + @Setter private Map attributes; public UserPrincipal(Long id, String email, String username, String password, Collection authorities) { @@ -46,14 +51,6 @@ public class UserPrincipal implements OAuth2User, UserDetails { return userPrincipal; } - public Long getId() { - return id; - } - - public String getEmail() { - return email; - } - @Override public String getPassword() { return password; @@ -99,10 +96,6 @@ public class UserPrincipal implements OAuth2User, UserDetails { return attributes; } - public void setAttributes(Map attributes) { - this.attributes = attributes; - } - @Override public String getName() { return String.valueOf(id); -- 2.47.2 From 6f6bbe6d8b79e264d17ab9bd09db06fa6320f043 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 21 May 2025 10:56:56 +0200 Subject: [PATCH 7/9] refactor(security): remove unused GitHubService and comments --- .../de/szut/casino/security/AuthController.java | 3 --- .../java/de/szut/casino/security/CorsFilter.java | 1 - .../de/szut/casino/security/GitHubService.java | 15 --------------- .../de/szut/casino/security/SecurityConfig.java | 5 ----- .../security/oauth2/CustomOAuth2UserService.java | 5 +---- .../OAuth2AuthenticationSuccessHandler.java | 7 +------ .../casino/security/oauth2/UserPrincipal.java | 1 - 7 files changed, 2 insertions(+), 35 deletions(-) diff --git a/backend/src/main/java/de/szut/casino/security/AuthController.java b/backend/src/main/java/de/szut/casino/security/AuthController.java index 13a238e..561fc3c 100644 --- a/backend/src/main/java/de/szut/casino/security/AuthController.java +++ b/backend/src/main/java/de/szut/casino/security/AuthController.java @@ -23,9 +23,6 @@ public class AuthController { @Autowired private AuthService authService; - @Autowired - private GitHubService githubService; - @PostMapping("/login") public ResponseEntity authenticateUser(@Valid @RequestBody LoginRequestDto loginRequest) throws EmailNotVerifiedException { AuthResponseDto response = authService.login(loginRequest); diff --git a/backend/src/main/java/de/szut/casino/security/CorsFilter.java b/backend/src/main/java/de/szut/casino/security/CorsFilter.java index 446864e..b1c1b3b 100644 --- a/backend/src/main/java/de/szut/casino/security/CorsFilter.java +++ b/backend/src/main/java/de/szut/casino/security/CorsFilter.java @@ -23,7 +23,6 @@ public class CorsFilter implements Filter { HttpServletResponse response = (HttpServletResponse) res; HttpServletRequest request = (HttpServletRequest) req; - // Allow requests from the frontend response.setHeader("Access-Control-Allow-Origin", frontendHost); response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS"); response.setHeader("Access-Control-Allow-Headers", "*"); diff --git a/backend/src/main/java/de/szut/casino/security/GitHubService.java b/backend/src/main/java/de/szut/casino/security/GitHubService.java index a345825..b01cb76 100644 --- a/backend/src/main/java/de/szut/casino/security/GitHubService.java +++ b/backend/src/main/java/de/szut/casino/security/GitHubService.java @@ -45,22 +45,18 @@ public class GitHubService { public AuthResponseDto processGithubCode(String code) { try { - // Exchange code for access token RestTemplate restTemplate = new RestTemplate(); - // Create request body for token endpoint Map requestBody = new HashMap<>(); requestBody.put("client_id", clientId); requestBody.put("client_secret", clientSecret); requestBody.put("code", code); - // Set headers HttpHeaders headers = new HttpHeaders(); headers.set("Accept", "application/json"); HttpEntity> requestEntity = new HttpEntity<>(requestBody, headers); - // Get access token ResponseEntity response = restTemplate.exchange( "https://github.com/login/oauth/access_token", HttpMethod.POST, @@ -70,7 +66,6 @@ public class GitHubService { Map responseBody = response.getBody(); - // Check if there's an error in the response if (responseBody.containsKey("error")) { String error = (String) responseBody.get("error"); String errorDescription = (String) responseBody.get("error_description"); @@ -84,7 +79,6 @@ public class GitHubService { throw new RuntimeException("Failed to receive access token from GitHub"); } - // Get user info HttpHeaders userInfoHeaders = new HttpHeaders(); userInfoHeaders.set("Authorization", "Bearer " + accessToken); @@ -99,7 +93,6 @@ public class GitHubService { Map userAttributes = userResponse.getBody(); - // Get user emails HttpHeaders emailsHeaders = new HttpHeaders(); emailsHeaders.set("Authorization", "Bearer " + accessToken); @@ -115,7 +108,6 @@ public class GitHubService { List> emails = emailsResponse.getBody(); String email = null; - // Find primary email for (Map emailInfo : emails) { Boolean primary = (Boolean) emailInfo.get("primary"); if (primary != null && primary) { @@ -124,24 +116,19 @@ public class GitHubService { } } - // If no primary email, just use the first one if (email == null && !emails.isEmpty()) { email = (String) emails.get(0).get("email"); } - // Process user data String githubId = userAttributes.get("id").toString(); String username = (String) userAttributes.get("login"); - // Check if user exists by provider ID Optional userOptional = userRepository.findByProviderId(githubId); UserEntity user; if (userOptional.isPresent()) { - // Update existing user user = userOptional.get(); } else { - // Check if email exists userOptional = userRepository.findByEmail(email); if (userOptional.isPresent()) { @@ -149,7 +136,6 @@ public class GitHubService { user.setProvider(AuthProvider.GITHUB); user.setProviderId(githubId); } else { - // Create new user user = new UserEntity(); user.setEmail(email); user.setUsername(username); @@ -168,7 +154,6 @@ public class GitHubService { Authentication authentication = this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), randomPassword)); - // Generate JWT token String token = jwtUtils.generateToken(authentication); return new AuthResponseDto(token); diff --git a/backend/src/main/java/de/szut/casino/security/SecurityConfig.java b/backend/src/main/java/de/szut/casino/security/SecurityConfig.java index 9b3c880..740739e 100644 --- a/backend/src/main/java/de/szut/casino/security/SecurityConfig.java +++ b/backend/src/main/java/de/szut/casino/security/SecurityConfig.java @@ -32,9 +32,6 @@ public class SecurityConfig { @Value("${app.frontend-host}") private String frontendHost; - @Value("${app.oauth2.authorizedRedirectUris}") - private String authorizedRedirectUri; - @Autowired private UserDetailsService userDetailsService; @@ -73,8 +70,6 @@ public class SecurityConfig { .requestMatchers(org.springframework.http.HttpMethod.OPTIONS, "/**").permitAll() .anyRequest().authenticated(); }) - // Disable Spring's built-in OAuth2 login since we're implementing a custom flow - // We're using our own GitHubController for OAuth2 login .authenticationProvider(authenticationProvider()) .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java b/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java index de2de14..c873ad9 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java @@ -45,7 +45,6 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService { String registrationId = oAuth2UserRequest.getClientRegistration().getRegistrationId(); OAuth2UserInfo oAuth2UserInfo = OAuth2UserInfoFactory.getOAuth2UserInfo(registrationId, oAuth2User.getAttributes()); - // For GitHub, the email might not be directly available in attributes String email = oAuth2UserInfo.getEmail(); if (StringUtils.isEmpty(email)) { email = oAuth2UserInfo.getName() + "@github.user"; @@ -79,7 +78,6 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService { username = "github_" + oAuth2UserInfo.getId(); } - // Check if username already exists and append a suffix if needed if (userRepository.findByUsername(username).isPresent()) { username = username + "_" + UUID.randomUUID().toString().substring(0, 8); } @@ -90,11 +88,10 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService { user.setEmail(email); user.setEmailVerified(true); - // Generate a random password for OAuth users (they won't use it) String randomPassword = UUID.randomUUID().toString(); user.setPassword(oauth2PasswordEncoder.encode(randomPassword)); - user.setBalance(new BigDecimal("1000.00")); // Starting balance + user.setBalance(new BigDecimal("100.00")); // Starting balance return userRepository.save(user); } diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java index 6e4f1d0..1e27bca 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java @@ -1,8 +1,6 @@ package de.szut.casino.security.oauth2; import de.szut.casino.security.jwt.JwtUtils; -import de.szut.casino.user.UserRepository; -import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.slf4j.Logger; @@ -26,12 +24,9 @@ public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS @Autowired private JwtUtils jwtUtils; - @Autowired - private UserRepository userRepository; - @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) - throws IOException, ServletException { + throws IOException { String targetUrl = determineTargetUrl(authentication); logger.info("OAuth2 Authentication successful, redirecting to: {}", targetUrl); diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java b/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java index 1460982..881e3fd 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java @@ -58,7 +58,6 @@ public class UserPrincipal implements OAuth2User, UserDetails { @Override public String getUsername() { - // We're using email as the username for authentication return email; } -- 2.47.2 From 75de7d137028eb527e14ca505b067b8437987939 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 21 May 2025 10:59:36 +0200 Subject: [PATCH 8/9] style: fix missing newlines at end of files --- .../exceptions/OAuth2AuthenticationProcessingException.java | 2 +- .../src/main/java/de/szut/casino/security/GitHubController.java | 2 +- .../src/main/java/de/szut/casino/security/GitHubService.java | 2 +- .../java/de/szut/casino/security/dto/GithubCallbackDto.java | 2 +- .../de/szut/casino/security/oauth2/CustomOAuth2UserService.java | 2 +- .../de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java | 2 +- .../security/oauth2/OAuth2AuthenticationSuccessHandler.java | 2 +- .../main/java/de/szut/casino/security/oauth2/OAuth2Config.java | 2 +- .../java/de/szut/casino/security/oauth2/OAuth2UserInfo.java | 2 +- .../de/szut/casino/security/oauth2/OAuth2UserInfoFactory.java | 2 +- .../main/java/de/szut/casino/security/oauth2/UserPrincipal.java | 2 +- backend/src/main/java/de/szut/casino/user/AuthProvider.java | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/OAuth2AuthenticationProcessingException.java b/backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/OAuth2AuthenticationProcessingException.java index 3a1447e..b4f421e 100644 --- a/backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/OAuth2AuthenticationProcessingException.java +++ b/backend/src/main/java/de/szut/casino/exceptionHandling/exceptions/OAuth2AuthenticationProcessingException.java @@ -6,4 +6,4 @@ public class OAuth2AuthenticationProcessingException extends AuthenticationExcep public OAuth2AuthenticationProcessingException(String msg) { super(msg); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/de/szut/casino/security/GitHubController.java b/backend/src/main/java/de/szut/casino/security/GitHubController.java index ea1f23e..b45fb62 100644 --- a/backend/src/main/java/de/szut/casino/security/GitHubController.java +++ b/backend/src/main/java/de/szut/casino/security/GitHubController.java @@ -46,4 +46,4 @@ public class GitHubController { AuthResponseDto response = githubService.processGithubCode(code); return ResponseEntity.ok(response); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/de/szut/casino/security/GitHubService.java b/backend/src/main/java/de/szut/casino/security/GitHubService.java index b01cb76..1051498 100644 --- a/backend/src/main/java/de/szut/casino/security/GitHubService.java +++ b/backend/src/main/java/de/szut/casino/security/GitHubService.java @@ -162,4 +162,4 @@ public class GitHubService { throw new RuntimeException("Failed to process GitHub authentication", e); } } -} \ No newline at end of file +} diff --git a/backend/src/main/java/de/szut/casino/security/dto/GithubCallbackDto.java b/backend/src/main/java/de/szut/casino/security/dto/GithubCallbackDto.java index 7d0315d..07619aa 100644 --- a/backend/src/main/java/de/szut/casino/security/dto/GithubCallbackDto.java +++ b/backend/src/main/java/de/szut/casino/security/dto/GithubCallbackDto.java @@ -5,4 +5,4 @@ import lombok.Data; @Data public class GithubCallbackDto { private String code; -} \ No newline at end of file +} diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java b/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java index c873ad9..eb09b27 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/CustomOAuth2UserService.java @@ -102,4 +102,4 @@ public class CustomOAuth2UserService extends DefaultOAuth2UserService { } return userRepository.save(existingUser); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java b/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java index 77ac16c..f98390e 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/GitHubOAuth2UserInfo.java @@ -22,4 +22,4 @@ public class GitHubOAuth2UserInfo extends OAuth2UserInfo { public String getEmail() { return (String) attributes.get("email"); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java index 1e27bca..778fa88 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2AuthenticationSuccessHandler.java @@ -52,4 +52,4 @@ public class OAuth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS .queryParam("token", token) .build().toUriString(); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2Config.java b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2Config.java index 224d9c0..32104ed 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2Config.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2Config.java @@ -12,4 +12,4 @@ public class OAuth2Config { public PasswordEncoder oauth2PasswordEncoder() { return new BCryptPasswordEncoder(); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfo.java b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfo.java index 5019bb1..14e2bcc 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfo.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfo.java @@ -17,4 +17,4 @@ public abstract class OAuth2UserInfo { public abstract String getName(); public abstract String getEmail(); -} \ No newline at end of file +} diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfoFactory.java b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfoFactory.java index 6f9c155..b7d4365 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfoFactory.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/OAuth2UserInfoFactory.java @@ -14,4 +14,4 @@ public class OAuth2UserInfoFactory { throw new OAuth2AuthenticationProcessingException("Sorry! Login with " + registrationId + " is not supported yet."); } } -} \ No newline at end of file +} diff --git a/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java b/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java index 881e3fd..8ec25e0 100644 --- a/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java +++ b/backend/src/main/java/de/szut/casino/security/oauth2/UserPrincipal.java @@ -99,4 +99,4 @@ public class UserPrincipal implements OAuth2User, UserDetails { public String getName() { return String.valueOf(id); } -} \ No newline at end of file +} diff --git a/backend/src/main/java/de/szut/casino/user/AuthProvider.java b/backend/src/main/java/de/szut/casino/user/AuthProvider.java index d7c7e05..2216da7 100644 --- a/backend/src/main/java/de/szut/casino/user/AuthProvider.java +++ b/backend/src/main/java/de/szut/casino/user/AuthProvider.java @@ -3,4 +3,4 @@ package de.szut.casino.user; public enum AuthProvider { LOCAL, GITHUB -} \ No newline at end of file +} -- 2.47.2 From 969e2ac0da85be2308341ce7e16eb9f8fa0ba637 Mon Sep 17 00:00:00 2001 From: Constantin Simonis Date: Wed, 21 May 2025 11:00:01 +0200 Subject: [PATCH 9/9] style: format HTML and TypeScript files --- .../feature/auth/login/login.component.html | 19 +++++++++++++------ .../auth/oauth2/oauth2-callback.component.ts | 16 +++++++++------- frontend/src/app/service/auth.service.ts | 4 ++-- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/frontend/src/app/feature/auth/login/login.component.html b/frontend/src/app/feature/auth/login/login.component.html index fee6ce9..14a7d1e 100644 --- a/frontend/src/app/feature/auth/login/login.component.html +++ b/frontend/src/app/feature/auth/login/login.component.html @@ -82,20 +82,27 @@
- +
ODER
- +
- diff --git a/frontend/src/app/feature/auth/oauth2/oauth2-callback.component.ts b/frontend/src/app/feature/auth/oauth2/oauth2-callback.component.ts index e352952..f905f75 100644 --- a/frontend/src/app/feature/auth/oauth2/oauth2-callback.component.ts +++ b/frontend/src/app/feature/auth/oauth2/oauth2-callback.component.ts @@ -11,7 +11,9 @@ import { AuthService } from '@service/auth.service';

Finishing authentication...

-
+

{{ error }}

@@ -28,7 +30,7 @@ export class OAuth2CallbackComponent implements OnInit { ngOnInit(): void { // Check for code in URL params - this.route.queryParams.subscribe(params => { + this.route.queryParams.subscribe((params) => { const code = params['code']; if (code) { @@ -40,18 +42,18 @@ export class OAuth2CallbackComponent implements OnInit { }, error: (err) => { console.error('GitHub authentication error:', err); - this.error = err.error?.message || "Authentication failed. Please try again."; + this.error = err.error?.message || 'Authentication failed. Please try again.'; console.log('Error details:', err); - + // Redirect back to landing page after showing error setTimeout(() => { this.router.navigate(['/']); }, 3000); - } + }, }); } else { - this.error = "Authentication failed. No authorization code received."; - + this.error = 'Authentication failed. No authorization code received.'; + // Redirect back to landing page after showing error setTimeout(() => { this.router.navigate(['/']); diff --git a/frontend/src/app/service/auth.service.ts b/frontend/src/app/service/auth.service.ts index c517d88..081ad72 100644 --- a/frontend/src/app/service/auth.service.ts +++ b/frontend/src/app/service/auth.service.ts @@ -29,7 +29,7 @@ export class AuthService { this.userSubject = new BehaviorSubject(this.getUserFromStorage()); // Check for token in URL (OAuth callback) on initialization - this.route.queryParams.subscribe(params => { + this.route.queryParams.subscribe((params) => { const token = params['token']; if (token) { this.handleOAuthCallback(token); @@ -48,7 +48,7 @@ export class AuthService { this.router.navigate([], { relativeTo: this.route, queryParams: {}, - replaceUrl: true + replaceUrl: true, }); } -- 2.47.2