Please provide an API to enable OAuth2AuthorizationCodeGrantFilter to resolve the Authentication, given the OAuth2AuthorizationCodeAuthenticationToken. If present, this would be used to obtain the Authentication used to:
- construct the OAuth2AuthorizedClient; and
- save it via OAuth2AuthorizedClientRepository
E.g.,
public void setAuthenticationResolver(Function<OAuth2AuthorizationCodeAuthenticationToken, Authentication> resolver) {
this.authenticationResolver = resolver;
}
This would be used in processAuthorizationResponse(...) as follows:
Authentication currentAuthentication;
if (authenticationResolver != null) {
currentAuthentication = authenticationResolver .apply(authenticationResult);
else {
// support the existing behaviour
currentAuthentication = this.securityContextHolderStrategy.getContext().getAuthentication();
}
String principalName = (currentAuthentication != null) ? currentAuthentication.getName() : "anonymousUser";
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
authenticationResult.getClientRegistration(), principalName, authenticationResult.getAccessToken(),
authenticationResult.getRefreshToken());
this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, currentAuthentication, request,
response);
Current Behavior
The current behaviour uses the securityContextHolderStrategy to resolve the authentication.
Context
This is related to https://stackoverflow.com/questions/75254595
I use JavaMail to send email via SMTP and monitor replies via IMAP for multiple accounts. This uses username and password authentication. I now need to add support for OAuth2, using the code grant flow.
I want to associate the email address of the user who granted access to their mailbox with the corresponding OAuth2AuthorizedClient, in order to get the appropriate access token when connecting to a mailbox.
The current behaviour of OAuth2AuthorizationCodeGrantFilter is insufficient, as it uses the securityContextHolderStrategy to determine the Authentication. In my case, this is the user logged into the webapp, whereas I want it to be the email address of the resource owner.
To work around this, I've duplicated OAuth2AuthorizationCodeGrantFilter and added:
private Authentication getAuthentication(OAuth2AuthorizationCodeAuthenticationToken authenticationResult) {
ClientRegistration clientRegistration = authenticationResult.getClientRegistration();
JwtDecoder decoder = tokenDecoderFactory.createDecoder(clientRegistration);
String idToken = (String) authenticationResult.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN);
if (StringUtils.isEmpty(idToken)) {
OAuth2Error error = new OAuth2Error(
"invalid_id_token", "Missing (required) ID Token in Token Response for Client Registration: "
+ clientRegistration.getRegistrationId(), null);
throw new OAuth2AuthenticationException(error, error.toString());
}
Jwt jwt = decoder.decode(idToken);
String email = jwt.getClaimAsString("email");
if (StringUtils.isEmpty(email)) {
OAuth2Error error = new OAuth2Error(
"invalid_id_token", "Missing email in Token Response for Client Registration: "
+ clientRegistration.getRegistrationId(), null);
throw new OAuth2AuthenticationException(error, error.toString());
}
return new PreAuthenticatedAuthenticationToken(email, null, Collections.emptyList());
}
This is invoked within processAuthorizationResponse(...) follows:
Authentication authentication = getAuthentication(authenticationResult);
String principalName = authentication.getName();
OAuth2AuthorizedClient authorizedClient = new OAuth2AuthorizedClient(
authenticationResult.getClientRegistration(), principalName, authenticationResult.getAccessToken(),
authenticationResult.getRefreshToken());
this.authorizedClientRepository.saveAuthorizedClient(authorizedClient, authentication, request, response);
To override the default code grant filter, I've added:
<security:custom-filter before="OAUTH2_AUTHORIZATION_CODE_GRANT_FILTER"
ref="myOauth2AuthorizationCodeGrantFilter"/>