0

I have a simple web application which uses the default Spring Security form authentication and, once authenticated, the user is allowed to browse between Thymeleaf views and access content.

I'm able to provide JSON for REST client applications instead of web views, for the same endpoint, simply by using Spring mapping like this:

// response for web application, thymeleaf views
@RequestMapping("/fruits", produces = MediaType.TEXT_HTML_VALUE)
public String index(Model model) {
    model.addAttribute("fruits", fruits);
    return "fruitsView";
}

// response for REST client applications
@RequestMapping("/fruits", produces = MediaType.APPLICATION_JSON_VALUE)
public Fruits[] index() {
    return fruits;
}

The problem is: is it possible to accept Basic Authentication instead of responding with a login web form when the request accepts JSON (Accept header field) instead of first accepting HTML?

My security configuration:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // works well for web views: 
        http.authorizeRequests().antMatchers("/**").hasRole("USER").and().formLogin();

        // works well for REST clients:
        // http.authorizeRequests().antMatchers("/**").hasRole("USER").and().httpBasic();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().withUser("user").password("password").roles("USER");
    }

}

Is it possible to configure both httpBasic and formLogin authentication in the way each one responds for a specif Accept content type header field?

I've learned that it's possible to have two different authentications for different URL patterns: Spring REST security - Secure different URLs differently. But how about two different authentication mechanisms for the same URL where the requests are differentiated by Accept content header field?

Community
  • 1
  • 1
dumb
  • 11
  • 1
  • @dur Tried `.formLogin().and().httpBasic()`. While accessing through browser I get the login form, which is expected. But while accessing through other client passing *application/json* as *Accept* header field I'm allowed to access the content if not authenticated. – dumb Mar 13 '17 at 21:23
  • @dur Although you've removed your comment, you were right: the client tool is a browser plugin and so it sends browser cookies. When no cookies are sent, `.formLogin().and().httpBasic()` works pretty well to use form login authentication while accessing endpoint through browser and http basic authentication while consuming the endpoint like a REST API. Problem solved! Thanks! – dumb Mar 17 '17 at 16:09

1 Answers1

0

This may not be good practice to go with in longer run in terms of scalability and maintainability of the code.

But, this might help you to an extent. But this points to the same resource with both types of logins and serves the same response. In your case, you want to serve different response for same resource. That is the reason it is not a good practice. Combining basic authentication and form login for the same REST Api

An alternative suggestion is to adding an optional attribute to your request and when you reach a request on server side based on the optional attribute value, invoke the appropriate method and set the response content type as needed.

Community
  • 1
  • 1
upendra
  • 111
  • 5
  • Thanks for the time you took to post this. But I want to serve **same resource** in two different formats, that's why I want to do this through the same endpoint, and that is the purpose of *Accept* header field according to HTTP RFC (see "content type negotiation"). That is also the Ruby on Rails way and this is explicitly supported by Spring through *RequestMapping* annotation. And I can't realize why enabling two different forms of authentication to the same endpoint would be a bad practice. – dumb Mar 13 '17 at 21:43