I do believe Spring Security Team performed a great work to allow us, application developers, configure sophisticated security things in simple way, however I have found out that sometimes it is required to somehow intercept authentication providers and http filters, and it is much simpler to take advantage of BeanPostProcessor capabilities rather than to reproduce OoTB configuration. Unfortunately in some cases it is not possible due to "oversights" in Spring Security, please check some examples below.

AbstractDaoAuthenticationConfigurer#configure

    @Override
    public void configure(B builder) throws Exception {
                // here BPP is required to return instance of DaoAuthenticationProvider 
                // instead of more common as sufficient AuthenticationProvider
        this.provider = postProcess(this.provider);
        builder.authenticationProvider(this.provider);
    }

LdapAuthenticationProviderConfigurer#getLdapAuthoritiesPopulator

    private LdapAuthoritiesPopulator getLdapAuthoritiesPopulator() {
        if (this.ldapAuthoritiesPopulator != null) {
            return this.ldapAuthoritiesPopulator;
        }
        DefaultLdapAuthoritiesPopulator defaultAuthoritiesPopulator = new DefaultLdapAuthoritiesPopulator(
                this.contextSource, this.groupSearchBase);
        defaultAuthoritiesPopulator.setGroupRoleAttribute(this.groupRoleAttribute);
        defaultAuthoritiesPopulator.setGroupSearchFilter(this.groupSearchFilter);
        defaultAuthoritiesPopulator.setSearchSubtree(this.groupSearchSubtree);
        defaultAuthoritiesPopulator.setRolePrefix(this.rolePrefix);
        this.ldapAuthoritiesPopulator = postProcess(defaultAuthoritiesPopulator);

                // here we are potentially returning bean which was not post processed
        return defaultAuthoritiesPopulator;
    }

TrustedIssuerJwtAuthenticationManagerResolver#resolve

        @Override
        public AuthenticationManager resolve(String issuer) {
            if (this.trustedIssuer.test(issuer)) {
                AuthenticationManager authenticationManager = this.authenticationManagers.computeIfAbsent(issuer,
                        (k) -> {
                            this.logger.debug("Constructing AuthenticationManager");
                            JwtDecoder jwtDecoder = JwtDecoders.fromIssuerLocation(issuer);

                                                          // here we are not even trying to post process JwtAuthenticationProvider
                            return new JwtAuthenticationProvider(jwtDecoder)::authenticate;
                        });
                this.logger.debug(LogMessage.format("Resolved AuthenticationManager for issuer '%s'", issuer));
                return authenticationManager;
            }
            else {
                this.logger.debug("Did not resolve AuthenticationManager since issuer is not trusted");
            }
            return null;
        }

OAuth2ResourceServerConfigurer#configure

    public void configure(H http) {
        BearerTokenResolver bearerTokenResolver = getBearerTokenResolver();
        this.requestMatcher.setBearerTokenResolver(bearerTokenResolver);
        AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
        if (resolver == null) {
            AuthenticationManager authenticationManager = getAuthenticationManager(http);
            resolver = (request) -> authenticationManager;
        }

        BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
        filter.setBearerTokenResolver(bearerTokenResolver);
        filter.setAuthenticationEntryPoint(this.authenticationEntryPoint);

                // here BPP is required to return instance of BearerTokenAuthenticationFilter
                // the problem is BearerTokenAuthenticationFilter is final, however in case
                // of http filters it is not clear how to use addFilterBefore/addFilterAfter
                // keeping in mind that http filters could be post processed

        filter = postProcess(filter);
        http.addFilter(filter);
    }

Comment From: sjohnr

Hi @andreybpanfilov, thanks for your interest in the project!

Would you be able to provide a more thorough explanation of the specific problem you're facing and a suggested improvement? I'm not sure I'm clear on the specific problem you're detailing here and how it could be resolved. Also, it seems you are pointing out multiple issues, and I think it would be helpful to narrow focus to a single location within the code-base to start off with. Could this issue be updated to point to your most problematic area? We could potentially open other issues later once we make some progress.

Also, is there anything in your case that could be solved by using an ObjectPostProcessor?

Comment From: andreybpanfilov

Hi @sjohnr ,

I do understand that issue looks "too general", however it is based on expectation that if ObjectPostProcessor returns something the caller side must use the value returned.

btw, the initial case is following:

I'm trying to implement some sophisticated AAA process with the following features/requirements: - multiple authentication sources could be configured for the single system - users may be allowed to use multiple authentication sources, single authentication source only or may not be allowed to authenticate at all (e.g.: internal system user may not authenticate) - depending on authentication source and user's environment (network segment, time, etc) we may either escalate or deescalate user's privileges or ignore authentication attempt

At current moment implementation of features described above faces with the following difficulties: - we need to either rely on concrete implementations of AuthenticationProvider, Filter and SecurityConfigurerAdapter or build everything from scratch - both options do not look good from maintenance perspective - I do believe it would be possible to intercept AuthenticationManager, however in this case we actually need to setup the chain of AuthenticationManagers - seems not to be a good alternative because it complicates configuration, moreover it won't work in some cases

On the other hand the only thing we actually need is ability to somehow intercept the result (or exception thrown) of AuthenticationProvider#authenticate.

Comment From: sjohnr

Hi @andreybpanfilov, thanks for your response. I think I'm starting to see what you're working towards. I have a couple of questions still.

I do understand that issue looks "too general", however it is based on expectation that if ObjectPostProcessor returns something the caller side must use the value returned.

Is that not the case? In your examples, it looks like objects that are post processed are used.

Also, having read through the list of issues you raise again, I'm still at a point where I feel there are too many items to address in a single issue.

On the other hand the only thing we actually need is ability to somehow intercept the result (or exception thrown) of AuthenticationProvider#authenticate.

Before addressing other issues you raised, I'm wondering: Are events or AOP/AspectJ not a better fit for your use case?

Comment From: andreybpanfilov

Hi @sjohnr!

Is that not the case? In your examples, it looks like objects that are post processed are used.

Currently I do observe following "patterns": - sometimes everything works as expected, for example in AnonymousConfigurer authenticationProvider gets post processed and it does not rely on AnonymousAuthenticationProvider implementation (from our perspective anonymous subjects actually may differ due to environment, for example network segment anonymous user came from), and such implementation allows us to separate HttpSecurity configuration from our AAA process - sometimes SecurityConfigurer post processes AuthenticationProvider but at the same time it relies on concrete implementation of AuthenticationProvider, for example AbstractDaoAuthenticationConfigurer expects that AuthenticationProvider is an instance of DaoAuthenticationProvider, which in turn forces us to rely on concrete implementation too - sometimes AuthenticationProvider does not get post processed at all (TrustedIssuerJwtAuthenticationManagerResolver case described in my issue) - sometimes implementation of AuthenticationProvider is final, for example OpaqueTokenAuthenticationProvider is both final and does not gets post processed, that forces us to rewrite everything from scratch

Also, having read through the list of issues you raise again, I'm still at a point where I feel there are too many items to address in a single issue.

I believe it is up to Spring Security Team how to properly interpret this issue, may be it even does not worth an attention. We are just looking for an option to intercept result of AuthenticationProvider#authenticate call, sometimes it is easy enough, sometimes it is hard or even not possible.

Before addressing other issues you raised, I'm wondering: Are events or AOP/AspectJ not a better fit for your use case?

Initially I was thinking about less invasive solution based on something that is already implemented, if discuss other options my thoughts are the following: - AOP/AspectJ will require load-time weaving because AuthenticationProviders and Filters are actually not spring beans, moreover it will require some hacky tricks to access spring infrastructure - events - it is bit unclear what do you mean: ProviderManager publishes AuthenticationSuccessEvent at the end of authentication process but we are looking for an option to influence on that process, moreover BearerTokenAuthenticationFilter, for example, does not publish this event if AuthenticationManagerResolver has been configured - at first glance something like: ```java protected Authentication doAuthenticate(AuthenticationProvider provider, Authentication authentication) { AuthenticationHandler handler = getAuthenticationHandler(); return handler.authenticate(provider, authentication); }

interface AuthenticationHandler {

    default Authentication authenticate(AuthenticationProvider provider, Authentication authentication) {
        return provider.authenticate(authentication);
    }

}
```
 in ProviderManager would be helpful, but currently there are a plenty of places where AuthenticationManager is used as functional interface (TrustedIssuerJwtAuthenticationManagerResolver case for example)

Comment From: sjohnr

Thanks @andreybpanfilov. Appreciate you going into good detail for me!

I believe it is up to Spring Security Team how to properly interpret this issue, may be it even does not worth an attention.

This is good discussion, and it's definitely worth attention!

ProviderManager publishes AuthenticationSuccessEvent at the end of authentication process but we are looking for an option to influence on that process

You're right, I wasn't thinking events and then threw it in as an example of "intercepting" but it's not useful for your case.


If I were to distill your overall challenge into as small a focal point as I can, would it be that you're trying to "wrap" the AuthenticationManager (and for argument's sake, let's just say specifically the ProviderManager) to be able to influence the decision it makes regardless of which provider is used internally?

(And let's assume for now the TrustedIssuerJwtAuthenticationManagerResolver is a special case that probably would need to be a separate issue.)

And perhaps more specifically, your challenge is that it's difficult to do this with the current hook of using an ObjectPostProcessor, because it requires you to return an instance that is the same concrete type as the original instance?

For example, in order to wrap a pre-configured ProviderManager with an existing list of AuthenticationProviders, you need to configure your own ProviderManager? (Which, ultimately doesn't even solve your problem anyway since you want custom code wrapping the ProviderManager...)

Am I on the right track? :smile:

Comment From: sjohnr

@andreybpanfilov just checking to see if you had a chance to review my last comment?

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: spring-projects-issues

Closing due to lack of requested feedback. If you would like us to look at this issue, please provide the requested information and we will re-open the issue.