I'm using spring-boot-starter-oauth2-client
to authenticate my user with Google. This works well and I can sign in and get valid access and refresh token as expected.
I'm creating the access token as such:
public class TokenServiceImpl implements TokenService {
private final OAuth2AuthorizedClientService clientService;
@Override
public GoogleCredentials credentials() {
final var accessToken = getAccessToken();
return getGoogleCredentials(accessToken);
}
private GoogleCredentials getGoogleCredentials(String accessToken) {
return GoogleCredentials
.newBuilder()
.setAccessToken(new AccessToken(accessToken, null))
.build();
}
private String getAccessToken() {
final var oauthToken = (OAuth2AuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
return clientService.loadAuthorizedClient(
oauthToken.getAuthorizedClientRegistrationId(),
oauthToken.getName()).getAccessToken().getTokenValue();
}
}
The token is ultimately being used in the Google Photo API client as such
private PhotosLibraryClient getClient() {
final var settings =
PhotosLibrarySettings
.newBuilder()
.setCredentialsProvider(FixedCredentialsProvider.create(tokenService.credentials()))
.build();
return PhotosLibraryClient.initialize(settings);
}
The problem is that the token will expire after a short period and I'd like to refresh it to keep it active.
I'm unsure what pattern of methods I can use to do this, without having to write the entire OAuth flow (defeating the purpose of something like the Spring oauth2-client
).
So far I have no other token/security/filter logic in my application.
Do I just need to write it all out manually, or is there another way I can do this?
The OAuth2AuthorizedClientManager will take care of refreshing your access token for you, assuming you get a refresh token along with your access token. The doco for OAuth2AuthorizedClientManager is at
https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2client
When configuring your OAuth2AuthorizedClientManager, make sure you have included refreshToken in the OAuth2AuthorizedClientProvider...
@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {
OAuth2AuthorizedClientProvider authorizedClientProvider =
OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken()
.build();
DefaultOAuth2AuthorizedClientManager authorizedClientManager =
new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Assuming the `username` and `password` are supplied as `HttpServletRequest` parameters,
// map the `HttpServletRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
authorizedClientManager.setContextAttributesMapper(contextAttributesMapper());
return authorizedClientManager;
}
You then use the OAuth2AuthorizedClientManager to get the access token. The sample from the spring doco is below...
@Controller
public class OAuth2ClientController {
@Autowired
private OAuth2AuthorizedClientManager authorizedClientManager;
@GetMapping("/")
public String index(Authentication authentication,
HttpServletRequest servletRequest,
HttpServletResponse servletResponse) {
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
.principal(authentication)
.attributes(attrs -> {
attrs.put(HttpServletRequest.class.getName(), servletRequest);
attrs.put(HttpServletResponse.class.getName(), servletResponse);
})
.build();
OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);
OAuth2AccessToken accessToken = authorizedClient.getAccessToken();
...
return "index";
}
}
If the current accessToken has expired, this will automatically request a new accessToken using the previously obtained refreshToken.