I've developed a basic oauth/oidc example using SpringBoot 2.1.7 with Okta providing authentication services. Here is my Gradle dependency setup for reference:
plugins {
id 'org.springframework.boot' version '2.1.7.RELEASE'
id 'java'
}
apply plugin: 'io.spring.dependency-management'
sourceCompatibility = '1.8'
configurations {
developmentOnly
runtimeClasspath {
extendsFrom developmentOnly
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.okta.spring:okta-spring-boot-starter:1.2.1'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
}
All elements on the Okta side are configured properly and the example works as expected. Pretty much a "hello world" type demo here. I would like to add a custom authentication provider that executes after all other AuthenticationProvider's provided by Spring. I have stepped through the code using my debugger and noticed Spring auto configures several AuthenticationProviders. They are:
- AnonymousAuthenticationProvider
- OAuth2LoginAuthenticationProvider
- OidcAuthorizationCodeAuthenticationProvider
- OAuth2AuthorizationCodeAuthenticationProvider
- JwtAuthenticationProvider
I would like to run my authentication provider in the 6th position. I tried the following WebSecurityConfig that did not work even though the configure(AuthenticationManagerBuilder authBuilder) method fires:
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
@Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests()
.anyRequest().authenticated()
.and()
.oauth2Login()
.successHandler(customOauthLoginSuccessHandler())
.failureHandler(customOauthLoginFailureHandler())
.and()
.oauth2Client();
http.csrf().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
@Bean
public CustomOauthLoginSuccessHandler customOauthLoginSuccessHandler()
{
CustomOauthLoginSuccessHandler handler = new CustomOauthLoginSuccessHandler();
return handler;
}
@Bean
public CustomOauthLoginFailureHandler customOauthLoginFailureHandler()
{
CustomOauthLoginFailureHandler handler = new CustomOauthLoginFailureHandler();
handler.setUseForward(true);
handler.setDefaultFailureUrl("/oautherror");
return handler;
}
}
My Authentication provider never executes. Here is my custom AP:
@Component
public class CustomAuthenticationProvider implements AuthenticationProvider
{
private Logger logger = LoggerFactory.getLogger(CustomAuthenticationProvider.class);
private CustomOidcUserService customOidcUserService;
public CustomAuthenticationProvider(CustomOidcUserService customOidcUserService)
{
this.customOidcUserService = customOidcUserService;
}
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException
{
logger.info("CustomAuthenticationProvider executing...");
Object user = authentication.getPrincipal();
if (user instanceof DefaultOidcUser)
{
logger.info("principal is instanceof DefaultOidcUser");
DefaultOidcUser authToken = (DefaultOidcUser) user;
this.customOidcUserService.loadUserByUsername(authToken.getClaims().get("preferred_username").toString());
// add additional info to the Authentication object
}
return authentication;
}
@Override
public boolean supports(Class<?> authentication)
{
return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication);
}
}
My main goal is to add additional information about the authenticated user that comes from our legacy Oracle database. It is NOT an option to add this additional information to the Okta side (in the cloud).
Note that I was able to easily add success and failure handlers into the authentication path. I feel like the inherent nature of SpringBoot's auto-config may be getting in my way. Just need to know how to get around this.
Additional info, yml file:
okta:
oauth2:
issuer: https://myhost.okta.com/oauth2/default
client-id: 123456AAABBBCCC
client-secret: AAAAAAAAABBBBBBBBBBB