Announcement Announcement Module
Collapse
No announcement yet.
using s2-oauth provider for sign-on: one-time grant prompt and returning same token Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • #16
    hi dave,

    i grabbed a recent M5+ version from github maybe a week ago, and tried out some of these ideas.

    looks like the latest code base is being a good citizen w.r.t. not creating new tokens when there is an existing one, so nice work on that!

    i have been working on attempting to hook as discussed above such that the client-user will only get prompted once for consent, and then subsequently will get whisked through without a consent prompt as long as there is an appropriate existing token.

    i'm attempting to do this in a provider based on sparklr2 using the following altered version of the provided AccessConfirmationController:

    Code:
    	
    package org.springframework.security.oauth.examples.sparklr.mvc;
    
    import java.security.Principal;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.oauth2.common.OAuth2AccessToken;
    import org.springframework.security.oauth2.provider.AuthorizationRequest;
    import org.springframework.security.oauth2.provider.ClientDetails;
    import org.springframework.security.oauth2.provider.ClientDetailsService;
    import org.springframework.security.oauth2.provider.OAuth2Authentication;
    import org.springframework.security.oauth2.provider.token.TokenStore;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.ModelAttribute;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.SessionAttributes;
    
    /**
     * Controller for retrieving the model for and displaying the confirmation page for access to a protected resource.
     * 
     * @author Ryan Heaton
     */
    @Controller
    @SessionAttributes(types = AuthorizationRequest.class)
    public class AccessConfirmationController
    {
      private static final Logger log = LoggerFactory.getLogger(AccessConfirmationController.class);
      private ClientDetailsService clientDetailsService;
      private TokenStore tokenStore;
    
      @RequestMapping("/oauth/confirm_access")
      public String getAccessConfirmation(@ModelAttribute AuthorizationRequest authorizationRequest, Model model, Principal principal)
          throws Exception
      {
        // if there is already a token granted for this client/user/resource/scope quadruple, then 'short-circuit' confirmation page.
        //
        // current strategy for determining this is to use implementation of TokenStore.getAccessToken(OAuth2Authentication) which
        // uses DefaultAuthenticationKeyGenerator which in turn accounts for the client/user/resource/scope quadruple.
        //
        // current 'short-circuit' strategy is to forward to a page (based on the original access_confirmation.jsp)
        // which does a javascript based 'auto-post' to simulate user accepting on consent page...
    
        // at this point, authorizationRequest doesn't have resource-ids because they aren't passed on the request,
        // but rather stashed with the client info, so we need to dig up ClientDetails, grab resource-ids and create
        // a new AuthorizationRequest based on the original but augmented with resource-ids.
        //
        ClientDetails clientDetails = clientDetailsService.loadClientByClientId(authorizationRequest.getClientId());
    
        authorizationRequest = new AuthorizationRequest(clientDetails.getClientId(), authorizationRequest.getScope(),
            clientDetails.getAuthorities(), clientDetails.getResourceIds());
    
        UsernamePasswordAuthenticationToken userAuthentication = new UsernamePasswordAuthenticationToken(principal, null, null);
        OAuth2Authentication oa2Authentication = new OAuth2Authentication(authorizationRequest, userAuthentication);
        OAuth2AccessToken oa2AccessToken = tokenStore.getAccessToken(oa2Authentication);
    
        String viewName = null;
    
        if (oa2AccessToken != null)
        {
          // already issued a token for this client/user/resource/scope quadruple, so no need to prompt for access again,
          // so initiate short-circuit strategy
          //
          viewName = "forward:/auto-confirm";
        }
        else
        {
          model.addAttribute("auth_request", authorizationRequest);
          model.addAttribute("client", clientDetails);
          viewName = "access_confirmation";
        }
    
        return viewName;
      }
    
      @Autowired
      public void setClientDetailsService(ClientDetailsService clientDetailsService)
      {
        this.clientDetailsService = clientDetailsService;
      }
    
      public void setTokenStore(TokenStore tokenStore)
      {
        this.tokenStore = tokenStore;
      }
    }
    i originally tried to avoid the 'auto-submit-form' page by passing control directly to AuthorizationEndpoint.approveOrDeny(), but ran into issues downstream in TokenEndpoint when AuthorizationCodeTokenGranter tossed a RedirectMismatchException because redirect-url wasn't included in the parameters for some reason with this strategy.

    at that point, i was too beat down to investigate further, so i fell back to the clunky (but tried-and-true) 'auto-submit-form' method:

    Code:
    <!DOCTYPE html>
    <html>
    <head>
    <script src="http://code.jquery.com/jquery-latest.min.js"></script>
    <script>
    	$(document).ready(function() {
    		$("#confirmationForm").submit();
    	});
    </script>
    </head>
    
    <body>
    	<form id="confirmationForm" name="confirmationForm"
    		action="<%=request.getContextPath()%>/oauth/authorize" method="post">
    		<input name="user_oauth_approval" value="true" type="hidden" />
    	</form>
    </body>
    
    </html>
    anyway, just wanted to kick my solution out for comments.

    thanks,
    tony.
    Last edited by tony_k; Feb 17th, 2012, 10:06 PM. Reason: use spaces v tabs in code block to improve readability

    Comment


    • #17
      i actually just spotted kent's comments from last month, and while i think some of dialog may be rendered moot by improvements introduced in M5, i'll +1 his comment that it would be more convenient if s2-oauth integrated the functionality i introduced into the access-confirmation-controller w.r.t. short-circuiting the consent prompt if a token appropriate to the request already exists.

      i'd even venture to suggest that you could default that behavior to 'true' and allow for that to be customized to 'false' if desired.

      Comment


      • #18
        That controller is provided by the application (not by the framework), but I can definitely get behind changing the sample app if it works better that way. Can you think of a way to abstract the behaviour into a generic contoller that the framework could provide?

        Comment


        • #19
          hi dave,

          yes, i understand that access-confirmation-controller is part of sparklr v s2-oauth.

          i was thinking that the behavior could be integrated into s2-oauth in the flow
          prior to calling into the a-c-c controller, but admittedly didn't dig into the detail of if/how that could occur.

          i didn't consider the concept of packing up an a-c-c into s2-oauth, but if it makes sense to keep that behavior
          in a discrete controller (v maybe pulling it into one or more existing elements of the framework),
          then i can give it a little more thought. i think it's possible to write that auto-submit page directly into the response
          from the controller, so a discrete page need not exist (some more thought might be required w.r.t. details like
          grabbing jquery from a cdn).

          as i indicated, i was instinctively trying to avoid a trip back to the browser, but the request mapping for AuthorizationEndpoint.approveOrDeny() was configured as a POST, and i ran into that trouble calling it directly, but if i knew you were behind the idea at some level, i could try some things that may be cleaner given some accommodation within the s2-oauth codebase.
          Last edited by tony_k; Feb 18th, 2012, 08:01 PM. Reason: typo

          Comment


          • #20
            hi dave,

            i had some success illustrating how this functionality might be integrated directly into s2-oauth.

            i have transferred the discussion and relevant information here as it seemed more appropriate for this jira entry:

            https://jira.springsource.org/browse...#comment-75969

            sorry i'm not more git-savvy just yet, or i would have provided code in git vs attachments to the jira entry.

            can you review when you have an opportunity and comment as appropriate?

            thanks!
            tony.

            Comment


            • #21
              hi dave,

              just got around to trying your solution for

              https://jira.springsource.org/browse/SECOAUTH-129

              and it worked like a champ!

              Code:
                <bean id="userApprovalHandler" class="org.springframework.security.oauth2.provider.approval.TokenServicesUserApprovalHandler">
                  <property name="tokenServices" ref="tokenServices" />
                </bean>
              
                <oauth:authorization-server client-details-service-ref="clientDetails" token-services-ref="tokenServices"
                  user-approval-handler-ref="userApprovalHandler">
                  <oauth:authorization-code />
                  <oauth:implicit />
                  <oauth:refresh-token />
                  <oauth:client-credentials />
                  <oauth:password />
                </oauth:authorization-server>
              thanks again!

              tony...

              Comment

              Working...
              X