Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java Spring Security OAuth2: Accept client credentials via POST

I have a fairly basic Spring Boot setup and I have installed Spring Security and I have managed to successfully set up OAuth2 to protect my API.

I had some trouble a few days back and asked (and answered) a question with regards to hitting my /oauth/token end point. I soon figured out that the problem was that I was trying to send my client credentials in the body of my POST request but the token end point is configured in Spring Security to accept the client credentials (client_id and secret) via HTTP Basic Auth instead.

Most of my experience with consuming OAuth2 APIs has involved sending client credentials in the body of the POST request and I was wondering if it was possible to configure Spring Security to function in the same way?

I've tried a few different things with no success, like setting the following configuration option, but I feel like that might only be used when configuring an OAuth2 client:

security.oauth2.client.clientAuthenticationScheme=form

This is my Authorization server configuration.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.token.TokenStore;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private UserApprovalHandler userApprovalHandler;

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client_id")
                .secret("secret")
                .authorizedGrantTypes("password", "authorization_code", "refresh_token")
                .scopes("read", "write")
                .accessTokenValiditySeconds(600)
                .refreshTokenValiditySeconds(3600);
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(this.tokenStore)
                .userApprovalHandler(this.userApprovalHandler)
                .authenticationManager(this.authenticationManager);
    }

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                .passwordEncoder(this.passwordEncoder);
    }
}
like image 550
Jonathon Avatar asked Sep 19 '18 20:09

Jonathon


1 Answers

As pointed out by @chrylis in the comments, the trick is to use the allowFormAuthenticationForClients method on AuthorizationServerSecurityConfigurer when configuring the authorization server. In my case, I have this in my AuthorizationServerConfig class:

@Override
public void configure(AuthorizationServerSecurityConfigurer security) {
    security.tokenKeyAccess("permitAll()")
            .checkTokenAccess("isAuthenticated()")
            .passwordEncoder(this.passwordEncoder)
            .allowFormAuthenticationForClients();
}

That will allow passing client credentials via standard parameters, such as in the body of the POST request (or in the query string), though Spring prefers using HTTP Basic Auth by joining the client_id and secret together with a colon (<client_id>:<secret>), base-64 encoding the result, prefixing it with Basic and passing that to the Authorization header, so you would end up with something like this:

Authorization: Basic QWxhZGRpbjpPcGVuU2VzYW1l
like image 184
Jonathon Avatar answered Nov 07 '22 11:11

Jonathon