spring-projects / spring-projects/spring-security

Provide API to enable OAuth2AuthorizationCodeGrantFilter to resolve the Authentication given the OAuth2AuthorizationCodeAuthenticationToken

Open
#12,658 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage type: enhancement
Dominant language
Java
Stars
9.6k
Forks
6.3k
Avg merge
2d 11h
Merged PRs (30d)
52

Description

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"/>

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at OAuth2AuthorizationCodeGrantFilter.processAuthorizationResponse(...) and trace how securityContextHolderStrategy currently supplies Authentication, then inspect the OAuth2AuthorizedClient construction and repository save call. Done means the requested resolver API supports the resource-owner principal while preserving the existing fallback behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
authentication, backend, security
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.