Results 1 to 9 of 9

Thread: Oauth1: User authentication is skipped for Token Authorization

Hybrid View

  1. #1
    Join Date
    Sep 2011
    Posts
    14

    Default 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. #2
    Join Date
    Jun 2005
    Posts
    4,231

    Default

    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?

  3. #3
    Join Date
    Sep 2011
    Posts
    14

    Default

    Thanks for your swift reply, Dave.

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

    Quote 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

  4. #4
    Join Date
    Jun 2005
    Posts
    4,231

    Default

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

  5. #5
    Join Date
    Sep 2011
    Posts
    14

    Default

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

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

  6. #6
    Join Date
    Sep 2011
    Posts
    14

    Default

    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.

Posting Permissions

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