Announcement Announcement Module
Collapse
No announcement yet.
Oauth2 Error handling for client credentials in authorization header Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Oauth2 Error handling for client credentials in authorization header

    I'm having problems with OAuth2 error handling.
    After I upgraded to the very latest snapshot BUILD-20120406.070018-90, the error handling behavior changed.
    I believe the Spring security XML configuration changes required by the latest changes, are properly constructed, but
    I'm attaching my xml config files here so that you can help me verify that I got it right.

    If I pass invalid client credentials using the Basic Authorization header for example:

    POST /exerciser-api/oauth/token HTTP/1.1
    Accept: application/json
    Authorization: Basic OTk5OTk5OlByZXZhU2VjcmV0
    Content-Type: application/x-www-form-urlencoded; charset=UTF-8
    grant_type=client_credentials

    Then I get:

    HTTP/1.1 401 Unauthorized
    WWW-Authenticate: Basic realm="Preva"
    Content-Type: text/html;charset=utf-8

    <html>…something generated by Tomcat for a 401 error…</html>

    1. According to the OAuth2 spec, section 5.2, the WWW-Authenticate response header should contain error=”invalid_client”.
    2. The response body (which is optional) should be json not html.

    http://tools.ietf.org/pdf/draft-ietf-oauth-v2-25.pdf


    The BasicAuthenticationFilter is being triggered instead of the ClientCredentialsTokenEndpointFilter.
    The client credentials error results in an Oauth2Exception being thrown by ClientDetailsService at loadUserByUsername
    at ClientDetailsUserDetailsService. This isn't being handled by the oauthAccessDeniedHandler, instead it's being handled
    by Spring basic security (not the Oauth2 extension), which obviously does not know anything about the Oauth2 spec.

    Question is, how can we properly wire this to eliminate the problems described above... or is a fix already in progress?


    Thank you

    I have two xml config files:

    security-context.xml:

    Code:
    <?xml version="1.0" encoding="UTF-8" ?>
    <beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xmlns="http://www.springframework.org/schema/security"
                 xmlns:beans="http://www.springframework.org/schema/beans"
                 xsi:schemaLocation="http://www.springframework.org/schema/security http://www.springframework.org/schem...curity-3.1.xsd
    		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
    
        <http realm="Preva" pattern="/oauth/token" create-session="never" entry-point-ref="oauthAuthenticationEntryPoint"
              authentication-manager-ref="clientAuthenticationManager">
            <intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY"/>
            <anonymous enabled="false"/>
            <http-basic/>
            <!-- This needs to be anonymous so that the auth endpoint can handle oauth errors itself -->
            <!-- This allows you to put client_id=[value]&client_secret=value in the request body -->
            <custom-filter ref="clientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER"/>
            <access-denied-handler ref="oauthAccessDeniedHandler"/>
        </http>
        <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="Preva"/>
        </beans:bean>
    
        <http pattern="/version" security="none"/>
    
        <!-- 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 realm="Preva" 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="/**" access="ROLE_USER,ROLE_TRUSTED_CLIENT"/>
            <!--<custom-filter ref="resourceServerFilter" before="EXCEPTION_TRANSLATION_FILTER" />-->
            <custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER"/>
            <access-denied-handler ref="oauthAccessDeniedHandler"/>
        </http>
    
        <beans:bean id="passwordEncoder" class="org.jasypt.spring.security3.PasswordEncoder">
            <beans:property name="passwordEncryptor">
                <beans:bean class="org.jasypt.util.password.StrongPasswordEncryptor"/>
            </beans:property>
        </beans:bean>
    
        <beans:bean id="clientDetailsUserService"
                    class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
            <beans:constructor-arg ref="clientDetailsService"/>
        </beans:bean>
    
        <authentication-manager id="clientAuthenticationManager" alias="clientAuthenticationManager">
            <authentication-provider user-service-ref="clientDetailsUserService">
                <password-encoder ref="passwordEncoder"/>
            </authentication-provider>
        </authentication-manager>
    
        <authentication-manager alias="authenticationManager">
            <authentication-provider user-service-ref="exerciserAccountAuthenticationService">
                <password-encoder ref="passwordEncoder"/>
            </authentication-provider>
            <authentication-provider ref="easyGymUserAuthenticationProvider"/>
        </authentication-manager>
    
        <global-method-security proxy-target-class="true"
                                pre-post-annotations="enabled" secured-annotations="enabled"
                                jsr250-annotations="enabled">
            <expression-handler ref="oauthExpressionHandler"/>
        </global-method-security>
    
    </beans:beans>
    oauth2-context.xml:

    Code:
    <?xml version="1.0" encoding="UTF-8" ?>
    <beans:beans xmlns="http://www.springframework.org/schema/security/oauth2"
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 xmlns:beans="http://www.springframework.org/schema/beans"
                 xmlns:util="http://www.springframework.org/schema/util"
                 xsi:schemaLocation="
           http://www.springframework.org/schema/security/oauth2 http://www.springframework.org/schem...oauth2-1.0.xsd
           http://www.springframework.org/schema/util http://www.springframework.org/schem...pring-util.xsd
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <beans:bean id="customCompositeTokenGranter"
                    class="org.springframework.security.oauth2.provider.CompositeTokenGranter">
            <beans:constructor-arg>
                <util:list>
                    <beans:bean class="org.springframework.security.oauth2.provider.client.ClientCredentialsTokenGranter" autowire="constructor"/>
                    <beans:bean class="org.springframework.security.oauth2.provider.password.ResourceOwnerPasswordTokenGranter" autowire="constructor"/>
                    <beans:bean class="org.springframework.security.oauth2.provider.refresh.RefreshTokenGranter" autowire="constructor"/>
                    <beans:bean class="com.precor.preva.external.security.MemberManagementSystemTokenGranter" autowire="constructor"/>
                </util:list>
            </beans:constructor-arg>
        </beans:bean>
    
        <authorization-server client-details-service-ref="clientDetailsService"
                              token-services-ref="tokenServices" token-granter-ref="customCompositeTokenGranter">
        </authorization-server>
        <resource-server id="resourceServerFilter" resource-id="preva-api" token-services-ref="tokenServices"/>
    
        <expression-handler id="oauthExpressionHandler"/>
    
    </beans:beans>
    Last edited by Paulo; Apr 10th, 2012, 11:26 AM.

  • #2
    You need to add the entry point reference explicitly to the <http-basic/> element (as in the sparklr2 sample). Can you make that change and see if it helps? (And please use [code][/code] tags to enclose code and log extracts.)

    Comment


    • #3
      Hi Dave,
      Thank you for your prompt reply.
      I added the tags, the post looks a lot better.

      I added the missing entry-point-ref="oauthAuthenticationEntryPoint" to the <http-basic/> element in the configuration.
      It's much better behavior now ... but still not completely correct IMHO

      This is the relevant part of the response I see now:

      Code:
      HTTP/1.1 401 Unauthorized
      WWW-Authenticate: Bearer realm="Preva", error="invalid_token", error_description="Client not found: 999999"
      Content-Type: application/json
      
      {"error":"invalid_token","error_description":"Client not found: 999999"}
      I think the error should be "invalid_client" instead of "invalid_token".
      I think the authentication scheme should be 'Basic' instead of 'Bearer'.

      Do you see anything else I should be doing to get invalid_client instead of invalid_token ?

      Thanks again,
      Paulo & Nick

      Comment


      • #4
        Hi Dave,

        Just to add some more information, I believe the problem is in the DefailWebResponseExceptionTranslator.
        More precisely, it should not do this: (around line 55)

        Code:
        		if (ase instanceof AuthenticationException) {
        			return handleOAuth2Exception(new InvalidTokenException(e.getMessage(), e));
        		}
        Create an InvalidTokenException when the original exception thrown by ClienDetailsServiceImpl is InvalidCLientException.

        I'm going to file a bug for this.

        Thanks,

        Paulo

        Comment

        Working...
        X