Reputation: 409
I'm developing a Spring application which acts as an OAuth2 client and Spotify is the resource server. This is my configuration:
spring:
security:
oauth2:
client:
registration:
spotify:
client-id: ...
client-secret: ...
authorization-grant-type: authorization_code
redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
scope: user-read-private, user-read-email
client-name: Spotify
client-alias: spotify
provider:
spotify:
authorization-uri: https://accounts.spotify.com/authorize
token-uri: https://accounts.spotify.com/api/token
user-info-uri: https://api.spotify.com/v1/me
user-name-attribute: display_name
My problem is that I just can't find how to get the refresh token that is sent by Spotify in the response of /api/token
This is how the Spotify response looks like: (Source: https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow)
I tried to implement my own CustomUserService
like this:
.and()
.userInfoEndpoint()
.userService(customUserService)
inside my CustomUserService
I tried to overload the following method: public OAuth2User loadUser(OAuth2UserRequest userRequest)
In this OAuth2UserRequest object I can find the access token but there is absolutely no information about the refresh token:
I'm thinking about I need some additional config to put the refresh_token in the additionalParameters
object but I can't find anything like this.
Is there any way I can get the refresh token in my code and do stuff with that?
Upvotes: 5
Views: 2280
Reputation: 409
So I figured out a way to overcome this. The first thing needed is to include the accessTokenResponseClient
in the security config with a custom implementation.
Security Config:
...
.and()
.tokenEndpoint()
.accessTokenResponseClient(accessTokenResponseClient())
...
And the key part here is to set our CustomTokenResponseConverter
:
@Bean
public OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient() {
DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient =
new DefaultAuthorizationCodeTokenResponseClient();
OAuth2AccessTokenResponseHttpMessageConverter tokenResponseHttpMessageConverter =
new OAuth2AccessTokenResponseHttpMessageConverter();
tokenResponseHttpMessageConverter.setTokenResponseConverter(new CustomTokenResponseConverter());
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
new FormHttpMessageConverter(), tokenResponseHttpMessageConverter));
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
accessTokenResponseClient.setRestOperations(restTemplate);
return accessTokenResponseClient;
}
In this converter it is possible to access the refresh token and for example put it in the additionalParameters
map that is mentioned in the question:
public class CustomTokenResponseConverter implements
Converter<Map<String, String>, OAuth2AccessTokenResponse> {
@Override
public OAuth2AccessTokenResponse convert(Map<String, String> tokenResponseParameters) {
String accessToken = tokenResponseParameters.get(OAuth2ParameterNames.ACCESS_TOKEN);
String refreshToken = tokenResponseParameters.get(OAuth2ParameterNames.REFRESH_TOKEN);
long expiresIn = Long.parseLong(tokenResponseParameters.get(OAuth2ParameterNames.EXPIRES_IN));
Set<String> scopes = Collections.emptySet();
if (tokenResponseParameters.containsKey(OAuth2ParameterNames.SCOPE)) {
String scope = tokenResponseParameters.get(OAuth2ParameterNames.SCOPE);
scopes = Arrays.stream(StringUtils.delimitedListToStringArray(scope, " "))
.collect(Collectors.toSet());
}
Map<String, Object> additionalParameters = new HashMap<>();
additionalParameters.put(OAuth2ParameterNames.REFRESH_TOKEN, refreshToken);
return OAuth2AccessTokenResponse.withToken(accessToken)
.tokenType(OAuth2AccessToken.TokenType.BEARER)
.expiresIn(expiresIn)
.scopes(scopes)
.refreshToken(refreshToken)
.additionalParameters(Collections.unmodifiableMap(additionalParameters))
.build();
}
}
This way it can be accessed among the additionalParameters
in the custom user service the following way:
String refreshToken = (String) userRequest.getAdditionalParameters().get(OAuth2ParameterNames.REFRESH_TOKEN);
Upvotes: 2