Stefan Falk
Stefan Falk

Reputation: 25387

oauth/token Full authentication is required to access this resource

I want to login as a user but for some reason it seems that the endpoint oauth/token is protected:

Request URL:http://192.168.0.14:8080/oauth/token
Request Method:POST
Status Code:401 
Remote Address:192.168.0.14:8080
Referrer Policy:no-referrer-when-downgrade
Access-Control-Allow-Headers:x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN
Access-Control-Allow-Methods:PATCH,POST,GET,OPTIONS,DELETE
Access-Control-Allow-Origin:*
Access-Control-Max-Age:3600
Cache-Control:no-store
Cache-Control:no-cache, no-store, max-age=0, must-revalidate
Content-Type:application/json;charset=UTF-8
Date:Tue, 06 Mar 2018 18:59:25 GMT
Expires:0
Pragma:no-cache
Pragma:no-cache
Transfer-Encoding:chunked
WWW-Authenticate:Bearer realm="testjwtresourceid", error="unauthorized", error_description="Full authentication is required to access this resource"
WWW-Authenticate:Basic realm="oauth2/client"
X-Content-Type-Options:nosniff
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block

Interestingly enough... I get this message:

2018-03-06 19:59:25.766  WARN 31030 --- [nio-8080-exec-2] o.s.s.c.bcrypt.BCryptPasswordEncoder     : Encoded password does not look like BCrypt

It seems that for some reason the whole thing runs through the BasicAuthenticationFilter. It's clear that this does not look like BCrypt .. though I am setting BCrypt a s my password encoder:

@Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    auth.authenticationProvider(daoAuthenticationProvider());
}

/**
 * Using {@link BCryptPasswordEncoder} for user-password encryption.
 * @return
 */
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

It looks like WebSecurity tries to encode the client secret with BCrypt O_o

security.jwt.client-id=CLIENT_ID
security.jwt.client-secret=CLIENT_SECRET

The entire web config

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${security.signing-key}")
    private String signingKey;

    @Value("${security.encoding-strength}")
    private Integer encodingStrength;

    @Value("${security.security-realm}")
    private String securityRealm;

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    /**
     * Nothing to configure yet.
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception { }

    /**
     * Define routes for {@link WebSecurity}.
     *
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {

        final String[] SWAGGER_UI = {
                "/swagger-resources/**",
                "/swagger-ui.html",
                "/v2/api-docs",
                "/webjars/**"
        };

        web.ignoring().antMatchers("/pub/**", "/users")
                .antMatchers(SWAGGER_UI);
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(signingKey);
        return converter;
    }

    /**
     * Using {@link JwtTokenStore} for JWT access tokens.
     * @return
     */
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    /**
     * Provide {@link DefaultTokenServices} using the {@link JwtTokenStore}.
     * @return
     */
    @Bean
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }

    /**
     * We provide the AuthenticationManagerBuilder using our {@link UserDetailsService} and the {@link BCryptPasswordEncoder}.
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()).and()
            .authenticationProvider(daoAuthenticationProvider());
    }

    /**
     * Using {@link BCryptPasswordEncoder} for user-password encryption.
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * Provide {@link DaoAuthenticationProvider} for password encoding and set the {@link UserDetailsService}.
     * @return
     */
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        daoAuthenticationProvider.setUserDetailsService(this.userDetailsService);
        return daoAuthenticationProvider;
    }

}

The OAuth config

@Configuration
@EnableAuthorizationServer
@EnableResourceServer
public class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {

    @Configuration
    @EnableResourceServer
    public class ResourceServer extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                    .requestMatchers().antMatchers("/**")
                    .and()
                    .authorizeRequests().anyRequest().access("#oauth2.hasScope('write')");
        }

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.resourceId(resourceIds);
        }

    }

    @Value("${security.jwt.client-id}")
    private String clientId;

    @Value("${security.jwt.client-secret}")
    private String clientSecret;

    @Value("${security.jwt.grant-type}")
    private String grantType;

    @Value("${security.jwt.scope-read}")
    private String scopeRead;

    @Value("${security.jwt.scope-write}")
    private String scopeWrite;

    @Value("${security.jwt.resource-ids}")
    private String resourceIds;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
        configurer
                .inMemory()
                .withClient(clientId)
                .secret(clientSecret)
                .authorizedGrantTypes(grantType)
                .scopes(scopeRead, scopeWrite)
                .resourceIds(resourceIds);
    }


    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        enhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
        endpoints.tokenStore(tokenStore)
                .accessTokenConverter(accessTokenConverter)
                .tokenEnhancer(enhancerChain)
                .authenticationManager(authenticationManager);
    }

}

Upvotes: 4

Views: 5590

Answers (1)

divinedragon
divinedragon

Reputation: 5346

Because you have the following Bean created, all security configurations will use this PasswordEncoder. In your case, WebSecurity and AuthorizationServerSecurity.

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

I am pretty sure your security.jwt.client-secret is a plain text.

So, what is happening is that when you make the request, your AuthorizationSecurityConfig tried to read security.jwt.client-secret as a BCrypt encoded string, which eventually it is not.

Hence, the whole Authentication process fails and the warning message is logged.

To resolve this, you have two options:

First:

Configure the AuthorizationSecurity to use NoOpPasswordEncoder. Here is how you can override this. Mind you NoOpPasswordEncoder is already deprecated.

@Override
public void configure(final AuthorizationServerSecurityConfigurer security) throws Exception {
    security.passwordEncoder(NoOpPasswordEncoder.getInstance());
}

Second (Preferred):

Generate the BCrypt value of your security.jwt.client-secret and set it in your application.properties/yaml.

Thanks to David who cleared the doubt on a similar answer here.

Guess what, I was facing the same issue when I was looking at this question for solution. :-)

Upvotes: 3

Related Questions