Spring OAuth2 - Can't get client token

10,128

I had the same problem. In my case is was because I mapped my DispatcherServlet like this :

<servlet-mapping>
  <servlet-name>dispatcher</servlet-name>
  <url-pattern>/dispatcher/*</url-pattern>
</servlet-mapping>

This was causing problem because security was configured on "/oauth/token", instead of "/dispatcher/oauth/token" :

<security:http pattern="/oauth/token" ...

So I just had to do :

<security:http pattern="/dispatcher/oauth/token" 

And the problem was gone.

If that is not the case for you, it may be because you have no DelegatingFilterProxy configured.

Try configuring it in web.xml :

<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

And if you use java config instead of web.xml, extending the class org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer will configure it.

Share:
10,128
nmb1106
Author by

nmb1106

Updated on June 04, 2022

Comments

  • nmb1106
    nmb1106 almost 2 years

    I'm not 100% sure I'm performing the correct request but I can't seem to get a token for a client. I've based my solution off of this tutorial 5 minutes with spring oauth 2.0.

    I'm performing this request from postman:

    /oauth/token?grant_type=client_credentials&client_id=mysupplycompany&client_secret=mycompanyk

    That request responds with an exception:

    org.springframework.security.authentication.InsufficientAuthenticationException: There is no client authentication. Try adding an appropriate authentication filter. org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(TokenEndpoint.java:91)

    Below are some files/classes you may need to help me figure out any issues.

    WebInitializer

    package com.squirrels.config;
    
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRegistration.Dynamic;
    
    import org.springframework.web.WebApplicationInitializer;
    import org.springframework.web.context.ContextLoaderListener;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.request.RequestContextListener;
    import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
    import org.springframework.web.filter.DelegatingFilterProxy;
    import org.springframework.web.servlet.DispatcherServlet;
    
    public class WebInitializer implements WebApplicationInitializer {
    
        public void onStartup(ServletContext servletContext) throws ServletException {          
            WebApplicationContext context = getContext();
            servletContext.addListener(new ContextLoaderListener(context));
            servletContext.addListener(new RequestContextListener());    
    
            Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(context));  
            servlet.addMapping("/");  
            servlet.setLoadOnStartup(1);  
    
            DelegatingFilterProxy filter = new DelegatingFilterProxy("springSecurityFilterChain");
            filter.setContextAttribute("org.springframework.web.servlet.FrameworkServlet.CONTEXT.dispatcher");
            servletContext.addFilter("springSecurityFilterChain", filter).addMappingForUrlPatterns(null, false, "/*");
        }  
    
        private AnnotationConfigWebApplicationContext getContext() {
            AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
            context.setConfigLocation("com.squirrels.config");
            return context;
        }
    
    }
    

    PersistenceJPAConfig

    package com.squirrels.config;
    
    import java.util.Properties;
    
    import javax.persistence.EntityManagerFactory;
    import javax.sql.DataSource;
    
    import liquibase.integration.spring.SpringLiquibase;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.ImportResource;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.core.env.Environment;
    import org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor;
    import org.springframework.jdbc.datasource.DriverManagerDataSource;
    import org.springframework.orm.jpa.JpaTransactionManager;
    import org.springframework.orm.jpa.JpaVendorAdapter;
    import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
    import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    @Configuration
    @EnableTransactionManagement
    @ComponentScan({ "com.squirrels.controller", "com.squirrels.services", "com.squirrels.persistence.dao", "com.squirrels.auth" })
    @PropertySource(value = { "classpath:squirrel.properties" })
    @ImportResource("classpath:spring-security.xml")
    public class PersistenceJPAConfig {
    
        @Autowired
        private Environment environment;
    
        @Bean
        public SpringLiquibase liquibase() {
            SpringLiquibase liquibase = new SpringLiquibase();
            liquibase.setDataSource(dataSource());
            liquibase.setDefaultSchema(environment.getRequiredProperty("db_schema"));
            liquibase.setChangeLog("classpath:/db/changelog/db.changelog-master.xml");
    
            return liquibase;
        }
    
        @Bean
        public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
            LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
            em.setDataSource(dataSource());
            em.setPersistenceUnitName("SquirrelAuth");
            em.setPackagesToScan(new String[] { "com.squirrels.persistence.model" });
    
            JpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
            em.setJpaVendorAdapter(vendorAdapter);
            em.setJpaProperties(additionalProperties());
    
            return em;
        }
    
        @Bean
        public DataSource dataSource() {
            DriverManagerDataSource dataSource = new DriverManagerDataSource();
            dataSource.setDriverClassName(environment.getRequiredProperty("db_driverClass"));
            dataSource.setUrl(environment.getRequiredProperty("db_jdbcUrl"));
            dataSource.setUsername(environment.getRequiredProperty("db_user"));
            dataSource.setPassword(environment.getRequiredProperty("db_password"));
            return dataSource;
        }
    
        @Bean
        public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
            JpaTransactionManager transactionManager = new JpaTransactionManager();
            transactionManager.setEntityManagerFactory(emf);
    
            return transactionManager;
        }
    
        @Bean
        public PersistenceExceptionTranslationPostProcessor exceptionTranslation() {
            return new PersistenceExceptionTranslationPostProcessor();
        }
    
        Properties additionalProperties() {
            Properties properties = new Properties();
            properties.setProperty("hibernate.dialect", environment.getRequiredProperty("db_hibernateDialect"));
            return properties;
        }
    
    
    }
    

    security-spring.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:oauth="http://www.springframework.org/schema/security/oauth2"
        xmlns:sec="http://www.springframework.org/schema/security" xmlns:mvc="http://www.springframework.org/schema/mvc"
        xsi:schemaLocation="http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schema/security/spring-security-oauth2-1.0.xsd
            http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.2.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd 
            http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
    
    
        <sec:http pattern="/oauth/token" create-session="stateless"
            authentication-manager-ref="authenticationManager">
            <sec:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
            <sec:anonymous enabled="false" />
            <sec:http-basic entry-point-ref="clientAuthenticationEntryPoint" />
            <sec:custom-filter ref="clientCredentialsTokenEndpointFilter"
                before="BASIC_AUTH_FILTER" />
            <sec:access-denied-handler ref="oauthAccessDeniedHandler" />
        </sec:http>
    
        <sec:http pattern="/api/**" create-session="never"
            entry-point-ref="oauthAuthenticationEntryPoint">
            <sec:anonymous enabled="false" />
            <sec:intercept-url pattern="/api/**" method="GET"
                access="IS_AUTHENTICATED_FULLY" />
            <sec:custom-filter ref="resourceServerFilter"
                before="PRE_AUTH_FILTER" />
            <sec:access-denied-handler ref="oauthAccessDeniedHandler" />
        </sec:http>
    
        <bean id="oauthAuthenticationEntryPoint"
            class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
        </bean>
    
        <bean id="clientAuthenticationEntryPoint"
            class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
            <property name="realmName" value="springsec/client" />
            <property name="typeName" value="Basic" />
        </bean>
    
        <bean id="oauthAccessDeniedHandler"
            class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler">
        </bean>
    
        <bean id="clientCredentialsTokenEndpointFilter"
            class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
            <property name="authenticationManager" ref="authenticationManager" />
        </bean>
    
        <sec:authentication-manager alias="authenticationManager">
            <sec:authentication-provider
                user-service-ref="clientDetailsUserService" />
        </sec:authentication-manager>
    
        <bean id="clientDetailsUserService"
            class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
            <constructor-arg ref="clientDetails" />
        </bean>
    
        <bean id="clientDetails" class="com.squirrels.auth.OAuthClientDetailsImpl">
            <property name="id" value="mysupplycompany" />
            <property name="secretKey" value="mycompanykey" />
        </bean>
    
        <sec:authentication-manager id="userAuthenticationManager">
            <sec:authentication-provider ref="customUserAuthenticationProvider" />
        </sec:authentication-manager>
    
        <bean id="customUserAuthenticationProvider" class="com.squirrels.auth.OAuthAuthenticationProvider">
        </bean>
    
        <oauth:authorization-server
            client-details-service-ref="clientDetails" token-services-ref="tokenServices">
            <oauth:authorization-code />
            <oauth:implicit />
            <oauth:refresh-token />
            <oauth:client-credentials />
            <oauth:password authentication-manager-ref="userAuthenticationManager" />
        </oauth:authorization-server>
    
        <oauth:resource-server id="resourceServerFilter"
            resource-id="springsec" token-services-ref="tokenServices" />
    
        <bean id="tokenStore"
            class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore" />
    
        <bean id="tokenServices"
            class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
            <property name="tokenStore" ref="tokenStore" />
            <property name="supportRefreshToken" value="true" />
            <property name="accessTokenValiditySeconds" value="120"></property>
            <property name="clientDetailsService" ref="clientDetails" />
        </bean>
    
        <mvc:annotation-driven />
    </beans>
    

    OAuthAuthenticationProvider

    package com.squirrels.auth;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.authentication.AuthenticationProvider;
    import org.springframework.security.authentication.BadCredentialsException;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.core.GrantedAuthority;
    
    public class OAuthAuthenticationProvider implements AuthenticationProvider {
    
        @Autowired
        private OAuthProxy proxy;
    
        @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
            boolean result = proxy.isValidUser("1", authentication.getPrincipal().toString(), authentication.getCredentials().toString());
    
            if (result) {
                List<GrantedAuthority> grantedAuthorities =
    
                new ArrayList<GrantedAuthority>();
                OAuthAuthenticationToken auth = new OAuthAuthenticationToken(authentication.getPrincipal(), authentication.getCredentials(), grantedAuthorities);
    
                return auth;
            } else {
                throw new BadCredentialsException("Bad User Credentials.");
            }
        }
    
        @Override
        public boolean supports(Class<?> arg0) {
            return true;
        }
    
    }
    

    WebMvcConfig

    package com.squirrels.config;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.accept.ContentNegotiationManager;
    import org.springframework.web.servlet.ViewResolver;
    import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    import org.springframework.web.servlet.view.ContentNegotiatingViewResolver;
    
    @Configuration
    @EnableWebMvc
    public class WebMvcConfig extends WebMvcConfigurerAdapter {
    
    
        @Override
        public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
            configurer.enable();
        };
    
        /*
         * Configure ContentNegotiatingViewResolver
         */
        @Bean
        public ViewResolver contentNegotiatingViewResolver(ContentNegotiationManager manager) {
            ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver();
            resolver.setContentNegotiationManager(manager);
    
            // Define all possible view resolvers
            List<ViewResolver> resolvers = new ArrayList<ViewResolver>();
            resolvers.add(jsonViewResolver());         
            resolver.setViewResolvers(resolvers);
            return resolver;
        }
    
        /*
         * Configure View resolver to provide JSON output using JACKSON library to
         * convert object in JSON format.
         */
        @Bean
        public ViewResolver jsonViewResolver() {
            return new JsonViewResolver();
        }
    
    }
    

    OAuthenticationToken

    package com.squirrels.auth;
    
    import java.util.Collection;
    
    import org.springframework.security.authentication.AbstractAuthenticationToken;
    import org.springframework.security.core.GrantedAuthority;
    
    public class OAuthAuthenticationToken extends AbstractAuthenticationToken {
    
        private static final long serialVersionUID = -1092219614309982278L;
        private final Object principal;
        private Object credentials;
    
        public OAuthAuthenticationToken(Object principal, Object credentials,
                Collection<? extends GrantedAuthority> authorities) {
            super(authorities);
            this.principal = principal;
            this.credentials = credentials;
            super.setAuthenticated(true);
        }
    
        @Override
        public Object getCredentials() {
            return credentials;
        }
    
        @Override
        public Object getPrincipal() {
            return principal;
        }
    
    }
    

    OAuthClientDetailsImpl

    package com.squirrels.auth;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import org.springframework.security.oauth2.provider.ClientDetails;
    import org.springframework.security.oauth2.provider.ClientDetailsService;
    import org.springframework.security.oauth2.provider.ClientRegistrationException;
    import org.springframework.security.oauth2.provider.NoSuchClientException;
    import org.springframework.security.oauth2.provider.client.BaseClientDetails;
    
    public class OAuthClientDetailsImpl implements ClientDetailsService {
    
        private String id;
        private String secretKey;
    
        @Override
        public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
            if (clientId.equals(id))
            {
                List<String> authorizedGrantTypes = new ArrayList<String>();
                authorizedGrantTypes.add("password");
                authorizedGrantTypes.add("refresh_token");
                authorizedGrantTypes.add("client_credentials");
    
                BaseClientDetails clientDetails = new BaseClientDetails();
                clientDetails.setClientId(id);
                clientDetails.setClientSecret(secretKey);
                clientDetails.setAuthorizedGrantTypes(authorizedGrantTypes);
    
                return clientDetails;
            }
            else {
                throw new NoSuchClientException("No client recognized with id: " + clientId);
            }
        }
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        public String getSecretKey() {
            return secretKey;
        }
    
        public void setSecretKey(String secretKey) {
            this.secretKey = secretKey;
        }
    
    }
    

    OAuthProxy

    package com.squirrels.auth;
    
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import com.squirrels.dto.UserDTO;
    import com.squirrels.persistence.dao.UserDao;
    import com.squirrels.persistence.model.UserModel;
    import com.squirrels.services.UserUtil;
    
    
    @Service
    public class OAuthProxy {
    
        protected static final Logger logger = LogManager.getLogger(OAuthProxy.class);
    
        @Autowired
        private UserDao userDaoImpl;
    
        @Autowired
        private UserUtil userUtil;  
    
        @Transactional
        public boolean isValidUser(String organizationId, String username, String password) {
    
            try{
                UserModel user = userDaoImpl.findByOrganizationAndUserName(organizationId, username);
                UserDTO userDto = userUtil.getDTO(user);
    
                //TODO validate password (or other means of auth)
    
                return true;            
            }catch(Exception e){
                logger.error("No user: " + username + " found in organization: " + organizationId, e);
            }
    
            return false;
        }
    
    }
    
  • nmb1106
    nmb1106 about 9 years
    Going to try this now it makes sense actually. I have my Dispatcher mapped to /api/*. I am also going to edit my question and include my JavaConfig file classes. All of my spring stuff is configured via java except the spring security stuff.
  • nmb1106
    nmb1106 about 9 years
    Thank you! This answer worked as well as just switching my dispatcher from /api/* to /*. I'm finally running a completely operable oauth2 provider
  • baraber
    baraber about 9 years
    Glad to help :) In case you would like to keep "/api/*", maybe this thread could help you. It says that if you override default oauth endpoint urls, you might have to configure another DelegatingFilterProxy.