Spring Security Oauth2 Resource Owner Password flow: When I send a REST request, my user detail service always gets the client id instead of username

StackOverflow https://stackoverflow.com/questions/23158084

Question

So I'm trying to send a request to my oauth server (with no headers): grant_type=password&username=blah&password=blah&client_id=blahblah.

I have 2 authentication-manager (one for the client and another for the user validation). The problem is, none of my authentication managers are being passing the username to verify. In both cases, they are being called with the client_id. What I want is one to verify the client and the other to verify the user.

My bean configuration is this:

 <http pattern="/oauth/token" create-session="stateless"  authentication-manager-ref="authenticationManager" xmlns="http://www.springframework.org/schema/security">
    <intercept-url pattern="/oauth/token" access="ROLE_USER" />
    <anonymous enabled="false" />
    <http-basic entry-point-ref="clientAuthenticationEntryPoint" />

    <custom-filter ref="clientCredentialsTokenEndpointFilter" after="BASIC_AUTH_FILTER" />  

    <access-denied-handler ref="oauthAccessDeniedHandler" />
</http>


<!-- The OAuth2 protected resources are separated out into their own block so we can deal with authorization and error handling 
    separately. This isn't mandatory, but it makes it easier to control the behaviour. -->
<http pattern="/api/**" create-session="stateless" entry-point-ref="oauthAuthenticationEntryPoint"
    access-decision-manager-ref="accessDecisionManager" xmlns="http://www.springframework.org/schema/security">
    <anonymous enabled="false" />
    <intercept-url pattern="/api" access="ROLE_USER,SCOPE_READ" />

    <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
    <access-denied-handler ref="oauthAccessDeniedHandler" />
</http>


<authentication-manager  alias="authenticationManager"  xmlns="http://www.springframework.org/schema/security">
    <sec:authentication-provider user-service-ref="clientDetailsUserService" />
    <sec:authentication-provider user-service-ref="customUserDetailService">
        <sec:password-encoder ref="passwordEncoder" />
    </sec:authentication-provider>
</authentication-manager>


<beans:bean id="customUserDetailService" class="com.cointraders.api.securities.UserDetailsServiceImpl" />


<beans:bean id="clientDetails" class="org.springframework.security.oauth2.provider.JdbcClientDetailsService">
    <beans:constructor-arg ref="dataSource" />
</beans:bean>

<beans:bean id="accessDecisionManager" class="org.springframework.security.access.vote.UnanimousBased" xmlns="http://www.springframework.org/schema/beans">
    <beans:constructor-arg>
        <beans:list>
            <beans:bean class="org.springframework.security.oauth2.provider.vote.ScopeVoter" />
            <beans:bean class="org.springframework.security.access.vote.RoleVoter" />
            <beans:bean class="org.springframework.security.access.vote.AuthenticatedVoter" />
        </beans:list>
    </beans:constructor-arg>
</beans:bean>

<oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices">
    <oauth:refresh-token />
    <oauth:password authentication-manager-ref="authenticationManager"  />
</oauth:authorization-server>


<oauth:resource-server id="resourceServerFilter" resource-id="api" token-services-ref="tokenServices" />

<sec:global-method-security pre-post-annotations="enabled" proxy-target-class="true">
    <sec:expression-handler ref="oauthExpressionHandler" />
</sec:global-method-security>

<oauth:expression-handler id="oauthExpressionHandler" />

<oauth:web-expression-handler id="oauthWebExpressionHandler" />

<beans:bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    <beans:property name="realmName" value="api/" />
</beans:bean>



<beans:bean id="clientCredentialsTokenEndpointFilter" class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter">
    <beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>


<beans:bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.JdbcTokenStore">
    <beans:constructor-arg ref="dataSource" />
</beans:bean>


<beans:bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
    <beans:property name="tokenStore" ref="tokenStore" />
    <beans:property name="supportRefreshToken" value="true" />
    <beans:property name="clientDetailsService" ref="clientDetails" />
</beans:bean>

<beans:bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
    <beans:constructor-arg ref="clientDetails" />
</beans:bean>

<beans:bean id="oauthAccessDeniedHandler" class="org.springframework.security.oauth2.provider.error.OAuth2AccessDeniedHandler" />

<beans:bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    <beans:property name="realmName" value="api" />
</beans:bean>

This is my Web.XML

    <!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/root-context.xml</param-value>
</context-param>
  <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    <init-param>
        <param-name>contextAttribute</param-name>
        <param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.spring</param-value>
    </init-param>
</filter>

<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<servlet>
    <servlet-name>spring</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/appServlet/*.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>spring</servlet-name>
    <url-pattern>/</url-pattern>
 </servlet-mapping>

My User Detail Service Class

 public class UserDetailsServiceImpl extends PersonServiceImpl implements
    UserDetailsService {

@Override
public UserDetails loadUserByUsername(String username)
        throws UsernameNotFoundException {

    UserDetails details = null;
    Specification<Person> specs = Specifications.where(PersonSpecification.equalsUserName(username));
    Person person = this.personRepository.findOne(specs);
    if(person!=null)
    {   
        List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
        authorities.add(new   SimpleGrantedAuthority(person.getRole().getRole().name()));
        details = new User(person.getUserName(), person.getPassword(), person.getActive(), false, false, false, authorities);
    }
    return details;
 }

 }
Was it helpful?

Solution

In your <authorization-server/> you haven't set the AuthenticationManager in the <password/> grant type (so it is using the default bean name "authenticationManager" which you have mapped to the client credentials).

An additional problem is that you use <authentication-manager/> twice without giving an explicit "id=" for either, which actually has the effect of defining only one AuthenticationManager since the second one overrides the first (they both have the same id). Try using "id=" instead of "alias=".

You really need to send the client credentials in a header if you can (but you probably know that).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top