'"Full authentication is required to access this resource" on spring oauth2 authorization server on /oauth/token request

I'm trying to create a page that will hold some user data that may be accessed through some client webpage. That brought me to oauth2 (authorization code) and Spring. Currently I'm working on some kind of proof of concept for this basing my code on https://github.com/dynamind/spring-boot-security-oauth2-minimal/ and this workflow https://techannotation.files.wordpress.com/2015/06/oauth2-0-authorization-code.png

I already have a resource server that uses login page for authentication of users and then gives them a possibility of some kind of approval for sharing their data. Then it redirects user to a page set up for client.

@SpringBootApplication
public class AuthorizationServerApplication extends SpringBootServletInitializer {

    private static final Logger log = LoggerFactory.getLogger(AuthorizationServerApplication.class);

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(AuthorizationServerApplication.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(AuthorizationServerApplication.class);
    }

    @Configuration
    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    protected static class SecurityConfig extends WebSecurityConfigurerAdapter {

        @Override
        @Autowired // <-- This is crucial otherwise Spring Boot creates its own
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            log.info("Defining inMemoryAuthentication (2 users)");
            auth.inMemoryAuthentication()

                    .withUser("user").password("password").roles("USER")

                    .and()

                    .withUser("admin").password("password").roles("USER", "ADMIN");
        }

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.formLogin()

                    .and()

                    .httpBasic().disable().anonymous().disable().authorizeRequests().anyRequest().authenticated();
        }
    }

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

        @Value("${config.oauth2.privateKey}")
        private String privateKey;

        @Value("${config.oauth2.publicKey}")
        private String publicKey;

        @Autowired
        private AuthenticationManager authenticationManager;

        @Bean
        public JwtAccessTokenConverter tokenEnhancer() {
            log.info("Initializing JWT with public key:\n" + publicKey);
            JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
            converter.setSigningKey(privateKey);
            converter.setVerifierKey(publicKey);
            return converter;
        }

        @Bean
        public JwtTokenStore tokenStore() {
            return new JwtTokenStore(tokenEnhancer());
        }

        /**
         * Defines the security constraints on the token endpoints
         * /oauth/token_key and /oauth/check_token Client credentials are
         * required to access the endpoints
         *
         * @param oauthServer
         * @throws Exception
         */
        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
            oauthServer.tokenKeyAccess("isAnonymous() || hasRole('ROLE_TRUSTED_CLIENT')") // permitAll()
                    .checkTokenAccess("hasRole('TRUSTED_CLIENT')"); // isAuthenticated()
        }

        /**
         * Defines the authorization and token endpoints and the token services
         *
         * @param endpoints
         * @throws Exception
         */
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints

                    // Which authenticationManager should be used for the
                    // password grant
                    // If not provided, ResourceOwnerPasswordTokenGranter is not
                    // configured
                    .authenticationManager(authenticationManager)

                    // Use JwtTokenStore and our jwtAccessTokenConverter
                    .tokenStore(tokenStore()).accessTokenConverter(tokenEnhancer());
        }

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            clients.inMemory()

                    // Public client where client secret is vulnerable (e.g.
                    // mobile apps, browsers)
                    .withClient("clientname") // No secret!
                    .authorizedGrantTypes("authorization_code").scopes("read")
                    .redirectUris("http://localhost:8080/client")

            ;
        }

    }

}

Currently I'm dealing with a simplest possible client webpage. I created a page with link to authorization server (localhost:8081/oauth/authorize...). User clicks on it and is redirected to authorization server, logs in there, approves sharing his/hers data and is then redirected back to client site (localhost:8080/client but with code given by authorization server), but now with additional option to click one more link (localhost:8081/oauth/token...) that has returned code in it. All this works, but when user clicks this second link and is redirected to authorization server, this auth server responds with "Full authentication is required to access this resource".

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>client page</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
    <p th:text="'oooo text oooo'" />
    if you want it
    <a
        th:href="@{http://localhost:8081/oauth/authorize(response_type='code',client_id='client',key='value',scope='read',redirect_uri='http://localhost:8080/client')}">clickit</a>

    <a th:if="${param.code != null}"
        th:href="@{http://localhost:8081/oauth/token(grant_type='authorization_code',code=${param.code[0]},redirect_uri='http://localhost:8080/client')}">
        approve
    </a>
    <div th:if=" ${param.code !=null}
            "
        th:text="${'requestParam: ' + param.code[0]}"></div>

</body>
</html>

Do you have any ideas for this problem?



Solution 1:[1]

Turns out it was because I didn't use POST request. Using this solved the problem:

<form th:if="${param.code != null}"
  th:method="post"
  th:action="@{http://localhost:8081/oauth/token}">
  <input type="text" id="grant_type" name="grant_type" th:value="authorization_code"/>
  <input type="text" id="client_id" name="client_id" th:value="client"/>
  <input type="text" id="code" name="code" th:value="${param.code[0]}"/>
  <input type="text" id="redirect_uri" name="redirect_uri" th:value="@{http://localhost:8080/client}"/>
      <input type="submit" />
</form>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 szeldon