Jack
Jack

Reputation: 13

Issue configuring Spring web app(non-boot) with both oauth2 authorization code and client credential flow

Currently the spring(non spring boot, no application.properties) mvc app with a web.xml has both view pages and rest endpoints resource.

The view portion of the app needs to be protected by oidc openid login with idp generated login page.

And the rest endpoints (specific rest url string mapping) need to be protected by client credential grant for some client apps to access.

Both view and rest resource lives in /app/ servlet mapping*

I was able to configure the spring security to have the view pages protected via oidc login, upon request to root of the app, I was presented with okta login page, upon entering the username/pass, I was able to get to the main page of the app.

What I currently stuck is how to set up the client credential flow for spring security.

I wasn't able to find any resource online about setting up multiple flow in one single app, any input is appreicated.

Thank you.

Below is my working config only for oidc.

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) {
        web.ignoring()
                .antMatchers("/css/**", "/js/**")
                .antMatchers("/public/healthcheck/status")
                .antMatchers("/app/rest/endpoint**");//this is one of the rest endpoint I was trying to protect via client credential flow, for now I have it bypass security or else it lives behind the oidc login
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests(authorize -> authorize
                        .antMatchers("/app/**").authenticated()
                )
                .oauth2Login(oauth2 -> oauth2
                        .redirectionEndpoint(redirection -> redirection
                                .baseUri("/login/oauth2/code/okta")
                        )
                );
    }

    @Bean
    public ClientRegistrationRepository clientRegistrationRepository() {
        return new InMemoryClientRegistrationRepository(this.oktaClientRegistration());
    }

    @Bean
    public OAuth2AuthorizedClientService authorizedClientService(
            ClientRegistrationRepository clientRegistrationRepository) {
        return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository);
    }

    @Bean
    public OAuth2AuthorizedClientRepository authorizedClientRepository(
            OAuth2AuthorizedClientService authorizedClientService) {
        return new AuthenticatedPrincipalOAuth2AuthorizedClientRepository(authorizedClientService);
    }

    private ClientRegistration oktaClientRegistration() {
        return ClientRegistrations
                .fromIssuerLocation("https://company-dev.oktapreview.com")
                .clientId("xxxx")
                .clientSecret("yyyyy-cccc")
                .scope("openid", "email", "profile")
                .registrationId("okta")
                .clientName("myappname")
                .redirectUriTemplate("{baseUrl}/login/oauth2/code/okta").build();
    }

Upvotes: 1

Views: 725

Answers (1)

Rob Winch
Rob Winch

Reputation: 21720

You can create multiple WebSecurityConfigurerAdapter instances with an @Order annotation on them. Only the first HttpSecurity that matches will be used.

For your case you might have something that looks like this:

@EnableWebSecurity
public class MultiHttpSecurityConfig {
    // ...

    @Configuration
    @Order(1) // look at this configuration first                                                        
    public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
        protected void configure(HttpSecurity http) throws Exception {
            http
                // this ENTIRE HttpSecurity only applies to URLs starting with /app/rest/
                .antMatcher("/app/rest/**")                            
                .authorizeRequests(authorize -> authorize
                    .anyRequest().authenticated()
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                    .jwt(jwt -> jwt
                        .jwkSetUri("https://idp.example.com/.well-known/jwks.json")
                     )
               );
        }
    }

    @Configuration
    // no @Order so use the default of last                                                   
    public static class OidcSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                // no matchers on HttpSecurity so use default of all requests
                // note that each HttpSecurity is attempted and only the first is used
                // this means requests starting with /app/rest/ will not use this configuration
                .authorizeRequests(authorize -> authorize
                    .anyRequest().authenticated()
                )                    
                .oauth2Login(oauth2 -> oauth2
                    .redirectionEndpoint(redirection -> redirection
                            .baseUri("/login/oauth2/code/okta")
                    )
                );
        }
    }
}

Upvotes: 1

Related Questions