Compare commits
No commits in common. "v1.63.0" and "v1.62.0" have entirely different histories.
42 changed files with 79 additions and 833 deletions
|
@ -8,6 +8,9 @@ import org.springframework.boot.CommandLineRunner;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.context.annotation.Bean;
|
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.mail.javamail.JavaMailSenderImpl;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ public class BlackJackService {
|
||||||
dealCardToPlayer(game);
|
dealCardToPlayer(game);
|
||||||
dealCardToSplitHand(game);
|
dealCardToSplitHand(game);
|
||||||
|
|
||||||
return processGameBasedOnState(game);
|
return blackJackGameRepository.save(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BlackJackGameEntity processGameBasedOnState(BlackJackGameEntity game) {
|
private BlackJackGameEntity processGameBasedOnState(BlackJackGameEntity game) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package de.szut.casino.config;
|
package de.szut.casino.config;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import io.swagger.v3.oas.models.Components;
|
import io.swagger.v3.oas.models.Components;
|
||||||
import io.swagger.v3.oas.models.OpenAPI;
|
import io.swagger.v3.oas.models.OpenAPI;
|
||||||
import io.swagger.v3.oas.models.info.Info;
|
import io.swagger.v3.oas.models.info.Info;
|
||||||
|
|
|
@ -18,12 +18,12 @@ public class WebConfig {
|
||||||
@Override
|
@Override
|
||||||
public void addCorsMappings(CorsRegistry registry) {
|
public void addCorsMappings(CorsRegistry registry) {
|
||||||
registry.addMapping("/**")
|
registry.addMapping("/**")
|
||||||
.allowedOrigins(frontendHost)
|
.allowedOrigins(frontendHost)
|
||||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
|
||||||
.allowedHeaders("*")
|
.allowedHeaders("*")
|
||||||
.exposedHeaders("*")
|
.exposedHeaders("*")
|
||||||
.allowCredentials(true)
|
.allowCredentials(true)
|
||||||
.maxAge(3600);
|
.maxAge(3600);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,8 +53,8 @@ public class DepositController {
|
||||||
.build())
|
.build())
|
||||||
.setQuantity(1L)
|
.setQuantity(1L)
|
||||||
.build())
|
.build())
|
||||||
.setSuccessUrl(frontendHost + "/home?success=true")
|
.setSuccessUrl(frontendHost+"/home?success=true")
|
||||||
.setCancelUrl(frontendHost + "/home?success=false")
|
.setCancelUrl(frontendHost+"/home?success=false")
|
||||||
.setMode(SessionCreateParams.Mode.PAYMENT)
|
.setMode(SessionCreateParams.Mode.PAYMENT)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package de.szut.casino.exceptionHandling.exceptions;
|
package de.szut.casino.exceptionHandling.exceptions;
|
||||||
|
|
||||||
|
import de.szut.casino.security.service.EmailService;
|
||||||
|
|
||||||
public class EmailNotVerifiedException extends Exception {
|
public class EmailNotVerifiedException extends Exception {
|
||||||
public EmailNotVerifiedException() {
|
public EmailNotVerifiedException() {
|
||||||
super("Email not verified");
|
super("Email not verified");
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
package de.szut.casino.exceptionHandling.exceptions;
|
|
||||||
|
|
||||||
import org.springframework.security.core.AuthenticationException;
|
|
||||||
|
|
||||||
public class OAuth2AuthenticationProcessingException extends AuthenticationException {
|
|
||||||
public OAuth2AuthenticationProcessingException(String msg) {
|
|
||||||
super(msg);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,6 @@
|
||||||
package de.szut.casino.security;
|
package de.szut.casino.security;
|
||||||
|
|
||||||
|
import de.szut.casino.exceptionHandling.ErrorDetails;
|
||||||
import de.szut.casino.exceptionHandling.exceptions.EmailNotVerifiedException;
|
import de.szut.casino.exceptionHandling.exceptions.EmailNotVerifiedException;
|
||||||
import de.szut.casino.security.dto.AuthResponseDto;
|
import de.szut.casino.security.dto.AuthResponseDto;
|
||||||
import de.szut.casino.security.dto.LoginRequestDto;
|
import de.szut.casino.security.dto.LoginRequestDto;
|
||||||
|
@ -14,12 +15,12 @@ import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("/auth")
|
@RequestMapping("/auth")
|
||||||
public class AuthController {
|
public class AuthController {
|
||||||
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private AuthService authService;
|
private AuthService authService;
|
||||||
|
|
||||||
|
@ -37,11 +38,11 @@ public class AuthController {
|
||||||
|
|
||||||
@PostMapping("/verify")
|
@PostMapping("/verify")
|
||||||
public ResponseEntity<Void> verifyEmail(@RequestParam("token") String token) throws MessagingException, IOException {
|
public ResponseEntity<Void> verifyEmail(@RequestParam("token") String token) throws MessagingException, IOException {
|
||||||
if (authService.verifyEmail(token)) {
|
if (authService.verifyEmail(token)) {
|
||||||
return ResponseEntity.badRequest().build();
|
return ResponseEntity.badRequest().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok().build();
|
return ResponseEntity.ok().build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("/recover-password")
|
@PostMapping("/recover-password")
|
||||||
|
|
|
@ -23,6 +23,7 @@ public class CorsFilter implements Filter {
|
||||||
HttpServletResponse response = (HttpServletResponse) res;
|
HttpServletResponse response = (HttpServletResponse) res;
|
||||||
HttpServletRequest request = (HttpServletRequest) req;
|
HttpServletRequest request = (HttpServletRequest) req;
|
||||||
|
|
||||||
|
// Allow requests from the frontend
|
||||||
response.setHeader("Access-Control-Allow-Origin", frontendHost);
|
response.setHeader("Access-Control-Allow-Origin", frontendHost);
|
||||||
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
|
response.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
|
||||||
response.setHeader("Access-Control-Allow-Headers", "*");
|
response.setHeader("Access-Control-Allow-Headers", "*");
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
package de.szut.casino.security;
|
|
||||||
|
|
||||||
import de.szut.casino.security.dto.AuthResponseDto;
|
|
||||||
import de.szut.casino.security.dto.GithubCallbackDto;
|
|
||||||
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.ResponseEntity;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
import org.springframework.web.servlet.view.RedirectView;
|
|
||||||
|
|
||||||
@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<AuthResponseDto> githubCallback(@RequestBody GithubCallbackDto githubCallbackDto) {
|
|
||||||
String code = githubCallbackDto.getCode();
|
|
||||||
AuthResponseDto response = githubService.processGithubCode(code);
|
|
||||||
return ResponseEntity.ok(response);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,165 +0,0 @@
|
||||||
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.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 {
|
|
||||||
@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 {
|
|
||||||
RestTemplate restTemplate = new RestTemplate();
|
|
||||||
|
|
||||||
Map<String, String> requestBody = new HashMap<>();
|
|
||||||
requestBody.put("client_id", clientId);
|
|
||||||
requestBody.put("client_secret", clientSecret);
|
|
||||||
requestBody.put("code", code);
|
|
||||||
|
|
||||||
HttpHeaders headers = new HttpHeaders();
|
|
||||||
headers.set("Accept", "application/json");
|
|
||||||
|
|
||||||
HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
|
|
||||||
|
|
||||||
ResponseEntity<Map> response = restTemplate.exchange(
|
|
||||||
"https://github.com/login/oauth/access_token",
|
|
||||||
HttpMethod.POST,
|
|
||||||
requestEntity,
|
|
||||||
Map.class
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, Object> responseBody = response.getBody();
|
|
||||||
|
|
||||||
if (responseBody.containsKey("error")) {
|
|
||||||
String error = (String) responseBody.get("error");
|
|
||||||
String errorDescription = (String) responseBody.get("error_description");
|
|
||||||
|
|
||||||
throw new RuntimeException("GitHub OAuth error: " + errorDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
String accessToken = (String) responseBody.get("access_token");
|
|
||||||
if (accessToken == null || accessToken.isEmpty()) {
|
|
||||||
|
|
||||||
throw new RuntimeException("Failed to receive access token from GitHub");
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpHeaders userInfoHeaders = new HttpHeaders();
|
|
||||||
userInfoHeaders.set("Authorization", "Bearer " + accessToken);
|
|
||||||
|
|
||||||
HttpEntity<String> userInfoRequestEntity = new HttpEntity<>(null, userInfoHeaders);
|
|
||||||
|
|
||||||
ResponseEntity<Map> userResponse = restTemplate.exchange(
|
|
||||||
"https://api.github.com/user",
|
|
||||||
HttpMethod.GET,
|
|
||||||
userInfoRequestEntity,
|
|
||||||
Map.class
|
|
||||||
);
|
|
||||||
|
|
||||||
Map<String, Object> userAttributes = userResponse.getBody();
|
|
||||||
|
|
||||||
HttpHeaders emailsHeaders = new HttpHeaders();
|
|
||||||
emailsHeaders.set("Authorization", "Bearer " + accessToken);
|
|
||||||
|
|
||||||
HttpEntity<String> emailsRequestEntity = new HttpEntity<>(null, emailsHeaders);
|
|
||||||
|
|
||||||
ResponseEntity<List> emailsResponse = restTemplate.exchange(
|
|
||||||
"https://api.github.com/user/emails",
|
|
||||||
HttpMethod.GET,
|
|
||||||
emailsRequestEntity,
|
|
||||||
List.class
|
|
||||||
);
|
|
||||||
|
|
||||||
List<Map<String, Object>> emails = emailsResponse.getBody();
|
|
||||||
String email = null;
|
|
||||||
|
|
||||||
for (Map<String, Object> emailInfo : emails) {
|
|
||||||
Boolean primary = (Boolean) emailInfo.get("primary");
|
|
||||||
if (primary != null && primary) {
|
|
||||||
email = (String) emailInfo.get("email");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (email == null && !emails.isEmpty()) {
|
|
||||||
email = (String) emails.get(0).get("email");
|
|
||||||
}
|
|
||||||
|
|
||||||
String githubId = userAttributes.get("id").toString();
|
|
||||||
String username = (String) userAttributes.get("login");
|
|
||||||
|
|
||||||
Optional<UserEntity> userOptional = userRepository.findByProviderId(githubId);
|
|
||||||
UserEntity user;
|
|
||||||
|
|
||||||
if (userOptional.isPresent()) {
|
|
||||||
user = userOptional.get();
|
|
||||||
} else {
|
|
||||||
userOptional = userRepository.findByEmail(email);
|
|
||||||
|
|
||||||
if (userOptional.isPresent()) {
|
|
||||||
user = userOptional.get();
|
|
||||||
user.setProvider(AuthProvider.GITHUB);
|
|
||||||
user.setProviderId(githubId);
|
|
||||||
} else {
|
|
||||||
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"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String randomPassword = UUID.randomUUID().toString();
|
|
||||||
user.setPassword(oauth2PasswordEncoder.encode(randomPassword));
|
|
||||||
|
|
||||||
userRepository.save(user);
|
|
||||||
|
|
||||||
Authentication authentication = this.authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(user.getEmail(), randomPassword));
|
|
||||||
|
|
||||||
String token = jwtUtils.generateToken(authentication);
|
|
||||||
|
|
||||||
return new AuthResponseDto(token);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new RuntimeException("Failed to process GitHub authentication", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -38,7 +38,6 @@ public class SecurityConfig {
|
||||||
@Autowired
|
@Autowired
|
||||||
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
private JwtAuthenticationFilter jwtAuthenticationFilter;
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public DaoAuthenticationProvider authenticationProvider() {
|
public DaoAuthenticationProvider authenticationProvider() {
|
||||||
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
|
||||||
|
@ -62,16 +61,16 @@ public class SecurityConfig {
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
||||||
http
|
http
|
||||||
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
|
.cors(cors -> cors.configurationSource(corsConfigurationSource()))
|
||||||
.csrf(csrf -> csrf.disable())
|
.csrf(csrf -> csrf.disable())
|
||||||
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
|
||||||
.authorizeHttpRequests(auth -> {
|
.authorizeHttpRequests(auth -> {
|
||||||
auth.requestMatchers("/auth/**", "/webhook", "/swagger/**", "/swagger-ui/**", "/health", "/error", "/oauth2/**").permitAll()
|
auth.requestMatchers("/auth/**", "/webhook", "/swagger/**", "/swagger-ui/**", "/health", "/error").permitAll()
|
||||||
.requestMatchers(org.springframework.http.HttpMethod.OPTIONS, "/**").permitAll()
|
.requestMatchers(org.springframework.http.HttpMethod.OPTIONS, "/**").permitAll()
|
||||||
.anyRequest().authenticated();
|
.anyRequest().authenticated();
|
||||||
})
|
})
|
||||||
.authenticationProvider(authenticationProvider())
|
.authenticationProvider(authenticationProvider())
|
||||||
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
|
||||||
|
|
||||||
return http.build();
|
return http.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
package de.szut.casino.security.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public class GithubCallbackDto {
|
|
||||||
private String code;
|
|
||||||
}
|
|
|
@ -1,16 +1,12 @@
|
||||||
package de.szut.casino.security.jwt;
|
package de.szut.casino.security.jwt;
|
||||||
|
|
||||||
import de.szut.casino.security.oauth2.UserPrincipal;
|
|
||||||
import io.jsonwebtoken.Claims;
|
import io.jsonwebtoken.Claims;
|
||||||
import io.jsonwebtoken.Jwts;
|
import io.jsonwebtoken.Jwts;
|
||||||
import io.jsonwebtoken.SignatureAlgorithm;
|
import io.jsonwebtoken.SignatureAlgorithm;
|
||||||
import io.jsonwebtoken.security.Keys;
|
import io.jsonwebtoken.security.Keys;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.oauth2.core.user.OAuth2User;
|
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
import java.security.Key;
|
import java.security.Key;
|
||||||
|
@ -21,7 +17,6 @@ import java.util.function.Function;
|
||||||
|
|
||||||
@Component
|
@Component
|
||||||
public class JwtUtils {
|
public class JwtUtils {
|
||||||
private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
|
|
||||||
|
|
||||||
@Value("${jwt.secret}")
|
@Value("${jwt.secret}")
|
||||||
private String jwtSecret;
|
private String jwtSecret;
|
||||||
|
@ -34,26 +29,8 @@ public class JwtUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String generateToken(Authentication authentication) {
|
public String generateToken(Authentication authentication) {
|
||||||
String subject = null;
|
UserDetails userDetails = (UserDetails) authentication.getPrincipal();
|
||||||
Map<String, Object> claims = new HashMap<>();
|
return generateToken(userDetails.getUsername());
|
||||||
|
|
||||||
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) {
|
public String generateToken(String username) {
|
||||||
|
@ -63,9 +40,6 @@ public class JwtUtils {
|
||||||
|
|
||||||
private String createToken(Map<String, Object> claims, String subject) {
|
private String createToken(Map<String, Object> claims, String subject) {
|
||||||
Date now = new Date();
|
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);
|
Date expiryDate = new Date(now.getTime() + jwtExpirationMs);
|
||||||
|
|
||||||
return Jwts.builder()
|
return Jwts.builder()
|
||||||
|
|
|
@ -1,105 +0,0 @@
|
||||||
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());
|
|
||||||
|
|
||||||
String email = oAuth2UserInfo.getEmail();
|
|
||||||
if (StringUtils.isEmpty(email)) {
|
|
||||||
email = oAuth2UserInfo.getName() + "@github.user";
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<UserEntity> 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
String randomPassword = UUID.randomUUID().toString();
|
|
||||||
user.setPassword(oauth2PasswordEncoder.encode(randomPassword));
|
|
||||||
|
|
||||||
user.setBalance(new BigDecimal("100.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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
package de.szut.casino.security.oauth2;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class GitHubOAuth2UserInfo extends OAuth2UserInfo {
|
|
||||||
|
|
||||||
public GitHubOAuth2UserInfo(Map<String, Object> 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");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
package de.szut.casino.security.oauth2;
|
|
||||||
|
|
||||||
import de.szut.casino.security.jwt.JwtUtils;
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
|
|
||||||
throws IOException {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package de.szut.casino.security.oauth2;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
public abstract class OAuth2UserInfo {
|
|
||||||
protected Map<String, Object> attributes;
|
|
||||||
|
|
||||||
public OAuth2UserInfo(Map<String, Object> attributes) {
|
|
||||||
this.attributes = attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract String getId();
|
|
||||||
|
|
||||||
public abstract String getName();
|
|
||||||
|
|
||||||
public abstract String getEmail();
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
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<String, Object> attributes) {
|
|
||||||
if (registrationId.equalsIgnoreCase(AuthProvider.GITHUB.toString())) {
|
|
||||||
return new GitHubOAuth2UserInfo(attributes);
|
|
||||||
} else {
|
|
||||||
throw new OAuth2AuthenticationProcessingException("Sorry! Login with " + registrationId + " is not supported yet.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,102 +0,0 @@
|
||||||
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;
|
|
||||||
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 {
|
|
||||||
@Getter
|
|
||||||
private Long id;
|
|
||||||
@Getter
|
|
||||||
private String email;
|
|
||||||
private String username;
|
|
||||||
private String password;
|
|
||||||
private Collection<? extends GrantedAuthority> authorities;
|
|
||||||
@Setter
|
|
||||||
private Map<String, Object> attributes;
|
|
||||||
|
|
||||||
public UserPrincipal(Long id, String email, String username, String password, Collection<? extends GrantedAuthority> authorities) {
|
|
||||||
this.id = id;
|
|
||||||
this.email = email;
|
|
||||||
this.username = username;
|
|
||||||
this.password = password;
|
|
||||||
this.authorities = authorities;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UserPrincipal create(UserEntity user) {
|
|
||||||
List<GrantedAuthority> 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<String, Object> attributes) {
|
|
||||||
UserPrincipal userPrincipal = UserPrincipal.create(user);
|
|
||||||
userPrincipal.setAttributes(attributes);
|
|
||||||
return userPrincipal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getPassword() {
|
|
||||||
return password;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUsername() {
|
|
||||||
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<? extends GrantedAuthority> getAuthorities() {
|
|
||||||
return authorities;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Object> getAttributes() {
|
|
||||||
return attributes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return String.valueOf(id);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,6 +19,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.security.crypto.password.PasswordEncoder;
|
import org.springframework.security.crypto.password.PasswordEncoder;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.swing.text.html.Option;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
package de.szut.casino.user;
|
|
||||||
|
|
||||||
public enum AuthProvider {
|
|
||||||
LOCAL,
|
|
||||||
GITHUB
|
|
||||||
}
|
|
|
@ -1,6 +1,9 @@
|
||||||
package de.szut.casino.user;
|
package de.szut.casino.user;
|
||||||
|
|
||||||
import jakarta.persistence.*;
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Entity;
|
||||||
|
import jakarta.persistence.GeneratedValue;
|
||||||
|
import jakarta.persistence.Id;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
import lombok.NoArgsConstructor;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
@ -33,27 +36,12 @@ public class UserEntity {
|
||||||
|
|
||||||
private String passwordResetToken;
|
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) {
|
public UserEntity(String email, String username, String password, BigDecimal balance, String verificationToken) {
|
||||||
this.email = email;
|
this.email = email;
|
||||||
this.username = username;
|
this.username = username;
|
||||||
this.password = password;
|
this.password = password;
|
||||||
this.balance = balance;
|
this.balance = balance;
|
||||||
this.verificationToken = verificationToken;
|
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) {
|
public void addBalance(BigDecimal amountToAdd) {
|
||||||
|
|
|
@ -12,8 +12,6 @@ public interface UserRepository extends JpaRepository<UserEntity, Long> {
|
||||||
|
|
||||||
Optional<UserEntity> findByEmail(String email);
|
Optional<UserEntity> findByEmail(String email);
|
||||||
|
|
||||||
Optional<UserEntity> findByProviderId(String providerId);
|
|
||||||
|
|
||||||
boolean existsByUsername(String username);
|
boolean existsByUsername(String username);
|
||||||
|
|
||||||
boolean existsByEmail(String email);
|
boolean existsByEmail(String email);
|
||||||
|
|
|
@ -29,11 +29,11 @@ public class UserService {
|
||||||
}
|
}
|
||||||
|
|
||||||
UserEntity user = new UserEntity(
|
UserEntity user = new UserEntity(
|
||||||
createUserDto.getEmail(),
|
createUserDto.getEmail(),
|
||||||
createUserDto.getUsername(),
|
createUserDto.getUsername(),
|
||||||
passwordEncoder.encode(createUserDto.getPassword()),
|
passwordEncoder.encode(createUserDto.getPassword()),
|
||||||
BigDecimal.valueOf(100),
|
BigDecimal.valueOf(100),
|
||||||
RandomStringUtils.randomAlphanumeric(64)
|
RandomStringUtils.randomAlphanumeric(64)
|
||||||
);
|
);
|
||||||
|
|
||||||
return userRepository.save(user);
|
return userRepository.save(user);
|
||||||
|
@ -50,7 +50,7 @@ public class UserService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveUser(UserEntity user) {
|
public void saveUser(UserEntity user) {
|
||||||
userRepository.save(user);
|
userRepository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isVerified(String usernameOrEmail) {
|
public boolean isVerified(String usernameOrEmail) {
|
||||||
|
|
|
@ -19,7 +19,7 @@ public class TransactionController {
|
||||||
@RequestHeader("Authorization") String authToken,
|
@RequestHeader("Authorization") String authToken,
|
||||||
@RequestParam(value = "limit", required = false) Integer limit,
|
@RequestParam(value = "limit", required = false) Integer limit,
|
||||||
@RequestParam(value = "offset", required = false) Integer offset
|
@RequestParam(value = "offset", required = false) Integer offset
|
||||||
) {
|
) {
|
||||||
UserTransactionsDto transactionEntities = this.transactionService.getUserTransactionsDto(authToken, limit, offset);
|
UserTransactionsDto transactionEntities = this.transactionService.getUserTransactionsDto(authToken, limit, offset);
|
||||||
|
|
||||||
return ResponseEntity.ok(transactionEntities);
|
return ResponseEntity.ok(transactionEntities);
|
||||||
|
|
|
@ -28,16 +28,3 @@ logging.level.org.springframework.security=DEBUG
|
||||||
springdoc.swagger-ui.path=swagger
|
springdoc.swagger-ui.path=swagger
|
||||||
springdoc.swagger-ui.try-it-out-enabled=true
|
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
|
|
||||||
|
|
||||||
|
|
|
@ -123,7 +123,7 @@
|
||||||
<p>Klicken Sie auf den folgenden Button, um Ihre E-Mail-Adresse zu bestätigen:</p>
|
<p>Klicken Sie auf den folgenden Button, um Ihre E-Mail-Adresse zu bestätigen:</p>
|
||||||
|
|
||||||
<div style="text-align: center;">
|
<div style="text-align: center;">
|
||||||
<a href="${feUrl}/verify?email-token=${token}" class="button">E-Mail bestätigen</a>
|
<a href="${feUrl}/verify?token=${token}" class="button">E-Mail bestätigen</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="info-box">
|
<div class="info-box">
|
||||||
|
|
|
@ -33,13 +33,6 @@ export const routes: Routes = [
|
||||||
(m) => m.RecoverPasswordComponent
|
(m) => m.RecoverPasswordComponent
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: 'oauth2/callback/github',
|
|
||||||
loadComponent: () =>
|
|
||||||
import('./feature/auth/oauth2/oauth2-callback.component').then(
|
|
||||||
(m) => m.OAuth2CallbackComponent
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
path: 'game/blackjack',
|
path: 'game/blackjack',
|
||||||
loadComponent: () => import('./feature/game/blackjack/blackjack.component'),
|
loadComponent: () => import('./feature/game/blackjack/blackjack.component'),
|
||||||
|
|
|
@ -83,32 +83,7 @@
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<div class="my-4 flex items-center">
|
<div class="mt-6 text-center">
|
||||||
<div class="flex-grow h-px bg-deep-blue-light/30"></div>
|
|
||||||
<span class="px-3 text-xs text-text-secondary">ODER</span>
|
|
||||||
<div class="flex-grow h-px bg-deep-blue-light/30"></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-4">
|
|
||||||
<button
|
|
||||||
(click)="loginWithGithub()"
|
|
||||||
class="w-full py-2.5 px-4 rounded flex items-center justify-center bg-gray-800 hover:bg-gray-700 text-white transition-colors"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
class="h-5 w-5 mr-2"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="currentColor"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0 0 24 12c0-6.63-5.37-12-12-12z"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
Mit GitHub anmelden
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="text-center">
|
|
||||||
<p class="text-sm text-text-secondary">
|
<p class="text-sm text-text-secondary">
|
||||||
Passwort vergessen?
|
Passwort vergessen?
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -4,7 +4,6 @@ import { Router } from '@angular/router';
|
||||||
import { LoginRequest } from '../../../model/auth/LoginRequest';
|
import { LoginRequest } from '../../../model/auth/LoginRequest';
|
||||||
import { AuthService } from '@service/auth.service';
|
import { AuthService } from '@service/auth.service';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { environment } from '@environments/environment';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-login',
|
selector: 'app-login',
|
||||||
|
@ -66,11 +65,6 @@ export class LoginComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
loginWithGithub(): void {
|
|
||||||
this.isLoading.set(true);
|
|
||||||
window.location.href = `${environment.apiUrl}/oauth2/github/authorize`;
|
|
||||||
}
|
|
||||||
|
|
||||||
switchToForgotPassword() {
|
switchToForgotPassword() {
|
||||||
this.forgotPassword.emit();
|
this.forgotPassword.emit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,64 +0,0 @@
|
||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { CommonModule } from '@angular/common';
|
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
|
||||||
import { AuthService } from '@service/auth.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-oauth2-callback',
|
|
||||||
standalone: true,
|
|
||||||
imports: [CommonModule],
|
|
||||||
template: `
|
|
||||||
<div class="min-h-screen bg-deep-blue flex items-center justify-center">
|
|
||||||
<div class="text-center">
|
|
||||||
<h2 class="text-2xl font-bold text-white mb-4">Finishing authentication...</h2>
|
|
||||||
<div
|
|
||||||
class="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-emerald mx-auto"
|
|
||||||
></div>
|
|
||||||
<p *ngIf="error" class="mt-4 text-accent-red">{{ error }}</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`,
|
|
||||||
})
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -13,7 +13,7 @@ export class VerifyEmailComponent implements OnInit {
|
||||||
authService: AuthService = inject(AuthService);
|
authService: AuthService = inject(AuthService);
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
const token = this.route.snapshot.queryParamMap.get('email-token');
|
const token = this.route.snapshot.queryParamMap.get('token');
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
this.router.navigate(['/']);
|
this.router.navigate(['/']);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
import { BehaviorSubject, Observable, tap } from 'rxjs';
|
import { BehaviorSubject, Observable, tap } from 'rxjs';
|
||||||
import { Router, ActivatedRoute } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
import { LoginRequest } from '../model/auth/LoginRequest';
|
import { LoginRequest } from '../model/auth/LoginRequest';
|
||||||
import { RegisterRequest } from '../model/auth/RegisterRequest';
|
import { RegisterRequest } from '../model/auth/RegisterRequest';
|
||||||
import { AuthResponse } from '../model/auth/AuthResponse';
|
import { AuthResponse } from '../model/auth/AuthResponse';
|
||||||
|
@ -17,41 +17,20 @@ const USER_KEY = 'user';
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
private authUrl = `${environment.apiUrl}/auth`;
|
private authUrl = `${environment.apiUrl}/auth`;
|
||||||
private userUrl = `${environment.apiUrl}/users`;
|
private userUrl = `${environment.apiUrl}/users`;
|
||||||
private oauthUrl = `${environment.apiUrl}/oauth2`;
|
|
||||||
|
|
||||||
userSubject: BehaviorSubject<User | null>;
|
userSubject: BehaviorSubject<User | null>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private http: HttpClient,
|
private http: HttpClient,
|
||||||
private router: Router,
|
private router: Router
|
||||||
private route: ActivatedRoute
|
|
||||||
) {
|
) {
|
||||||
this.userSubject = new BehaviorSubject<User | null>(this.getUserFromStorage());
|
this.userSubject = new BehaviorSubject<User | null>(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()) {
|
if (this.getToken()) {
|
||||||
this.loadCurrentUser();
|
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 {
|
public get currentUserValue(): User | null {
|
||||||
return this.userSubject.value;
|
return this.userSubject.value;
|
||||||
}
|
}
|
||||||
|
@ -69,16 +48,6 @@ export class AuthService {
|
||||||
return this.http.post<User>(`${this.authUrl}/register`, registerRequest);
|
return this.http.post<User>(`${this.authUrl}/register`, registerRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
githubAuth(code: string): Observable<AuthResponse> {
|
|
||||||
return this.http.post<AuthResponse>(`${this.oauthUrl}/github/callback`, { code }).pipe(
|
|
||||||
tap((response) => {
|
|
||||||
console.log(response.token);
|
|
||||||
this.setToken(response.token);
|
|
||||||
this.loadCurrentUser();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
logout(): void {
|
logout(): void {
|
||||||
localStorage.removeItem(TOKEN_KEY);
|
localStorage.removeItem(TOKEN_KEY);
|
||||||
localStorage.removeItem(USER_KEY);
|
localStorage.removeItem(USER_KEY);
|
||||||
|
|
Reference in a new issue