0
votes

I am trying to implement Oauth2 spring security with Spring 4.1 I am sending password, username , client_id and client_secret but still i get bad credentials. the request i send is as follows:

http://localhost:8080/asset-manager/oauth/token?grant_type=password&client_id=restapp&client_secret=restapp&username=ankita&password=ankita

I have the following files

spring-security.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:context="http://www.springframework.org/schema/context"
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.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.1.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd ">

<!-- This is default url to get a token from OAuth -->
<http pattern="/oauth/token" create-session="stateless"
    authentication-manager-ref="clientAuthenticationManager"
    xmlns="http://www.springframework.org/schema/security">
    <intercept-url pattern="/oauth/token" access="isFullyAuthenticated()" />
    <anonymous enabled="false" />
    <http-basic entry-point-ref="clientAuthenticationEntryPoint" />
    <!-- include this only if you need to authenticate clients via request 
        parameters -->
    <custom-filter ref="clientCredentialsTokenEndpointFilter"
        after="BASIC_AUTH_FILTER" />
    <access-denied-handler ref="oauthAccessDeniedHandler" />
    <csrf disabled="true" />
</http>

<!-- This is where we tells spring security what URL should be protected 
    and what roles have access to them -->
<http pattern="/api/**" create-session="never"
    entry-point-ref="oauthAuthenticationEntryPoint"
    access-decision-manager-ref="accessDecisionManager"
    xmlns="http://www.springframework.org/schema/security">
    <anonymous enabled="false" />
    <intercept-url pattern="/api/**" access="hasRole('ROLE_USER')" />
    <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
    <access-denied-handler ref="oauthAccessDeniedHandler" />
    <csrf disabled="true" />
</http>


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

<bean id="requestFactory"
    class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
    <constructor-arg name="clientDetailsService" ref="clientDetails" />
</bean>

<bean id="clientAuthenticationEntryPoint"
    class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    <property name="realmName" value="test/client" />
    <property name="typeName" value="Basic" />
</bean>

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

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

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


<authentication-manager id="clientAuthenticationManager"
    xmlns="http://www.springframework.org/schema/security">
    <authentication-provider user-service-ref="clientDetailsUserService" />
</authentication-manager>

<bean id="authenticationProcessingFilter"
    class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
    <property name="authenticationManager" ref="authenticationManager" />
</bean>
<!-- This is simple authentication manager, with a hardcoded user/password 
    combination. We can replace this with a user defined service to get few users 
    credentials from DB -->
<authentication-manager alias="authenticationManager"
    xmlns="http://www.springframework.org/schema/security">
    <authentication-provider>
        <user-service>
            <user name="ankita" password="ankita" authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
    <authentication-provider user-service-ref="clientDetailsUserService" />

</authentication-manager>

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


<!-- This defined token store, we have used inmemory tokenstore for now 
    but this can be changed to a user defined one -->
<bean id="tokenStore"
    class="org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore" />

<!-- This is where we defined token based configurations, token validity 
    and other things -->
<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 name="clientDetailsService" ref="clientDetails" />
</bean>

<bean id="userApprovalHandler"
    class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler">
    <property name="tokenStore" ref="tokenStore" />
    <property name="clientDetailsService" ref="clientDetails" />
    <property name="requestFactory" ref="requestFactory" />
</bean>

<oauth:authorization-server
    client-details-service-ref="clientDetails" token-services-ref="tokenServices"
    user-approval-handler-ref="userApprovalHandler">
    <oauth:authorization-code />
    <oauth:implicit />
    <oauth:refresh-token />
    <oauth:client-credentials />
    <oauth:password />
</oauth:authorization-server>

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

<oauth:client-details-service id="clientDetails">
    <!-- client -->
    <oauth:client client-id="restapp"
        authorized-grant-types="authorization_code,client_credentials"
        authorities="ROLE_USER" scope="read,write,trust" secret="secret" />

    <oauth:client client-id="restapp"
        authorized-grant-types="password,authorization_code,refresh_token,implicit"
        secret="restapp" scope="read,write,trust" authorities="ROLE_USER" />

</oauth:client-details-service>
<bean id="httpSessionSecurityContextRepository"
    class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
    <property name='allowSessionCreation' value='false' />
</bean>
<bean id="securityContextPersistenceFilter"
    class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
    <constructor-arg ref="httpSessionSecurityContextRepository" />
</bean>
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
    <constructor-arg>
        <list>
            <sec:filter-chain pattern="/**"
                filters="securityContextPersistenceFilter" />
        </list>
    </constructor-arg>
</bean>
<sec:global-method-security
    pre-post-annotations="enabled" proxy-target-class="true"
    authentication-manager-ref="authenticationManager"
    secured-annotations="enabled">
    <!--you could also wire in the expression handler up at the layer of the 
        http filters. See https://jira.springsource.org/browse/SEC-1452 -->
    <sec:expression-handler ref="oauthExpressionHandler" />
</sec:global-method-security>

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

And this is my pom.xml

<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
      http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">



<servlet>
    <servlet-name>mvc-dispatcher</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>mvc-dispatcher</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>
    /WEB-INF/mvc-dispatcher-servlet.xml,        
      /WEB-INF/spring-security.xml
      </param-value>
</context-param>

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener> 

<!-- Spring Security -->

<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>
<listener>
    <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<listener>
    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>

Unfortunately whenever i send a request it throws this:

{
  "error": "invalid_grant",
  "error_description": "Bad credentials"
 }

Need Help !

3

3 Answers

0
votes

You only need to pass the username, password (both of these are for the user, not the client), grant_type and client_id request params.

You must send an Authorization header, containing BASIC auth using the client ID as the username and the client secret as the password.

This is how you would do it in cURL for example:

$ curl -u restapp:restapp http://localhost:8080/asset-manager/oauth/token -d grant_type=password -d client_id=restapp
0
votes

I had a similar issue when upgrading from Spring Security 4.0.4 to 4.1.1.

Try changing "alias" in your authentication-manager bean definition to "id":

<authentication-manager id="authenticationManager"
               xmlns="http://www.springframework.org/schema/security">
    <authentication-provider>
        <user-service>
            <user name="ankita" password="ankita" authorities="ROLE_USER" />
        </user-service>
    </authentication-provider>
<authentication-provider user-service-ref="clientDetailsUserService" />

I found that for some reason the "alias" definition in the custom "authenticationManager" does not get recognized. When I changed "alias" to "id", my custom AuthenticationManager worked again.

I saw this issue when testing Spring Security 4.1.0, so I'm not sure what changed between Spring Security 4.0.4 and 4.1.0 that caused this to break.

I tried to search the release notes and issues in the Spring Security github, but couldn't find anything related to this. All I know is that this small fix worked for me.

Hope this helps.

0
votes

I had to change some of the properties in spring-security.xml file with updated functionalities and also i override some of classes such as AuthenticationProvider and ClientDetailsService according to my use case, also i have used redis as my token store , you can replace it in memory token store

My updated spring-security.xml is:

<?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:context="http://www.springframework.org/schema/context"
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.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.1.xsd 
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd ">

<!-- This is default url to get a token from OAuth -->
<http pattern="/oauth/token" create-session="stateless"
    authentication-manager-ref="clientAuthenticationManager"
    xmlns="http://www.springframework.org/schema/security">
    <intercept-url pattern="/oauth/token" access="isFullyAuthenticated()" method="POST"/>
    <intercept-url pattern="/v1/access_token" access="isFullyAuthenticated()" method="POST"/>
    <anonymous enabled="false" />
    <http-basic entry-point-ref="oauthAuthenticationEntryPoint" />
    <!-- include this only if you need to authenticate clients via request 
        userService parameters -->
    <custom-filter ref="clientCredentialsTokenEndpointFilter"
        after="BASIC_AUTH_FILTER" />
    <access-denied-handler ref="oauthAccessDeniedHandler" />
    <csrf disabled="true" />
</http>

<!-- This is where we tells spring security what URL should be protected 
    and what roles have access to them -->
<http pattern="/v1/**" 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="/v1/application/register" access="hasRole('ROLE_ADMIN')" />
    <intercept-url pattern="/v1/application/{clientId}" access="hasAnyRole('ROLE_USER','ROLE_ADMIN')" />
    <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
    <access-denied-handler ref="oauthAccessDeniedHandler" />
    <csrf disabled="true" />
</http>

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

<bean id="clientAuthenticationEntryPoint"
    class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    <property name="realmName" value="test/client" />
    <property name="typeName" value="Basic" />
</bean>

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

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

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

<authentication-manager id="clientAuthenticationManager"
    xmlns="http://www.springframework.org/schema/security">
    <!-- <authentication-provider user-service-ref="clientDetailsUserService" 
        /> -->
    <authentication-provider ref="AuthenticationProvider" />
</authentication-manager>

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

<authentication-manager
    xmlns="http://www.springframework.org/schema/security" alias="authenticationManager">
    <authentication-provider ref="AuthenticationProvider" />
</authentication-manager>

<bean id="AuthenticationProvider"
    class="com.timeinc.security.biz.client.security.impl.AuthenticationProviderImpl">
    <property name="userService" ref="userService"></property>
</bean>

<bean id="tokenStore"
    class="org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore">
    <constructor-arg name="connectionFactory" ref="redisConnectionFactory" />
</bean>

<!--create redis connection factory and set db 1 -->
<bean id="redisConnectionFactory"
    class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="hostName" value="${redisHost}" />
    <property name="port" value="${redisPort}" />
    <property name="password" value="${redisPassword}" />
    <property name="database" value="${redisDatabase}" />
</bean>

<bean id="tokenServices" class="com.timeinc.security.biz.client.security.impl.DefaultTokenServicesImpl">
    <property name="tokenStore" ref="tokenStore" />
    <property name="supportRefreshToken" value="true" />
    <property name="accessTokenValiditySeconds" value="${accessTokenValiditySeconds}" />
    <property name="refreshTokenValiditySeconds" value="${refreshTokenValiditySeconds}"></property>
    <property name="clientDetailsService" ref="clientDetailsService" />
</bean>

<bean id="requestFactory"
    class="org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory">
    <constructor-arg name="clientDetailsService" ref="clientDetailsService" />
</bean>

<bean id="userApprovalHandler"
    class="org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler">
    <property name="tokenStore" ref="tokenStore" />
    <property name="clientDetailsService" ref="clientDetailsService" />
    <property name="requestFactory" ref="requestFactory" />
</bean>

<oauth:authorization-server
    client-details-service-ref="clientDetailsService" token-services-ref="tokenServices"
    user-approval-handler-ref="userApprovalHandler">
    <oauth:authorization-code />
    <oauth:implicit />
    <oauth:refresh-token />
    <oauth:client-credentials />
    <oauth:password />
</oauth:authorization-server>

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


<bean id="clientDetailsService" class="com.timeinc.security.biz.client.security.impl.ClientDetailsServiceImpl">
    <property name="appClient" ref="appClient"></property>
</bean>
<bean id="httpSessionSecurityContextRepository"
    class='org.springframework.security.web.context.HttpSessionSecurityContextRepository'>
    <property name='allowSessionCreation' value='false' />
</bean>

<bean id="userService" class="com.timeinc.security.biz.client.security.impl.UserDetailsServiceImpl">
    <property name="appClient" ref="appClient"></property>
</bean>

<bean id="securityContextPersistenceFilter"
    class="org.springframework.security.web.context.SecurityContextPersistenceFilter">
    <constructor-arg ref="httpSessionSecurityContextRepository" />
</bean>
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
    <constructor-arg>
        <list>
            <sec:filter-chain pattern="/**"
                filters="securityContextPersistenceFilter" />
        </list>
    </constructor-arg>
</bean>
<sec:global-method-security
    pre-post-annotations="enabled" proxy-target-class="true">
    <!--you could also wire in the expression handler up at the layer of the 
        http filters. See https://jira.springsource.org/browse/SEC-1452 -->
    <sec:expression-handler ref="oauthExpressionHandler" />
</sec:global-method-security>

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

</beans>