Announcement Announcement Module
Collapse
No announcement yet.
OAuth Login Matching Problem Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • OAuth Login Matching Problem

    Hi all,

    I'm extremely new to Spring, Spring Security, and OAuth (about 1 week into it.) On top of that I'm a Co-op student working for a corporation doing Rails dev, and I only have about 4 months of web development under my belt, so I'm looking for some experienced Spring devs for advice. I'll explain my problem and include some code.

    I need to create an OAuth provider (Spring side) and an OAuth client (Rails side). The implementation is supposed to be a POC and very simple. Because the documentation for OAuth is NOT for beginners, I'm having some trouble. I'm currently dissecting Sparklr and Tonr to understand the working parts and I believe I have a very rough provider.

    My application is extremely simple, when you go to localhost://8080/new_security it brings you to a welcome page with a logout button. I have a secured page at '/secured' and when an anonymous user tried to access /secured, they're interrupted with a login form. When a user enters their username and password, OAuth then matches the data against the following configuration:

    Code:
    <oauth:client-details-service id="clientDetails">
    		<oauth:client client-id="my-trusted-client" authorized-grant-types="password,authorization_code,refresh_token,implicit"
    			authorities="admin, ROLE_TRUSTED_CLIENT" scope="read,write,trust" access-token-validity="60" secret="derp"/>
    	</oauth:client-details-service>
    From what I understand the client-details-service is used to match token data (although I could be completely wrong.) Anyway, it should be matching the entered data to the following configuration instead:

    Code:
    <authentication-manager xmlns="http://www.springframework.org/schema/security">
    		<authentication-provider>
    			<user-service id="userDetailsService">
    				<user name="dave" password="derp" authorities="admin" />
    			</user-service>
    		</authentication-provider>
    	</authentication-manager>
    Of course if I enter 'my-trusted-client' for the username and 'derp' for the password, it grants me access and directs me to the secured page--which just tells the user they had to be logged in to view that page. So, I have a feeling I'm getting closer to where I need to be, but my configurations are screwed up because I don't fully understand every aspect of Spring, Security, OAuth.

    I'll paste my security-config.xml file below. Most of the configurations are copied and slightly changed from Sparklr's OAuth configuration xml.

    Keep in mind: any Controllers listed below are included in my application, whether or not I renamed the class.

    Security-Config.xml:
    Code:
    <?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/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
    		http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
    		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
      
    	 
    	<http pattern="/oauth/token" create-session="stateless" authentication-manager-ref="clientAuthenticationManager"
    		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>
    	
    	<http pattern="/oauth/(users|clients)/.*" request-matcher="regex" create-session="stateless" entry-point-ref="oauthAuthenticationEntryPoint"
    		use-expressions="true" xmlns="http://www.springframework.org/schema/security">
    		<anonymous enabled="false" />
    		<intercept-url pattern="/oauth/users/([^/].*?)/tokens/.*"
    			access="#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('write')"
    			method="DELETE" />
    		<intercept-url pattern="/oauth/users/.*"
    			access="#oauth2.clientHasRole('ROLE_CLIENT') and (hasRole('ROLE_USER') or #oauth2.isClient()) and #oauth2.hasScope('read')"
    			method="GET" />
    		<intercept-url pattern="/oauth/clients/.*"
    			access="#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.isClient() and #oauth2.hasScope('read')" method="GET" />
    		<intercept-url pattern="/**" access="denyAll()"/>
    		<custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
    		<access-denied-handler ref="oauthAccessDeniedHandler" />
    		<expression-handler ref="oauthWebExpressionHandler" />
    	</http>
    	
    	<http auto-config="true" use-expressions="true" xmlns="http://www.springframework.org/schema/security"
    	 		authentication-manager-ref="clientAuthenticationManager">
    		  <intercept-url pattern="/secured" access="hasRole('admin')"/>
    	 </http>
    	
    	<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>
    	  
    	<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
    		<authentication-provider user-service-ref="clientDetailsUserService" />
    	</authentication-manager>
    	 
    	<authentication-manager xmlns="http://www.springframework.org/schema/security">
    		<authentication-provider>
    			<user-service id="userDetailsService">
    				<user name="dave" password="derp" authorities="admin" />
    			</user-service>
    		</authentication-provider>
    	</authentication-manager>
    	
    	<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    		<property name="realmName" value="derp" />
    	</bean>
    	
    	<oauth:resource-server id="resourceServerFilter" resource-id="sparklr" token-services-ref="tokenServices" />
    	
    	<oauth:expression-handler id="oauthExpressionHandler" />
    
    	<oauth:web-expression-handler id="oauthWebExpressionHandler" />
    	
    	<bean id="accessConfirmationController" class="com.staples.ccs.AccessConfirmationController">
    		<property name="clientDetailsService" ref="clientDetails" />
    	</bean>
    	
    	<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
    		<property name="realmName" value="derp" />
    		<property name="typeName" value="derp" />
    	</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="clientAuthenticationManager" />
    	</bean>
    	
    	<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.InMemoryTokenStore" />
    
    	<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="com.staples.ccs.UserApprovalHandler">
    		<property name="autoApproveClients">
    			<set>
    				<value>my-less-trusted-autoapprove-client</value>
    			</set>
    		</property>
    		<property name="tokenServices" ref="tokenServices" />
    	</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>
    	
    	<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:client-details-service id="clientDetails">
    		<oauth:client client-id="my-trusted-client" authorized-grant-types="password,authorization_code,refresh_token,implicit"
    			authorities="admin, ROLE_TRUSTED_CLIENT" scope="read,write,trust" access-token-validity="60" secret="derp"/>
    	</oauth:client-details-service>
    	
    </beans>
    If anyone has any idea as to why this is happening, I'd love an explanation because I currently have no clue. Also, any incite on how to fix this would be appreciated as well.

  • #2
    I'm not really sure what the problem is. You have an OAuth2 provider based on the config you posted (copied from sparklr2). It will allow you to authenticate and issue tokens on behalf of the user dave. You might want to add back the separate <http/> filter chain for the /oauth/authorize endpoint, but I'd guess that it probably works as it is. Is something about that not clear? Not working?

    On the Rails side you would be accessing a resource via HTTP (e.g. with RestClient) but I wasn't clear on where that resource would be (the provider in the config file declares a resource-server filter but never uses it, so I assume that's a mistake). To access the protected resource yo would need to have a token, and the flow of obtaining a token could be handled by (e.g.) omniauth (although the standard OAuth2 implementation in omniauth is not a great fit for a Spring OAuth server because it is based on an older spec, but it is doable).

    Comment


    • #3
      Matching Fixed: /oauth/token route errors!

      Dave,

      Thanks for the reply. It was actually an issue on my rails app side, but it was easily fixed: was missing an additional '/' in a route string.

      Of course after fixing this error, I immediately ran into a new error. I'll explain my error and maybe it'll make sense to you, because I'm fairly lost. I'll post my configurations below as well.

      My rails app has 1 page with 1 link. When you click this link it redirects you to localhost:8080/ccs/secured (Spring app) --which is my only secured page. The Spring application then asks the user to log in. The matching problems are gone now, so I log in properly and I'm redirected to the OAuth page asking to either Authorize or Deny access (everything looks good, so far). From here, I click Authorize, and I'm redirected back to my rails app (localhost:3000/redirect/test) and I receive an error:

      Code:
      unauthorized: An Authentication object was not found in the SecurityContext
      {"error":"unauthorized","error_description":"An Authentication object was not found in the SecurityContext"}
      This looks like a rails error, but when I go to localhost:8080/ccs/oauth/token on my browser I get this error:

      Code:
      <oauth>
      <error_description>
      An Authentication object was not found in the SecurityContext
      </error_description>
      <error>unauthorized</error>
      </oauth>
      I know the /oauth/token path is supposed to be available to anonymous users, so I'm confused as to why I get this error when accessing this page, via oauth2 gem and/or by placing this URL in my browser. Because I've changed some things since my last post, I'll repost my web.xml and security-config.xml files so you can review them.

      web.xml:
      Code:
      <?xml version="1.0" encoding="UTF-8"?>
      <web-app version="2.5" 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">
      
      	<!-- The definition of the Root Spring Container shared by all Servlets and Filters -->
      	<listener>
      		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
      	</listener>
      	
      	<!--<listener>
      		<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher
      		</listener-class>
      	</listener>-->
      	
      	<filter>
              <filter-name>clientCredentialsTokenEndpointFilter</filter-name>
              <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
          </filter>
      	
      	<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>
      	
      	<filter-mapping>
              <filter-name>clientCredentialsTokenEndpointFilter</filter-name>
              <url-pattern>/oauth/token</url-pattern>
          </filter-mapping>
      	
      	<context-param>
      		<param-name>contextConfigLocation</param-name>
      		<param-value>
      		/WEB-INF/spring/root-context.xml
      		/WEB-INF/security-config.xml
      		</param-value>
      	</context-param>
      
      	<!-- Processes application requests -->
      	<servlet>
      		<servlet-name>appServlet</servlet-name>
      		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
      		<init-param>
      			<param-name>contextConfigLocation</param-name>
      			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
      		</init-param>
      		<load-on-startup>1</load-on-startup>
      	</servlet>
      		
      	<servlet-mapping>
      		<servlet-name>appServlet</servlet-name>
      		<url-pattern>/</url-pattern>
      	</servlet-mapping>
      	
      	<servlet-mapping>
      	    <servlet-name>appServlet</servlet-name>
      	    <url-pattern>*.css</url-pattern>
        	</servlet-mapping>
      	
      </web-app>

      security-config.xml **Note: I removed the configurations for /oauth/(users|client)/ AND all of the schema definitions from this code block to reduce text size to make this fit. Those two sections have not changed from my first post, though.

      Code:
      <beans
        
      	<http pattern="/oauth/token" use-expressions="true" create-session="stateless" authentication-manager-ref="clientAuthenticationManager"
      		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" />
      		
      		<custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
      		<access-denied-handler ref="oauthAccessDeniedHandler" />
      	</http>
      	
      	<http access-denied-page="/views/login.jsp?authorization_error=true" disable-url-rewriting="true"
      		xmlns="http://www.springframework.org/schema/security">
      		<intercept-url pattern="/oauth/**" access="ROLE_USER" />
      		<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
      
      		<form-login />
      		<logout />
      		<anonymous />
      	</http>
      	
      	<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>
      	  
      	<authentication-manager id="clientAuthenticationManager" xmlns="http://www.springframework.org/schema/security">
      		<authentication-provider user-service-ref="clientDetailsUserService" />
      	</authentication-manager>
      	 
      	<authentication-manager xmlns="http://www.springframework.org/schema/security">
      		<authentication-provider>
      			<user-service id="userDetailsService">
      				<user name="dave" password="derp" authorities="admin, ROLE_USER, ROLE_ADMIN, ROLE_CLIENT" />
      			</user-service>
      		</authentication-provider>
      	</authentication-manager>
      	
      	<bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
      		<property name="realmName" value="derp" />
      	</bean>
      	
      	<oauth:resource-server id="resourceServerFilter" resource-id="secured" token-services-ref="tokenServices" />
      	
      	<oauth:expression-handler id="oauthExpressionHandler" />
      
      	<oauth:web-expression-handler id="oauthWebExpressionHandler" />
      	
      	<bean id="accessConfirmationController" class="com.staples.ccs.AccessConfirmationController">
      		<property name="clientDetailsService" ref="clientDetails" /> <!-- used to be: ref="clientDetails" -->
      	</bean>
      	
      	<bean id="clientAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
      		<property name="realmName" value="derp" />
      		<property name="typeName" value="derp" />
      	</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="clientAuthenticationManager" />
      		<property name="filterProcessesUrl" value="/j_spring_security_check" />
      	</bean>
      	
      	<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.InMemoryTokenStore" />
      
      	<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="com.staples.ccs.UserApprovalHandler">
      		<property name="autoApproveClients">
      			<set>
      				<value>my-less-trusted-autoapprove-client</value>
      			</set>
      		</property>
      		<property name="tokenServices" ref="tokenServices" />
      	</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>
      	
      	<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:client-details-service id="clientDetails">
      		<oauth:client client-id="my-trusted-client" authorized-grant-types="password,authorization_code,refresh_token,implicit"
      			authorities="admin, ROLE_TRUSTED_CLIENT, ROLE_USER, ROLE_ADMIN, ROLE_CLIENT" scope="read,write,trust" access-token-validity="60" secret="derp"
      			redirect-uri="http://localhost:3000/redirect/test"/>
      	</oauth:client-details-service>
      	
      </beans>

      Thanks again in advance!

      Comment

      Working...
      X