Avoid basic auth when using x.509 authentication

Viewed 255

I have created an REST API based on Spring WebFlux that is protected through X.509 authentication. I followed this guide https://www.baeldung.com/x-509-authentication-in-spring-security to create all the certificates.

The router implementation:

@Configuration
class LogRouter {

    @Bean
    fun functionalRoutes(handler: LogHandler): RouterFunction<ServerResponse> =
        route()
            .route(RequestPredicates.path("/")) {
                ServerResponse.ok().body(Mono.just("I am alive"))
            }
            .nest(RequestPredicates.path("/api").and(RequestPredicates.accept(MediaType.APPLICATION_JSON))) { builder ->
                builder.GET("/fn/mono", handler::monoMessage)
                    .POST("/fn/mono", handler::monoPostMessage)
            }
            .build()
}

and app implementation:

@SpringBootApplication
@EnableWebFluxSecurity
class RestplayApplication {

    @Bean
    fun securityWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain? {
        val principalExtractor = SubjectDnX509PrincipalExtractor()

        principalExtractor.setSubjectDnRegex("OU=(.*?)(?:,|$)")

        val authenticationManager = ReactiveAuthenticationManager { authentication: Authentication ->
            authentication.isAuthenticated = "Trusted Org Unit" == authentication.name
            Mono.just(authentication)
        }

        http
            .x509 { x509 ->
                x509
                    .principalExtractor(principalExtractor)
                    .authenticationManager(authenticationManager)
            }
            .authorizeExchange { exchanges ->
                exchanges
                    .anyExchange().authenticated()
            }
        return http.build()

    }
}

fun main(args: Array<String>) {
    runApplication<RestplayApplication>( *args)
}

I use Firefox browser to test the x.509 authentication and I have added the self signed certificate(rootCA.crt) to the Firefox:

enter image description here

included client certificate(clientBob.p12).

enter image description here

When calling the link in the browser it shows basic authentication form:

enter image description here

However, I expect the authentication form not to be appeared because I have provided a valid client certificate in the browser.

Why the basic form appears every time?

The code is hosted on https://github.com/softshipper/restplay. The password for certificates are always changeit.

1 Answers

I debugged it and the problem seems to be that your client certificate clientBob.crt does not contain the field for Organization Unit of the subject and your principalExtractor is set to extract this field. As a result, your principalExtractor fails, and so it calls authenticationFailureHandler, which is set to prompt for basic authentication by default.

Possible solutions could be:

  1. Use a client certificate that includes the Organization Unit of the subject, and set it to "Trusted Org Unit".

  2. Alter the principalExtractor regex so that it uses a different field. The default one uses the common name (CN). If you do edit this, then remember to also update your authenticationManager to check for "Bob" instead of "Trusted Org Unit"

Related