5

I'm trying to make a custom spring boot starter that will be used by multiple projects to authenticate with Azure AD. All the Azure AD config has been set up and an individual project hardcoded with all the settings to work with Azure AD works fine too. Now I'm trying to move these settings into a custom Spring Boot starter so that multiple projects can use it. It works for the most part, except for one thing: moving the bean config for a custom AADAppRoleStatelessAuthenticationFilter. If I leave my custom implementation (CustomAADAppRoleStatelessAuthFilter) hardcoded in the actual implementing project, everything works and only CustomAADAppRoleStatelessAuthFilter is created, but as soon as I move it into the custom starter, I only ever get AADAppRoleStatelessAuthenticationFilter instead.

Note that my CustomAADAppRoleStatelessAuthFilter extends the starter's AADAppRoleStatelessAuthenticationFilter.

The autoconfig for AADAppRoleStatelessAuthenticationFilter in the azure-spring-boot project (https://github.com/microsoft/azure-spring-boot/blob/master/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/AADAuthenticationFilterAutoConfiguration.java) is:

@Bean
@ConditionalOnMissingBean(AADAppRoleStatelessAuthenticationFilter.class)
@ConditionalOnProperty(prefix = PROPERTY_PREFIX, value = PROPERTY_SESSION_STATELESS, havingValue = "true")
public AADAppRoleStatelessAuthenticationFilter azureADStatelessAuthFilter(ResourceRetriever resourceRetriever) {
    //bean details omitted
}

My custom autoconfig that should replace the above is as follows:

@Bean
@ConditionalOnMissingBean(AADAppRoleStatelessAuthenticationFilter.class)
@ConditionalOnProperty(prefix = PROPERTY_PREFIX, value = PROPERTY_SESSION_STATELESS, havingValue = "true")    
public AADAppRoleStatelessAuthenticationFilter customAADAppRoleStatelessAuthFilter(
        ResourceRetriever resourceRetriever) {
    return new CustomAADAppRoleStatelessAuthenticationFilter(/*details omitted*/);
}

No amount of @AutoConfigureBefore(AADAuthenticationFilterAutoConfiguration.class) works.

Also, if I change the condition on my custom bean to be the subtype (@ConditionalOnMissingBean(CustomAADAppRoleStatelessAuthFilter.class)), BOTH types get created, and I can autowire my CustomAwareAADAppRoleStatelessAuthFilter and put it in my WebSecurityConfigurerAdapter, but things STILL won't work. I debugged things and found that the CustomAADAppRoleStatelessAuthFilter is the only bean of the ADAppRoleStatelessAuthenticationFilter type in my spring security filter chain, but that once the 'end of the additional filter chain' has completed and the 'original chain proceeds', I find that the ADAppRoleStatelessAuthenticationFilter has fired! And of course it throws an error because my CustomAADAppRoleStatelessAuthFilter has already done things to customize the UserPrincipal. I can't figure out where the ADAppRoleStatelessAuthenticationFilter is getting added to any filter chain, and even if I mark my CustomAADAppRoleStatelessAuthFilter bean with @Primary, the starter ADAppRoleStatelessAuthenticationFilter will still be used instead.

The only 'solutions' that have worked are to define the CustomAADAppRoleStatelessAuthFilter in the actual implementing project instead of the custom starter project, or to exclude the AADAuthenticationFilterAutoConfiguration in my actual implementing project's @SpringBootApplication annotation (Not even excluding it the property-based way works).

Is there a way to make AADAuthenticationFilterAutoConfigurations ADAppRoleStatelessAuthenticationFilter bean definition back off? 'Cause @AutoConfigureBefore(AADAuthenticationFilterAutoConfiguration.class) on my custom auto configuration class that has my CustomAADAppRoleStatelessAuthFilter definition doesn't work, and having all the implementing projects explicitly exclude AADAuthenticationFilterAutoConfiguration isn't the most ideal solution (although at least with that solution they don't all need to declare their own bean definition for CustomAADAppRoleStatelessAuthFilter).

AForsberg
  • 1,074
  • 2
  • 13
  • 25
  • I have a similar problem. If you get the solution, please let me know. https://stackoverflow.com/questions/60874517/spring-boot-azuread-filter-autoconfiguration – Gabriel García Garrido Mar 27 '20 at 08:42

2 Answers2

2

Have you tried using @Order and assign your custom bean a higer precedence. By default all the beans gets lowest priority (Ordered.LOWEST_PRECEDENCE) losing to any other specified order value.

@Order(Ordered.LOWEST_PRECEDENCE - 1)
@Bean
@ConditionalOnMissingBean(AADAppRoleStatelessAuthenticationFilter.class)
@ConditionalOnProperty(prefix = PROPERTY_PREFIX, value = PROPERTY_SESSION_STATELESS, havingValue = "true")    
public AADAppRoleStatelessAuthenticationFilter customAADAppRoleStatelessAuthFilter(
    ResourceRetriever resourceRetriever) {
    return new CustomAADAppRoleStatelessAuthenticationFilter(/*details omitted*/);
}

Can you try putting the @Order(Ordered.LOWEST_PRECEDENCE - 1) like i mentioned above? Your bean should then take precedence over the other.

Akhil Bojedla
  • 1,968
  • 12
  • 19
  • I had not thought of that before, and trying it now sort of works, as in, I don't get the errors I was getting before, but the original AADAppRoleStatelessAuthenticationFilter from the Microsoft starter STILL ends up on the context and ends up still getting used somehow, as evidenced by the fact that the custom behavior we added in the CustomAADAppRoleStatelessAuthenticationFilter is no longer taking effect. It seems like things just don't work unless I remove the Microsoft autoconfig file completely from the equation or else define the bean in the implementing project. – AForsberg Jan 24 '20 at 14:25
  • Thats weird. The Authentication filters generally work as a chain (I'm not sure if it is the same in this case). So may be it is that all implementations are used as a filter chain. Have you tried removing the microsoft's `AADAppRoleStatelessAuthenticationFilter` bean definition from application context manually? – Akhil Bojedla Jan 24 '20 at 16:27
  • I did try that as part of the run command, but it seemed like that was too late. It's really weird, it seems like spring is aggressively picking up all filters (we've seen it happen with this filter and one of our own) and running them even though we define our own WebSecurityConfigurerAdapter. The filters don't show up in the spring security filter chain, but when debugging the app, they seem to run AFTER the spring security filter chain, as part of the main chain? Haven't figured out how it's doing that but only by making sure the beans aren't created in the first place can we prevent that. – AForsberg Jan 24 '20 at 19:24
1

Have you tried adding @Primary to your bean?

Gandalf
  • 9,648
  • 8
  • 53
  • 88