Reputation: 3030
I'm trying to solve a puzzle with enabling OAuth2-based authentication for my Feign client that is used for cross-service communication.
In normal cases, when a user pushes a request through API, I'm able to take all authentication details needed from the Spring's SecurityContextHolder
(as it normally does its job and fills all the details objects) and enhance Feign's Request
as follows:
public class FeignAccessTokenRelyInterceptor implements RequestInterceptor {
private static final Logger log = LoggerFactory.getLogger(FeignAccessTokenRelyInterceptor.class);
@Override
public void apply(RequestTemplate template) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
String tokenValue = null;
if (auth.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
tokenValue = details.getTokenValue();
}
if (tokenValue == null) {
log.warn("Current token value is null");
return;
}
template.header("Authorization", "Bearer " + tokenValue);
}
}
}
However, when it comes to scheduled calls that are triggered inside the system, the SecurityContext
is obviously empty. I'm filling it with UserInfoTokenServices
by manually requesting the access token by client credentials flow beforehand and loading user details:
OAuth2Authentication authentication = userInfoTokenServices.loadAuthentication(accessToken);
SecurityContextHolder.getContext().setAuthentication(authentication);
But such construction doesn't fill OAuth2Authentication.details
, on which I rely to get an access token. I tried extending OAuth2AuthenticationDetails
, but the only constructor requires HttpServletRequest
which is hard to get inside a scheduled task, and making a dummy instance of it feels like a bad choice.
So far, I see the only adequate option to make a separate custom implementation of details holder and pass it to OAuth2Authentication
along with the access token I have. And then pick it up in FeignAccessTokenRelyInterceptor
.
The question
Maybe there are some other options where I can store my access token in the security context and reliably get it from there, in order not to produce new custom classes?
Will be glad for any help.
Some related links I've studied:
Upvotes: 4
Views: 2573
Reputation: 3030
For the history, hope that'd help someone like me struggling with that.
I didn't find a better way than the initial one and made a custom InternalOAuth2Details
to hold a token value obtained from Spring's OAuth services. Then, in the FeignAccessTokenRelyInterceptor
I simply check if current details are InternalOAuth2Details
and try to get a token value from it, as follows:
@Override
public void apply(RequestTemplate template) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null){
String tokenValue = null;
if (auth.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) auth.getDetails();
tokenValue = details.getTokenValue();
} else if (auth.getDetails() instanceof InternalOAuth2Details) {
InternalOAuth2Details details = (InternalOAuth2Details) auth.getDetails();
tokenValue = details.getTokenValue();
}
if (tokenValue == null) {
log.warn("Current token value is null");
return;
}
template.header("Authorization", "Bearer " + tokenValue);
}
}
I bet it isn't the best solution, but it seems to work quite stable as of now.
Upvotes: 3