Announcement Announcement Module
Collapse
No announcement yet.
Authorization Request not in model when posting to AuthorizationEndpoint Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Authorization Request not in model when posting to AuthorizationEndpoint

    I'm not sure whether to post this in spring-security-cas, or spring-security-oauth. Here's the simplest symptom:
    • User attempts to access
      http://localhost:8080/demo/oauth/aut...www.google.com
    • User is redirected to the cas login page for authentication
    • User logs in with correct credentials
    • User is redirected to the confirmation page
    • If the user immediately presses "Confirm" he is redirected to https://www.google.com/?code=ar8st7 (expected behavior)
    • If the user waits 10 seconds and then hits "Confirm" he is taken to an error page stating "Cannot approve uninitialized authorization request."

    After some research, I've concluded that waiting 10 seconds and then hitting confirm triggers an AuthorizationException, which causes spring-security-oauth to ping the cas server to validate the session. The session is validated, but the model map no longer has an authorizationRequest in it when it returns.

    I suspect it's because the authorizationRequest that was added to the model in the authorize() method of AuthorizationEndpoint is now invalid since the CasAuthorizationToken (mapped to authorizationRequest) was regenerated. I thought I may be able to add a WebRequestInterceptor to the stack to add that object back into the model, but the only place I have access to the model is *after* the request has been handled.

    Am I going in the right direction here? You can find relevant configuration below:

    security-config.xml
    Code:
        <!-- Authentication section for oauth endpoints -->
        <sec:http entry-point-ref="casEntryPoint"
            pattern="/oauth2.0/**">
            <sec:custom-filter ref="oauth2ProviderFilter"
                before="PRE_AUTH_FILTER" />
            <sec:custom-filter ref="casFilter" position="CAS_FILTER" />
        </sec:http>
    ConfirmationEndpoint.java
    Code:
    @Controller
    @SessionAttributes("authorizationRequest")
    public class ConfirmationEndpoint {
        @RequestMapping("confirm_access")
        public ModelAndView getAccessConfirmation(ModelMap model) throws Exception {
            // ... some stuff with logging what's in the model. I've tried messing around with it to no effect
            return new ModelAndView(OAuthConstants.CONFIRM_VIEW, model);
        }
    }
    I feel like I've tried too many things to enumerate here. Does anything jump out at you?

  • #2
    It's been a while since I used CAS, so I don't have a good grasp of what's happening to the HttpSession, but I suspect that it must be a new session (standard practice when re-authenticating), and hence the original request is lost. I suppose you could provide a SessionAttributeStore so that your Spring session attributes are stored somewhere independent of the CAS process.

    Comment


    • #3
      Originally posted by Dave Syer View Post
      It's been a while since I used CAS, so I don't have a good grasp of what's happening to the HttpSession, but I suspect that it must be a new session (standard practice when re-authenticating), and hence the original request is lost. I suppose you could provide a SessionAttributeStore so that your Spring session attributes are stored somewhere independent of the CAS process.
      It looks like ExceptionTranslationFilter saves off the request in an HttpSessionRequestCache before redirecting to the authenticationEntryPoint, and SavedRequestAwareAuthenticationSuccessHandler retrieves that request when authentication succeeds. So I guess it's a new session, which is why @SessionAttributes isn't injecting the authorizationRequest into the model.

      Could I inject the authorizationRequest object into the model manually? I have access to the HttpRequest object in several places, I would just need to know where to put it (parameters? attributes?) so it shows up in the ModelMap for the POST handler in AuthorizationEndpoint.

      Comment


      • #4
        Spring uses teh HttpSession by default for @SessionAttributes. So you can inject it there if you have a chance. I'm not sure how that will be possible though.

        Comment


        • #5
          Originally posted by Dave Syer View Post
          Spring uses teh HttpSession by default for @SessionAttributes. So you can inject it there if you have a chance. I'm not sure how that will be possible though.
          Bummer, I had typed up a message a few hours ago and it didn't get posted.

          Anyway, I'm noticing symptoms of a race condition when trying to debug this issue. I think there is another thread listening for requests on j_spring_cas_security_check that do *something* to either the session or the saved request. When I place a breakpoint in that thread and pause for a few seconds the authorize endpoint returns a code successfully. Am I on the right track?

          Comment


          • #6
            I'm not sure why there would be more than one thread processing requests from the same session (normally it's just a sequence of redirects). Do you have an Ajax channel from the browser? So the race condition is a bit unexpected. But I guess it shows you that where you can stash the authorization request and re-inject it into the new session (a SessionAuthenticationStrategy maybe?).

            Comment


            • #7
              Interesting... In my situation the webapp represents a CAS authentication server and a CAS client. According to the spring-security-cas documents, the CAS client would always be listening on /j_spring_cas_security_check. I had originally assumed that this would be a separate thread, but it makes more sense that it's just another endpoint that the handler mapping bean checks.

              I think by default we're using a SessionFixationProtectionStrategy with migrateSession=true, which in theory should have stored off the entire session (including the authorizationCode field) before we attempt to authenticate. I'll have to remember to walk through that class with the debugger once I get this working.

              I like the idea of re-injecting that field into the session with my own SessionAuthenticationStrategy though. I'll work on getting that up and running and report back.

              Comment


              • #8
                So for some reason the custom SessionAuthenticationStrategy isn't getting run. The SessionManagementFilter is detecting the session as invalid. According to the source, either:
                • Authentication is null in the SecurityContextHolder
                • The authentication found represents an anonymous user


                and either:
                • The session id in the request is null (not the case for me)
                • The requested session is timed out


                At least this explains why the SessionFixationProtectionStrategy didn't work as I expected. The SessionManagementFilter never used it because it detected an expired session! I just need to figure out why the authentication is either null or anonymous at this point.

                Comment


                • #9
                  Solved it. CAS had an action that reset the lifetime of the HTTP session to 2 seconds. Removing that action made everything work correctly. No other custom code needed.

                  I may need to make another thread for this, but why does AuthorizationEndpoint use @SessionAttributes and not RedirectAttributes in the first place? It seems to make more sense to store the authorizationRequest there instead.

                  Comment


                  • #10
                    Glad you got something working, although I'm not sure I understood the problem properly.

                    As for the final question, @SessionAttributes is better if you don't have control over the content, or if some of it might be sensitive (as is the case with an AuthorizationRequest). But I take the point: the only parts of the request that we really need to store are the ones that come in as request parameters. If you think there's a useful improvement to be made open a JIRA ticket and/or send some code.

                    Comment

                    Working...
                    X