Results 1 to 4 of 4

Thread: Oauth2 Error handling for client credentials in authorization header

  1. #1
    Join Date
    Oct 2009
    Posts
    10

    Default 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 at 11:26 AM.

  2. #2
    Join Date
    Jun 2005
    Posts
    4,230

    Default

    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.)

  3. #3
    Join Date
    Oct 2009
    Posts
    10

    Default

    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

  4. #4
    Join Date
    Oct 2009
    Posts
    10

    Default

    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •