diff --git a/.idea/compiler.xml b/.idea/compiler.xml
index ddf8cff..7e43031 100644
--- a/.idea/compiler.xml
+++ b/.idea/compiler.xml
@@ -15,12 +15,9 @@
-
-
-
-
-
-
+
+
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index c4be380..792bbc5 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/build.gradle b/build.gradle
index 63d550e..261b0c7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -52,7 +52,7 @@ dependencies {
implementation("org.jetbrains:annotations:26.0.1")
implementation("org.springframework.boot:spring-boot-starter")
implementation("org.springframework.boot:spring-boot-starter-validation")
- implementation("org.springframework.security.oauth.boot:spring-security-oauth2-autoconfigure:2.6.8")
+ implementation("org.springframework.boot:spring-boot-starter-oauth2-client:3.3.5")
implementation("javax.inject:javax.inject:1")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8")
diff --git a/src/main/java/com/faforever/moderatorclient/api/FafApiCommunicationService.java b/src/main/java/com/faforever/moderatorclient/api/FafApiCommunicationService.java
index 764a5c2..e771c3a 100644
--- a/src/main/java/com/faforever/moderatorclient/api/FafApiCommunicationService.java
+++ b/src/main/java/com/faforever/moderatorclient/api/FafApiCommunicationService.java
@@ -27,7 +27,6 @@
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.JdkClientHttpRequestFactory;
-import org.springframework.security.oauth2.client.resource.OAuth2AccessDeniedException;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.MultiValueMap;
@@ -124,7 +123,7 @@ public void authorize(HydraAuthorizedEvent event) {
try {
meResult = getOne("/me", MeResult.class);
- } catch (OAuth2AccessDeniedException e) {
+ } catch (Exception e) {
log.error("login failed", e);
return;
}
@@ -220,19 +219,16 @@ public void delete(ElideNavigatorOnId> navigator) {
}
}
- @SuppressWarnings("unchecked")
@SneakyThrows
public T getOne(ElideNavigatorOnId navigator) {
return getOne(navigator.build(), navigator.getDtoClass(), Collections.emptyMap());
}
- @SuppressWarnings("unchecked")
@SneakyThrows
public T getOne(String endpointPath, Class type) {
return getOne(endpointPath, type, Collections.emptyMap());
}
- @SuppressWarnings("unchecked")
@SneakyThrows
public T getOne(String endpointPath, Class type, java.util.Map params) {
cycleAvoidingMappingContext.clearCache();
diff --git a/src/main/java/com/faforever/moderatorclient/api/TokenService.java b/src/main/java/com/faforever/moderatorclient/api/TokenService.java
index 25353d7..7bae466 100644
--- a/src/main/java/com/faforever/moderatorclient/api/TokenService.java
+++ b/src/main/java/com/faforever/moderatorclient/api/TokenService.java
@@ -1,77 +1,126 @@
package com.faforever.moderatorclient.api;
import com.faforever.moderatorclient.api.event.HydraAuthorizedEvent;
-import com.faforever.moderatorclient.api.event.TokenExpiredEvent;
import com.faforever.moderatorclient.config.EnvironmentProperties;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
+import org.springframework.http.ResponseEntity;
import org.springframework.http.client.JdkClientHttpRequestFactory;
-import org.springframework.security.oauth2.common.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.OAuth2AccessToken;
+import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
+import java.time.Duration;
+import java.time.Instant;
import java.util.List;
+import java.util.Map;
@Service
@Slf4j
public class TokenService {
- private final ApplicationEventPublisher applicationEventPublisher;
- private RestTemplate restTemplate;
- private EnvironmentProperties environmentProperties;
- private OAuth2AccessToken tokenCache;
-
- public TokenService(ApplicationEventPublisher applicationEventPublisher) {
- this.applicationEventPublisher = applicationEventPublisher;
- }
-
- public void prepare(EnvironmentProperties environmentProperties) {
- this.environmentProperties = environmentProperties;
- this.restTemplate = new RestTemplateBuilder()
- .requestFactory(JdkClientHttpRequestFactory.class)
- .rootUri(environmentProperties.getOauthBaseUrl())
- .build();
- }
-
- @SneakyThrows
- public String getRefreshedTokenValue() {
- if (tokenCache == null || tokenCache.isExpired()) {
- log.info("Token expired, requesting new login");
- applicationEventPublisher.publishEvent(new TokenExpiredEvent());
- } else {
- log.debug("Token still valid for {} seconds", tokenCache.getExpiresIn());
+ private final ApplicationEventPublisher applicationEventPublisher;
+ private RestTemplate restTemplate;
+ private EnvironmentProperties environmentProperties;
+ private OAuth2AccessTokenResponse tokenCache;
+
+ public TokenService(ApplicationEventPublisher applicationEventPublisher) {
+ this.applicationEventPublisher = applicationEventPublisher;
+ }
+
+ public void prepare(EnvironmentProperties environmentProperties) {
+ this.environmentProperties = environmentProperties;
+ this.restTemplate = new RestTemplateBuilder()
+ .requestFactory(JdkClientHttpRequestFactory.class)
+ .rootUri(environmentProperties.getOauthBaseUrl())
+ .build();
+ }
+
+ @SneakyThrows
+ public String getRefreshedTokenValue() {
+ if (tokenCache.getAccessToken().getExpiresAt().isBefore(Instant.now())) {
+ log.info("Token expired, requesting new with refresh token");
+ loginWithRefreshToken(tokenCache.getRefreshToken().getTokenValue(), false);
+ } else {
+ log.debug("Token still valid for {} seconds", Duration.between(Instant.now(), tokenCache.getAccessToken().getExpiresAt()));
+ }
+
+ return tokenCache.getAccessToken().getTokenValue();
+ }
+
+ public void loginWithAuthorizationCode(String code) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+ headers.setAccept(List.of(MediaType.APPLICATION_JSON));
+
+ MultiValueMap map = new LinkedMultiValueMap<>();
+ map.add("code", code);
+ map.add("client_id", environmentProperties.getClientId());
+ map.add("redirect_uri", environmentProperties.getOauthRedirectUrl());
+ map.add("grant_type", "authorization_code");
+
+ Map responseBody = requestToken(headers, map);
+ if (responseBody != null) {
+ parseResponse(responseBody);
+
+ applicationEventPublisher.publishEvent(new HydraAuthorizedEvent());
+ }
+
}
- return tokenCache.getValue();
- }
+ private void parseResponse(Map responseBody) {
+ String accessToken = (String) responseBody.get("access_token");
+ String refreshToken = (String) responseBody.get("refresh_token");
+ Long expiresIn = Long.valueOf(responseBody.get("expires_in").toString());
+
+ tokenCache = OAuth2AccessTokenResponse.withToken(accessToken)
+ .tokenType(OAuth2AccessToken.TokenType.BEARER)
+ .refreshToken(refreshToken)
+ .expiresIn(expiresIn)
+ .build();
+ }
- public void loginWithAuthorizationCode(String code) {
- HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
- headers.setAccept(List.of(MediaType.APPLICATION_JSON_UTF8));
+ public void loginWithRefreshToken(String refreshToken, boolean fireEvent) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
+ headers.setAccept(List.of(MediaType.APPLICATION_JSON));
- MultiValueMap map = new LinkedMultiValueMap<>();
- map.add("code", code);
- map.add("client_id", environmentProperties.getClientId());
- map.add("redirect_uri", environmentProperties.getOauthRedirectUrl());
- map.add("grant_type", "authorization_code");
+ MultiValueMap map = new LinkedMultiValueMap<>();
+ map.add("refresh_token", refreshToken);
+ map.add("client_id", environmentProperties.getClientId());
+ map.add("grant_type", "refresh_token");
+
+ Map responseBody = requestToken(headers, map);
+
+ if (responseBody != null) {
+ parseResponse(responseBody);
+
+ if (fireEvent) {
+ applicationEventPublisher.publishEvent(new HydraAuthorizedEvent());
+ }
+ }
+ }
- HttpEntity> request = new HttpEntity<>(map, headers);
+ private Map requestToken(HttpHeaders headers, MultiValueMap map) {
+ HttpEntity> request = new HttpEntity<>(map, headers);
- tokenCache = restTemplate.postForObject(
- "/oauth2/token",
- request,
- OAuth2AccessToken.class
- );
+ ResponseEntity