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

前端 未结 6 1175
陌清茗
陌清茗 2020-12-03 11:54

According to the spec, requests for a token using the authorization code grant are not required to be authenticated as long as the client_id is included in the

相关标签:
6条回答
  • 2020-12-03 12:16

    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.

    0 讨论(0)
  • 2020-12-03 12:18

    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.

    0 讨论(0)
  • 2020-12-03 12:19

    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);
    }
    
    0 讨论(0)
  • 2020-12-03 12:20

    This worked for me

        @Override
        public void configure(AuthorizationServerSecurityConfigurer cfg) throws Exception {
            cfg
                .tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients()
                .passwordEncoder(clientPasswordEncoder());
        }
    
    
        @Bean("clientPasswordEncoder")
        PasswordEncoder clientPasswordEncoder() {
            return new BCryptPasswordEncoder(4);
        }
    

    Test 1:

    Test 2:

    0 讨论(0)
  • 2020-12-03 12:33

    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.

    0 讨论(0)
  • 2020-12-03 12:36

    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!

    0 讨论(0)
提交回复
热议问题