Announcement Announcement Module
Collapse
No announcement yet.
Oauth1: User authentication is skipped for Token Authorization Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Oauth1: User authentication is skipped for Token Authorization

    Hi all,

    I've got a OAuth1 provider configuration, based on Spring Security 3.1.0 and Spring security Oauth 1.0.0.M6, that works partly:
    The requestToken endpoint does function.
    Then however, if the client calls the authorize endpoint directly with the request token, the provider just authorizes the request (returning a verifier code), without challenging the user to prove its mandatory role ("ROLE_USER")
    Only if I hit the confirm_access endpoint manually, user authentication kicks in.

    This is the core configuration:
    Code:
      <http entry-point-ref="shibbolethEntryPoint">
        <intercept-url pattern="/oauth/**" access="ROLE_USER" />
        <custom-filter ref="requestHeaderAuthenticationFilter" position="PRE_AUTH_FILTER" />
      </http>
    
      <oauth:provider consumer-details-service-ref="clientDetailsService"
                      token-services-ref="tokenServices"
                      request-token-url="/oauth/requestToken"
                      authenticate-token-url="/oauth/authorize"
                      token-id-param="oauth_token"
                      authentication-failed-url="/oauth/confirm_access"
                      access-token-url="/oauth/accessToken"
                      require10a="false"/>
    As you can see, I rely on a 'pre authenticated' request: an external Shibboleth apache module that puts a REMOTE_USER header on the request. This is triggered by the "shibbolethEntryPoint" which basically redirects the user to an external URL. Only after successful login the request will come back with the REMOTE_USER header set.

    What it looks like to me at this point is:
    the <intercept-url pattern="/oauth/**" access="ROLE_USER" /> seems only to be active for requests to /oauth/confirm_access and not for /oauth/authorize, because a request for the latter is handled by UserAuthorizationProcessingFilter (filter 9 of 12) (therefore not hitting FilterSecurityInterceptor, filter 12 of 12).

    Although there is some authentication check, at UserAuthorizationProcessingFilter:92
    Code:
        if (authentication == null || !authentication.isAuthenticated()) {
          throw new InsufficientAuthenticationException("User must be authenticated before authorizing a request token.");
        }
    this is not hit because the authentication object is an anonymous authentication here, which seems to be enough, as you can see in the log below.
    Code:
    11:17:30,184 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/authorize?oauth_token=7d34636e-764e-4a0e-bfde-6bcc26bb7cbf at position 1 of 12 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
    11:17:30,184 DEBUG [org.springframework.security.web.context.HttpSessionSecurityContextRepository] No HttpSession currently exists
    11:17:30,184 DEBUG [org.springframework.security.web.context.HttpSessionSecurityContextRepository] No SecurityContext was available from the HttpSession: null. A new one will be created.
    11:17:30,185 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/authorize?oauth_token=7d34636e-764e-4a0e-bfde-6bcc26bb7cbf at position 2 of 12 in additional filter chain; firing Filter: 'RequestHeaderAuthenticationFilter'
    11:17:30,185 DEBUG [org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter] Checking secure context token: null
    11:17:30,185 DEBUG [org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter] No pre-authenticated principal found in request
    11:17:30,186 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/authorize?oauth_token=7d34636e-764e-4a0e-bfde-6bcc26bb7cbf at position 3 of 12 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
    11:17:30,186 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/authorize?oauth_token=7d34636e-764e-4a0e-bfde-6bcc26bb7cbf at position 4 of 12 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
    11:17:30,186 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/authorize?oauth_token=7d34636e-764e-4a0e-bfde-6bcc26bb7cbf at position 5 of 12 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
    11:17:30,187 DEBUG [org.springframework.security.web.authentication.AnonymousAuthenticationFilter] Populated SecurityContextHolder with anonymous token: 'org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS'
    11:17:30,187 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/authorize?oauth_token=7d34636e-764e-4a0e-bfde-6bcc26bb7cbf at position 6 of 12 in additional filter chain; firing Filter: 'SessionManagementFilter'
    11:17:30,188 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/authorize?oauth_token=7d34636e-764e-4a0e-bfde-6bcc26bb7cbf at position 7 of 12 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
    11:17:30,188 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/authorize?oauth_token=7d34636e-764e-4a0e-bfde-6bcc26bb7cbf at position 8 of 12 in additional filter chain; firing Filter: 'UnauthenticatedRequestTokenProcessingFilter'
    11:17:30,188 DEBUG [org.springframework.security.oauth.provider.filter.UnauthenticatedRequestTokenProcessingFilter] Request does not require authentication.  OAuth processing skipped.
    11:17:30,189 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/authorize?oauth_token=7d34636e-764e-4a0e-bfde-6bcc26bb7cbf at position 9 of 12 in additional filter chain; firing Filter: 'UserAuthorizationProcessingFilter'
    11:17:30,189 DEBUG [org.springframework.security.oauth.provider.filter.UserAuthorizationProcessingFilter] Request is to process authentication
    11:18:00,240 DEBUG [org.springframework.security.oauth.provider.filter.UserAuthorizationProcessingFilter] Authentication success. Updating SecurityContextHolder to contain: org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
    11:18:00,240 DEBUG [org.springframework.security.oauth.provider.filter.UserAuthorizationSuccessfulAuthenticationHandler] Processing successful authentication successful
    11:18:00,242 DEBUG [org.springframework.security.oauth.provider.filter.UserAuthorizationSuccessfulAuthenticationHandler] Using default Url: /
    11:18:00,242 DEBUG [org.springframework.security.web.DefaultRedirectStrategy] Redirecting to '/?oauth_token=null&oauth_verifier=SqC2Lz'
    Is this behaviour correct? What am I missing?

    Thanks for reading and replying in advance.

    Geert

  • #2
    Your pre-authentication filter does not seem to have found the REMOTE_USER. How would you expect the authenticated principal to pick up the required ROLE_USER (the pre-auth filter presumably has no knowledge of roles)? Maybe you need to switch off anonymous authentication anyway, since you clearly don't want anonymous users to be obtaining access tokens?

    Comment


    • #3
      Thanks for your swift reply, Dave.

      Originally posted by Dave Syer View Post
      Your pre-authentication filter does not seem to have found the REMOTE_USER. How would you expect the authenticated principal to pick up the required ROLE_USER (the pre-auth filter presumably has no knowledge of roles)?
      I would expect that in this case the user should be pointed to the authenticationEntryPoint which will trigger the setting of the REMOTE_USER header...
      By the way: I would say that on the requestToken-request for example the authentication is not yet done, so that request should not fail because of the missing REMOTE_USER header..

      Originally posted by Dave Syer View Post
      Maybe you need to switch off anonymous authentication anyway, since you clearly don't want anonymous users to be obtaining access tokens?
      Good point; I did not try this.
      Now I've disabled anonymous, indeed authorization fails because of missing user authentication. Then the UserAuthorizationProcessingFilter uses its failureHandler to redirect to the failure url.

      Which leads me to a new problem; according to the log, the failure-url is not set:
      Code:
      13:11:35,835 DEBUG [org.springframework.security.oauth.provider.filter.UserAuthorizationProcessingFilter] Authentication request failed: org.springframework.security.authentication.InsufficientAuthenticationException: User must be authenticated before authorizing a request token.
      13:11:35,835 DEBUG [org.springframework.security.oauth.provider.filter.UserAuthorizationProcessingFilter] Updated SecurityContextHolder to contain null Authentication
      13:11:35,835 DEBUG [org.springframework.security.oauth.provider.filter.UserAuthorizationProcessingFilter] Delegating to authentication failure handlerorg.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler@2d597115
      13:11:38,402 DEBUG [org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler] No failure URL set, sending 401 Unauthorized error
      However, in the configuration it is set indeed:
      Code:
                        authentication-failed-url="/oauth/confirm_access"
      When debugging the context startup, I see the failure handler being created as a root bean, in OAuthProviderBeanDefinitionParser.java:83:
      Code:
          BeanDefinitionBuilder simpleUrlAuthenticationFailureHandler = BeanDefinitionBuilder.rootBeanDefinition(SimpleUrlAuthenticationFailureHandler.class);
          String authenticationFailedURL = element.getAttribute("authentication-failed-url");
          if (StringUtils.hasText(authenticationFailedURL)) {
            simpleUrlAuthenticationFailureHandler.addConstructorArgValue (authenticationFailedURL);
          }
      However, the filter ultimately seems to use its own instantiated failure handler, without its setter ever being used, in AbstractAuthenticationProcessingFilter.java:131:
      Code:
          private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
      Again, apparently I'm missing something...

      Regards,
      Geert

      Comment


      • #4
        That looks like a bug in the parser - it creates a bean definition for a AuthenticationFailureHandler and then never uses it. Your guess is as good as mine though. If you want to fix it and send a pull request that would be great (see instructions on README).

        Comment


        • #5
          Created issue SECOAUTH-234, fixed it and sent a pull request.

          Regards,
          Geert
          Last edited by geertp; Apr 5th, 2012, 02:42 AM.

          Comment


          • #6
            Hi,

            Now that the authentication-failure-url bug is out of the way, I face the next challenge.

            I'm wondering whether the following flow is correct for Oauth1 three legged, using this configuration:
            Code:
              <http entry-point-ref="shibbolethEntryPoint">
                <intercept-url pattern="/oauth/**" access="ROLE_USER" />
                <custom-filter ref="requestHeaderAuthenticationFilter" position="PRE_AUTH_FILTER" />
                <anonymous enabled="false" />
              </http>
            
              <oauth:provider consumer-details-service-ref="clientDetailsService"
                              token-services-ref="tokenServices"
                              request-token-url="/oauth/requestToken"
                              authenticate-token-url="/oauth/authorize"
                              token-id-param="oauth_token"
                              authentication-failed-url="/oauth/confirm_access"
                              access-token-url="/oauth/accessToken"
                              require10a="false"/>
            1. oauth client requests /oauth/requestToken with a signed request
            2. oauth server returns a token, without performing authentication of the end user
            3. client requests /oauth/authorize to authorize the request token
            4. server sees that no end user consent has been given yet and redirects to 'authentication-failed-url' (/oauth/confirm_access in my case)
            5. /oauth/confirm_access handling sees that end user is not authenticated yet and redirects to the entry point (shibbolethEntryPoint in my case)
            6. shibbolethEntryPoint authenticates and redirects to /oauth/confirm_access which serves a consent form (confirmationController yada)
            This is how I would expect it to work.
            In practice however, the server in step 5 says "An Authentication object was not found in the SecurityContext" and returns a HTTP 401 with "Authorize: OAuth" response header. (instead of redirecting to shibboleth entry point)
            The log says:
            Code:
            09:27:10,963 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/confirm_access at position 1 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
            09:27:10,963 DEBUG [org.springframework.security.web.context.HttpSessionSecurityContextRepository] HttpSession returned null object for SPRING_SECURITY_CONTEXT
            09:27:10,963 DEBUG [org.springframework.security.web.context.HttpSessionSecurityContextRepository] No SecurityContext was available from the HttpSession: org.eclipse.jetty.server.session.HashedSession:1hv90ged2dn53knqa0vpt8i7w@947379793. A new one will be created.
            09:27:10,965 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/confirm_access at position 2 of 11 in additional filter chain; firing Filter: 'RequestHeaderAuthenticationFilter'
            09:27:10,965 DEBUG [org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter] Checking secure context token: null
            09:27:10,965 DEBUG [org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter] No pre-authenticated principal found in request
            09:27:10,965 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/confirm_access at position 3 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
            09:27:10,965 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/confirm_access at position 4 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
            09:27:10,965 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/confirm_access at position 5 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
            09:27:10,965 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/confirm_access at position 6 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
            09:27:10,965 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/confirm_access at position 7 of 11 in additional filter chain; firing Filter: 'UnauthenticatedRequestTokenProcessingFilter'
            09:27:10,965 DEBUG [org.springframework.security.oauth.provider.filter.UnauthenticatedRequestTokenProcessingFilter] Request does not require authentication.  OAuth processing skipped.
            09:27:10,965 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/confirm_access at position 8 of 11 in additional filter chain; firing Filter: 'UserAuthorizationProcessingFilter'
            09:27:10,965 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/confirm_access at position 9 of 11 in additional filter chain; firing Filter: 'AccessTokenProcessingFilter'
            09:27:10,965 DEBUG [org.springframework.security.oauth.provider.filter.AccessTokenProcessingFilter] Request does not require authentication.  OAuth processing skipped.
            09:27:10,965 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/confirm_access at position 10 of 11 in additional filter chain; firing Filter: 'ProtectedResourceProcessingFilter'
            09:27:10,965 DEBUG [org.springframework.security.oauth.provider.filter.ProtectedResourceProcessingFilter] Supplied OAuth parameters are inadequate. Ignoring.
            09:27:10,966 DEBUG [org.springframework.security.web.FilterChainProxy] /oauth/confirm_access at position 11 of 11 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
            09:27:10,966 DEBUG [org.springframework.security.web.util.AntPathRequestMatcher] Checking match of request : '/oauth/confirm_access'; against '/oauth/**'
            09:27:10,966 DEBUG [org.springframework.security.web.access.intercept.FilterSecurityInterceptor] Secure object: FilterInvocation: URL: /oauth/confirm_access; Attributes: [ROLE_USER]
            09:27:14,965 DEBUG [org.springframework.security.oauth.provider.filter.ProtectedResourceProcessingFilter] org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
            Another question arises by the way: why is the attribute called 'authentication-failed-url'?
            Because it's not actually a matter of authentication failure but rather that of authorization failure: the oauth client has not yet been authorized by the end user.

            Comment


            • #7
              Hi all,

              I will rephrase my question more concisely.
              Perhaps this makes it easier for someone to stand up and enlighten me :-)

              Using sparklr/tonr as inspiration, I expect that this config:

              <oauth: provider authentication-failed-url="/confirm_access">

              will use the /confirm_access as an 'user consent' form, first redirecting the end user to his authentication entrypoint to authenticate, before actually prompting the consent form.
              Is this correct?

              What part of the configuration ensures that authentication takes place?
              (because in my case, the user gets a 401 - "An Authentication object was not found in the SecurityContext", instead of a redirection to an entry point)

              Thanks!

              Regards,
              Geert

              Comment


              • #8
                Ok, found it out in the end.

                It turns out that to disable anonymous (<anonymous enabled="false" />) was a bad move after all.
                This actually breaks the filter chain.
                Normally, an anonymous authentication present in the security context would eventually trigger the decisionmanager (with AffirmativeBased voters) which throws a proper access-denied-exception that in turn is translated into a redirection to the authentication entry point. See this log:

                Code:
                14:59:51,744 DEBUG [org.springframework.security.web.access.intercept.FilterSecurityInterceptor] Secure object: FilterInvocation: URL: /oauth/confirm_access?oauth_token=62637500-811d-4ec1-bb4e-8dde03862d17; Attributes: [ROLE_USER]
                14:59:51,744 DEBUG [org.springframework.security.web.access.intercept.FilterSecurityInterceptor] Previously Authenticated: org.springframework.security.authentication.AnonymousAuthenticationToken@9055e4a6: Principal: anonymousUser; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.WebAuthenticationDetails@957e: RemoteIpAddress: 127.0.0.1; SessionId: null; Granted Authorities: ROLE_ANONYMOUS
                14:59:51,744 DEBUG [org.springframework.security.access.vote.AffirmativeBased] Voter: org.springframework.security.access.vote.RoleVoter@655bf451, returned: -1
                14:59:51,744 DEBUG [org.springframework.security.access.vote.AffirmativeBased] Voter: [email protected]5d3, returned: 0
                14:59:51,747 DEBUG [org.springframework.web.context.support.XmlWebApplicationContext] Publishing event in Root WebApplicationContext: org.springframework.security.access.event.AuthorizationFailureEvent[source=FilterInvocation: URL: /oauth/confirm_access?oauth_token=62637500-811d-4ec1-bb4e-8dde03862d17]
                14:59:51,749 DEBUG [org.springframework.security.web.access.ExceptionTranslationFilter] Access is denied (user is anonymous); redirecting to authentication entry point
                However, if you disable anonymous, the oauth filter chain will dismiss the whole request and respond with a challenge (401 and a "Authorization: Oauth" response header), with the server log saying:
                Code:
                14:55:06,660 DEBUG [org.springframework.security.web.access.intercept.FilterSecurityInterceptor] Secure object: FilterInvocation: URL: /oauth/confirm_access?oauth_token=5a089c60-866f-4295-a9b6-e03527e19694; Attributes: [ROLE_USER]
                14:55:06,660 DEBUG [org.springframework.web.context.support.XmlWebApplicationContext] Publishing event in Root WebApplicationContext: org.springframework.security.access.event.AuthenticationCredentialsNotFoundEvent[source=FilterInvocation: URL: /oauth/confirm_access?oauth_token=5a089c60-866f-4295-a9b6-e03527e19694]
                14:55:06,661 DEBUG [org.springframework.security.oauth.provider.filter.ProtectedResourceProcessingFilter] org.springframework.security.authentication.AuthenticationCredentialsNotFoundException: An Authentication object was not found in the SecurityContext
                The sparklr example, by the way, also breaks when disabling anonymous, in exactly the same way.


                The point for my particular case was to let the client call /oauth/confirm_access url instead of /oauth/authorize directly, to authorize the request token.
                The sparklr case and in the spring-sec-oauth1 configuration got me off on the wrong foot on this: the attribute in <oauth: provider> is called authentication-failed-url even though it's actually an authorization-endpoint to be used by clients.

                Regards,
                Geert

                Comment


                • #9
                  OK, that sounds sensible. How about we change the name of the XML attribute to user-approval-url?

                  Comment

                  Working...
                  X