Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add getDelegate() method to ObservationAuthenticationManager #16071

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

mtommila
Copy link

Add a getDelegate() method to ObservationAuthenticationManager to get the underlying AuthenticationManager.

This may be useful when configuring security if you need to get the ProviderManager to customize it. If you try to get it from HttpSecurity.getSharedObject(AuthenticationManager.class) but the ProviderManager has been wrapped with an ObservationAuthenticationManager then there's no easy way to get the ProviderManager otherwise.

@pivotal-cla
Copy link

@mtommila Please sign the Contributor License Agreement!

Click here to manually synchronize the status of this Pull Request.

See the FAQ for frequently asked questions.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Nov 12, 2024
@pivotal-cla
Copy link

@mtommila Thank you for signing the Contributor License Agreement!

@jzheaux jzheaux self-assigned this Nov 20, 2024
@jzheaux
Copy link
Contributor

jzheaux commented Nov 20, 2024

Thanks for the suggestion, @mtommila. Instead of adding a getter to ObservationAuthenticationManager, you can construct an AuthenticationManager yourself and wrap it:

@Bean 
AuthenticationManager authenticationManager(ObservationRegistry registry) {
    // ... prepare authentication providers
    ProviderManager toObserve = new ProviderManager(providers);
    return new ObservationAuthenticationManager(registry, toObserve);
}

In this way, you'd have direct access to the underlying manager in addition to having more access to its configuration.

If that doesn't seem to help, can you tell me more about your situation and what you want to achieve by getting the delegate AuthenticationManager?

@jzheaux jzheaux added status: waiting-for-feedback We need additional information before we can continue in: core An issue in spring-security-core type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Nov 20, 2024
@mtommila
Copy link
Author

Hello,

Constructing my own AuthenticationManager and especially all the authentication providers is exactly what I want to avoid. The situation is that we are using a third-party product (the CoreMedia content management system), which is built on the Spring framework, including Spring security. It provides a Spring security setup, which we are customizing to add OAuth2 OIDC SSO to it.

So, the AuthenticationManager and authentication providers are set up elsewhere (by the underlying CoreMedia system, and by our customization of adding OAuth2 SSO to it). But what I need to customize further is the OAuth2 part slightly so that it works with the authentication provider, and the underlying CoreMedia system. It needs a bit of glue logic to get it to work.

In my glue logic, I need to get the existing, pre-configured AuthenticationManager (that is a ProviderManager) and from there, get the AuthenticationProviders to find the one of them that is the OidcAuthorizationCodeAuthenticationProvider and wrap it with a wrapper that contains my custom glue logic.

The logic is something as follows:

  public SecurityFilterChain aadSecurityFilterChain(HttpSecurity http) throws Exception {
    http.securityMatcher(loginUrl, redirectUrl).authorizeHttpRequests((authorize) -> authorize.requestMatchers(loginUrl, redirectUrl).permitAll()).oauth2Login((oauth2Login) -> oauth2Login.authorizationEndpoint((authorizationEndpoint) -> authorizationEndpoint.baseUri(baseUrl)).loginProcessingUrl(redirectUrl)
      .successHandler(successHandler)).sessionManagement((sessionManagement) -> sessionManagement.sessionAuthenticationStrategy(sessionAuthenticationStrategy));
    http.with(new SecurityConfigurerAdapter<>() {
      @Override
      public void configure(HttpSecurity http) {
        wrapAuthenticationProvider(http.getSharedObject(AuthenticationManager.class));
      }
    }, Customizer.withDefaults());
    return http.build();
  }

  @Bean
  public AADAuthenticationProviderWrapper aadProviderWrapper() {
    return new AADAuthenticationProviderWrapper();
  }

  private void wrapAuthenticationProvider(AuthenticationManager authenticationManager) {
    ProviderManager providerManager = (ProviderManager) authenticationManager;
    ListIterator<AuthenticationProvider> iterator = providerManager.getProviders().listIterator();
    boolean wrapped = false;
    while (iterator.hasNext()) {
      AuthenticationProvider authenticationProvider = iterator.next();
      if (authenticationProvider instanceof OidcAuthorizationCodeAuthenticationProvider) {
        AADAuthenticationProviderWrapper aadAuthenticationProviderWrapper = aadProviderWrapper();
        aadAuthenticationProviderWrapper.setAuthenticationProvider(authenticationProvider);
        iterator.set(aadAuthenticationProviderWrapper);
        wrapped = true;
      }
    }
    if (!wrapped) {
      throw new IllegalArgumentException("OidcAuthorizationCodeAuthenticationProvider not found");
    }
  }

Is there a better way to wrap the existing OIDC authentication provider?

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Nov 21, 2024
@jzheaux
Copy link
Contributor

jzheaux commented Nov 21, 2024

Gotcha. One of the strategies that can help when post-processing code that you don't have control over is registering an ObjectPostProcessor. It's like BeanPostProcessor, but for individual objects managed by the DSL.

It looks something like this:

http
    .oauth2Login((oauth2) -> oauth2
        .addObjectPostProcessor(new ObjectPostProcessor<>() {
            @Override
            public AuthenticationProvider postProcess(AuthenticationProvider provider) {
                if (provider instanceof OidcAuthorizationCodeAuthenticationProvider oidc) {
                    return wrap(oidc);
                }
                return provider; 
            }
        }
    )

What you return from the post-processor is the authentication provider that will get added. How well does that work for you?

@mtommila
Copy link
Author

Hello,

Yes, adding such an ObjectPostProcessor would seem to work. Thank you, I was not aware of such a possibility.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core An issue in spring-security-core status: feedback-provided Feedback has been provided type: enhancement A general enhancement
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants