Announcement Announcement Module
Collapse
No announcement yet.
OAuth approach for client authentication Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • OAuth approach for client authentication

    Hi, We have below architecture :
    Java Service consumers are consuming spring based REST full web services.
    We want to provide security for these services.
    So we do not have end user but just the client(java service consumers).
    I am researching on the ways to authenticate the client.
    I completed a POC with below configuration at service side. With this configuration I just sent the clientId in RestTemplate and service was able to authenticate.
    But my team wants a more robust way to authenticate client.
    Can you suggest which will be suitable method for client authentication in this case with sample configuration ?
    Also can you tell what are the merits/demerits of username/pwd versus secret key approach ?

    Code:
    <http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="authenticationManager"
    		xmlns="http://www.springframework.org/schema/security">
    		<intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
    		<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" before="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="/ws/claims/**" 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="/ws/claims/**" access="ROLE_CLIENT,SCOPE_TRUST" />
    		<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
    		<access-denied-handler ref="oauthAccessDeniedHandler" />
    	</http>
    
    
    	<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    		<property name="realmName" value="sparklr2" />
    	</bean>
    
    	<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    		<property name="realmName" value="sparklr2/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" />
    			</list>
    		</constructor-arg>
    	</bean>
    
    	<authentication-manager alias="authenticationManager" xmlns="http://www.springframework.org/schema/security">
    		<authentication-provider user-service-ref="clientDetailsUserService" />
    	</authentication-manager>
    
    	<bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
    		<constructor-arg ref="clientDetails" />
    	</bean>
    
    	<bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.JdbcTokenStore">
    		 <constructor-arg index="0"><ref bean="dataSource"/></constructor-arg>
    	</bean>
    
    	<bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
    		<property name="tokenStore" ref="tokenStore" />
    		<property name="supportRefreshToken" value="true" />
    		<property name="clientDetailsService" ref="clientDetails" />
    	</bean>
    
    	<bean id="userApprovalHandler" class="org.springframework.security.oauth2.provider.approval.TokenServicesUserApprovalHandler">
    		<property name="tokenServices" ref="tokenServices" />
    	</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="sparklr" token-services-ref="tokenServices" />
    
    <oauth:client-details-service id="clientDetails">
    	<oauth:client client-id="my-client-with-registered-redirect" authorized-grant-types="authorization_code,,client_credentials"
    			authorities="ROLE_CLIENT" redirect-uri="http://anywhere?key=value" scope="read,trust" />	
    </oauth:client-details-service>
    
    <mvc:annotation-driven />
    
    <mvc:default-servlet-handler />
    
    <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" />
    
    	<!--Basic application beans. -->
    	<bean id="viewResolver" class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    		<property name="mediaTypes">
    			<map>
    				<entry key="json" value="application/json" />
    			</map>
    		</property>
    		<property name="viewResolvers">
    			<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    				<property name="prefix" value="/WEB-INF/jsp/"></property>
    				<property name="suffix" value=".jsp"></property>
    			</bean>
    		</property>
    		<property name="defaultViews">
    			<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
    				<property name="extractValueFromSingleKeyModel" value="true" />
    			</bean>
    		</property>
    	</bean>
    
    	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    		<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
    		<property name="url" value="jdbc:oracle:thin:@sla740q1.unix.magellanhealth.com:1521/mpsappdv.magellanhealth.com"/>
    		<property name="username" value="liferayAppUserQ"/>
    		<property name="password" value="W3lc0m301"/>
    	</bean>
    
    </beans>

  • #2
    Originally posted by akawale View Post
    With this configuration I just sent the clientId in RestTemplate and service was able to authenticate.
    Not quite sure what you mean by that, or maybe I'm looking at the wrong part of your config file. Your /ws/claims/** resources are protected by the "resourceServerFilter" which decodes an OAuth2 bearer token in the header of every request. It knows the client id without you adding it explicitly to the request in some other way, and any request you send without the auth header is going to fail with a 403. So it looks like that part is already working.

    If you don't want to use OAuth2 for these calls then there are plenty of alternatives, but OAuth2 has the merits of being a (reasonably) well documented and understood standard. HTTP Basic might be just as good for some purposes (usually OAuth2 is in the mix because you want some part of your system to be able to call another on behalf of a user).

    Comment


    • #3
      Thanks Dave for Reply. I am fine with the '/ws/claims' part. Thanks for your additional details on that.
      The question is on the first part where client sends request with pattern="/oauth/token" to get the access token.
      Here I am just sending the client id and getting the access token.
      The question is 'As part of Oauth2 are there any additional means to authenticate client when it sends this request for token? '
      From the xml above below is my current configuration for client details :I have heard about the secret key and password approach. which one is better ?
      Code:
      <oauth:client-details-service id="clientDetails">
      	<oauth:client client-id="my-client-with-registered-redirect" authorized-grant-types="authorization_code,,client_credentials"
      			authorities="ROLE_CLIENT" redirect-uri="http://anywhere?key=value" scope="read,trust" />	
      </oauth:client-details-service>

      Comment


      • #4
        The spec says that the token endpoint should be protected and recommends HTTP Basic auth, which is easy to set up with Spring Security (just like in sparklr sample). You even have it in your config, but your client doesn't have a secret so it's not very secure.

        Comment


        • #5
          Hi Dave. thanks for your inputs. I configured a client at my service side with a secret.
          Below are client_details :
          Code:
          insert into oauth_client_details 
          ( scope , client_secret , authorized_grant_types,
          web_server_redirect_uri, authorities, client_id) 
          values ('read,trust','ak_secret', 'authorization_code,implicit','http://anywhere?key=value','ROLE_CLIENT','my-client-with-secret');
          i am sending a request from my service_consumer as below. But I am getting a 'Caused By: error="invalid_client", error_description="Bad client credentials" Error. i am noticing that the ClientCredentialsTokenEndpointFilter.java is getting
          client_secret value as null. Can you please help where I am going wrong here ?
          Code:
          ClientCredentialsResourceDetails resource = new ClientCredentialsResourceDetails();
          //System.out.println("url -- > " + "http://mo01dt0150:7001/sparklr/oauth/token");
          resource.setAccessTokenUri("http://localhost:7001/ClaimsHistoryService/oauth/token");
          resource.setClientId("my-client-with-secret");
          resource.setId("sparklr");
          resource.setScope(Arrays.asList("read"));
          resource.setClientSecret("ak_secret");
          System.out.println("is client only -- > " + resource.isClientOnly());	
          OAuth2RestTemplate template2 = new OAuth2RestTemplate(resource);
          OAuth2AccessToken oldToken = template2.getAccessToken();

          Comment


          • #6
            You inserted data into a database but you are still using the in-memory ClientDetailsService (as configured in your original post)?

            N.B. you shouldn't really use or need to use the ClientCredentialsTokenEndpointFilter (it's for "legacy" mode where the client secret is included in the form parameters).

            Comment


            • #7
              Thanks Dave. After removing the ClientCredentialsTokenEndpointFilter its working fine. If I send the client secret in resource then
              client is getting authenticated otherwise its returning unauthenticated.
              I dont see any option to mark the post as 'answered'.
              Also I forgot to inform you that I moved the client_details config to DB.So I am using the JdbcClientDetailsService.
              Code:
              <bean id="clientDetails" class="org.springframework.security.oauth2.provider.JdbcClientDetailsService">
              		<constructor-arg index="0"><ref bean="dataSource"/></constructor-arg>
              </bean>

              Comment

              Working...
              X