Announcement Announcement Module
Collapse
No announcement yet.
Oauth2 client for single sign on and pre authenticaion Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Oauth2 client for single sign on and pre authenticaion

    I have a requirement from a customer to use an Oauth2 client not only for resource access but also as a single sign on mechanism. I'm using Spring 3.1.1, Security 3.1.0, and OAuth 1.0.0.M6. The sample application Tonr helped me with a general configuration of an authorization_code resource.

    At first, I thought it should be as simple as changing my security configuration to
    Code:
    intercept-url pattern="/**"
    effectively triggering the OAuth2ClientContextFilter for all requestes. The filter fires correctly and the exception is caught.
    Code:
    ...
    try {
           chain.doFilter(servletRequest, servletResponse);
         }
         catch (Exception ex) {
           OAuth2ProtectedResourceDetails resourceThatNeedsAuthorization = checkForResourceThatNeedsAuthorization(ex);
    ...
    The problem is the in memory OAuth2ProtectedResourceDetails is never created. Instead, a runtime exception is thrown, redirecting the user to the configured application login page and not to the protected resource's login page. The expected exception is AccessTokenRequiredException but ex is type org.springframework.security.access.AccessDeniedEx ception, causing it to just be re thrown.
    Code:
    	
    protected OAuth2ProtectedResourceDetails checkForResourceThatNeedsAuthorization(Exception ex)
    throws ServletException, IOException {
      Throwable[] causeChain = throwableAnalyzer.determineCauseChain(ex);
      AccessTokenRequiredException ase = (AccessTokenRequiredException) throwableAnalyzer.getFirstThrowableOfType(
      AccessTokenRequiredException.class, causeChain);
      OAuth2ProtectedResourceDetails resourceThatNeedsAuthorization;
      if (ase != null) {
          resourceThatNeedsAuthorization = ase.getResource();
          if (resourceThatNeedsAuthorization == null) {
               throw new OAuth2AccessDeniedException(ase.getMessage());
          }
       }
       else {
              // Rethrow ServletExceptions and RuntimeExceptions as-is
    ...
    What I'm trying to do is authenticate a user on the resource server and upon successful authentication, create a user on our client user system (for stats and other functions), using a OAuth2RestTemplate to retrieve user data from the resource server.

    Any suggestions??

  • #2
    What you are trying to do is not impossible, but it will need a few more pieces, I think. There is a newish standard (OpenID Connect: http://openid.net/connect/) for what you want (SSO with OAuth2), and you can get pretty close to it (close enough to achieve SSO) with Spring Security OAuth as it is, but I think you need an extra filter or AuthenticationManager.

    I'm not 100% sure where your AccessDeniedException is coming from, since there doesn't seem to be any way to authenticate a user in your filter chain. We'd need to look at it in more detail to understand what you are doing exactly.

    Anyway, the OAuth2ClientContextFilter is not for authenticating users, it's for setting up an OAuth2ClientContext so that the OAuth2RestTemplate can work with the same interface as a normal RestTemplate. Maybe you can rig something up that works just with that, but how would it know the identity of the user, and who is going to populate the normal Spring SecurityContext? Maybe if your app has no resources of its own and only forwards requests to the resource server you don't need that, so I'd be keen to hear if you get something working.

    What you need for authentication is an authentication filter, replacing form-login or http-basic, or whatever you have in your client app now. I have been using an implementation of such a filter (not open source, but should be any day now) which simply uses the OAuth2RestTemplate to access the users profile on a resource server. As long as that filter is in the chain *after* OAuth2ClientContextFilter it works pretty much as you need.

    N.B. OAuth2ClientContextFilter has changed a bit since 1.0.0.M6, and while it should work with the older code, you can avoid an extra 302 redirect if you update to the latest snapshots.

    Comment


    • #3
      Thank you for your reply Dave! After learning a little more detail about OAuth2 vs other mechanisms like CAS or OpenId, I've come up with a plan. My customer only provides OAuth2 services so using something else is out of the question right now.

      I'm going to create a simple login page. No form, just a button. When the button is clicked, I'll have a controller call a service that uses the OAuth2RestTemplate to try and retrieve user data from the resource server. If allowed, I'll create the internal user with the supplied response data and log that user in. Optionally, I guess I can set a cookie to bypass using OAuth2 for authentication in future requests.

      I would really like to call the OAuth2RestTemplate from within a pre auth filter, eliminating the need for a login page but I guess I'll just have to play around.

      Comment


      • #4
        The filter approach is ultimately simpler and works better from a user experience point of view (they never need to see that button of yours). The filter I am using is here: https://github.com/cloudfoundry/uaa/...entFilter.java. Hopefully you can install that sample app and see it working. There might be issues with getting the right snapshot of Spring Security OAuth - let me know if you try it - but the code is there for browsing now.

        Comment


        • #5
          Working!

          I was successfully able to implement an AbstractAuthenticationProcessingFilter using your example. I'm able to pull user data from the resource server, create the user on our external user management app, and also add the user to the security context to enable a session.

          I had to do some minor tweaks to the OAuth2AccessTokenSupport due to the configuration of the resource server I'm accessing. I forced the client_secret param when retrieving the token as well as added a MappingJacksonHttpMessageConverter that supports the MediaType 'application/x-javascript'. Also, for some reason the EXPIRES_IN property of the JSON token response was coming from the resource server as a string so I had to change OAuth2AccessTokenDeserializer to parse the string into a Long.

          Nonetheless this seems like a good pseudo SSO solution using OAuth2.

          Thanks for your help!

          Comment


          • #6
            Great! If you have time, a pull request, or at least a JIRA tocket, for the change to the serialization of expires_in would be nice (look at the README for instructions and fill in the contributor's agreement).

            What's wrong with application/json?

            What did you mean by the client_secret being "forced"?

            Comment


            • #7
              I was looking for simple SSO solution with SS OAuth2.
              Thanks for sharing

              Comment


              • #8
                @Dave

                The specific resource server I'm working with returns a token response with with non standard 'accept application/x-javascript' only. That's why application/json would not work for me.

                What I meant by forcing the client_secret is that my resource sever requires the client secret to be sent as a param with every token request. I didn't realize that I had a mistake in my configuration so there was no need to change any code. Originally, I had 'authentication-scheme' set to 'query' in order to send the token as a param for resource requests. I figured out that I also had to set the 'client-authentication-scheme' to 'form' in order to send the client_secret for token requests.

                I will open a JIRA ticket and make a pull request for the deserialization of the EXPIRES_IN.

                Comment

                Working...
                X