-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use refresh token to avoid re-login after 1 hour
- Loading branch information
1 parent
0d6bc69
commit e4e2403
Showing
6 changed files
with
104 additions
and
62 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
141 changes: 95 additions & 46 deletions
141
src/main/java/com/faforever/moderatorclient/api/TokenService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<String, String> 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<String, Object> responseBody = requestToken(headers, map); | ||
if (responseBody != null) { | ||
parseResponse(responseBody); | ||
|
||
applicationEventPublisher.publishEvent(new HydraAuthorizedEvent()); | ||
} | ||
|
||
} | ||
|
||
return tokenCache.getValue(); | ||
} | ||
private void parseResponse(Map<String, Object> 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<String, String> 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<String, String> map = new LinkedMultiValueMap<>(); | ||
map.add("refresh_token", refreshToken); | ||
map.add("client_id", environmentProperties.getClientId()); | ||
map.add("grant_type", "refresh_token"); | ||
|
||
Map<String, Object> responseBody = requestToken(headers, map); | ||
|
||
if (responseBody != null) { | ||
parseResponse(responseBody); | ||
|
||
if (fireEvent) { | ||
applicationEventPublisher.publishEvent(new HydraAuthorizedEvent()); | ||
} | ||
} | ||
} | ||
|
||
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers); | ||
private Map<String, Object> requestToken(HttpHeaders headers, MultiValueMap<String, String> map) { | ||
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers); | ||
|
||
tokenCache = restTemplate.postForObject( | ||
"/oauth2/token", | ||
request, | ||
OAuth2AccessToken.class | ||
); | ||
ResponseEntity<Map<String, Object>> responseEntity = restTemplate.exchange( | ||
"/oauth2/token", | ||
HttpMethod.POST, | ||
request, | ||
new ParameterizedTypeReference<>() { | ||
} | ||
); | ||
|
||
if (tokenCache != null) { | ||
applicationEventPublisher.publishEvent(new HydraAuthorizedEvent()); | ||
return responseEntity.getBody(); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters