Spring Security OAuth 2.0 - client secret always required for authorization code grant

匆匆过客 提交于 2019-11-27 15:34:32
quintonm

Authenticating the client using the form parameters instead of basic auth is enabled using the allowFormAuthenticationForClients() method as shown in the code sample below.

class AuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {

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

The allowFormAuthenticationForClients() method triggers the addition of the ClientCredentialsTokenEndpointFilter which allows for authentication via form parameters.

Spring allows you to define OAuth2 clients with an empty secret. These can be considered "public" clients, or clients that are unable to keep a secret. Think of Javascript apps, mobile apps, ..... You typically don't want to have client secrets stored there.

As you point out, according to the OAuth2 spec, a token endpoint can opt to not require a secret for these public clients.

So in Spring, simply define an OAuth2 client with an empty secret, and configure it for a limited set of grant types (authorization_code and refresh_token)

The Spring Security implementation token url will accept a token exchange without a client secret for that particular OAuth2 client.

Initially I had a similar setup to the accepted answer, which is definitely a prerequisite to make this work. But what is missing is that you cannot simply set the password to null. You must set it to an empty password, for example like this:

String secret = PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("");
clientDetails.setClientSecret(secret);

If you don't do this, you will still get a 401!

In order to solve the issue see the method loadUserByUsername of class: org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService:

if (clientSecret == null || clientSecret.trim().length() == 0) {
   clientSecret = this.emptyPassword;
}

Probably in your case emptyPassword has not been initialized with empty encoded password by your password encoder. Set missing password encoder in AuthorizationServerConfigurerAdapter:

@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
    oauthServer.passwordEncoder(passwordEncoder);
}

like this:

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.inMemory()
            .withClient("client").secret(PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("")) //empty
            .authorizedGrantTypes("authorization_code", "refresh_token")
            .redirectUris("http://www.dev.com")
            .scopes("all")
            .autoApprove(true);
} @Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
        throws Exception {
    oauthServer.tokenKeyAccess("permitAll()")
            .checkTokenAccess("isAuthenticated()")
            .allowFormAuthenticationForClients();
}

It is ok.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!