Announcement Announcement Module
Collapse
No announcement yet.
OAuth 2 - RC3 - client_id Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • OAuth 2 - RC3 - client_id

    I'm on a quest to upgrade our OAuth version from M3 to RC3.

    My current snag is:

    When I punch in the user credentials and then drive /oauth/token (which we override to /access_token) the client_id seems to be getting wacked or not picked up.

    Here is my auth server:
    Code:
      <oauth:authorization-server   authorization-endpoint-url="/authorize" 
          							client-details-service-ref="jdbcClientDetailsService" 
          							token-services-ref="oAuth2ProviderTokenServices" 
          							user-approval-page="/token/confirm_access" 
          							user-approval-handler-ref="oL2ApprovalHandler"
          							token-endpoint-url="/access_token">
      	<oauth:authorization-code/>
      	<oauth:implicit/>
      	<oauth:refresh-token/>
      	<oauth:client-credentials/>
      	<oauth:password/>
      </oauth:authorization-server>
    http://localhost:8080/oauth/access_t...code=ROLE_USER


    After this GET request I would assume that the client_id pulled in TokenEndPoint would be 'FOO', but it ends up to be the username I punched into the form.

    Using the FROM and M3 all is well. I did see a similar post but I don't know if this was related.

    Any help would be great! Sorry if a little vague as this just sounds like a WTH?! =)

  • #2
    I'm not really following 100% what you did here. The token endpoint is secured with Basic auth and the credentials should be the client id and secret, so where was the "form" you were talking about? Your client should be POSTing to the token endpoint and the query you show is a GET (I assume), with way more in it than I would expect (you need grant_type and code, and maybe redirect_uri, but that's it) and the code doesn't look like a valid authorization code.

    Does it work before you change the endpoint urls?

    Comment


    • #3
      Originally posted by Dave Syer View Post
      I'm not really following 100% what you did here. The token endpoint is secured with Basic auth and the credentials should be the client id and secret, so where was the "form" you were talking about? Your client should be POSTing to the token endpoint and the query you show is a GET (I assume), with way more in it than I would expect (you need grant_type and code, and maybe redirect_uri, but that's it) and the code doesn't look like a valid authorization code.
      Thanks for the response Dave. Sorry about the details as I've been sort of pounding my head on the migration.

      Originally posted by Dave Syer View Post
      Does it work before you change the endpoint urls?
      No. I had to hunt down that the url wasn't get set in RC2 and hence I'm using RC3.

      Here is my work flow (which has been working with M3):

      My little test client makes this request:

      http://localhost:8080/oauth/access_t...code=ROLE_USER

      This obviously fails because there is no Basic Auth that has been preformed.

      Then I'm rediected to my setup page that asks the me to Auth. Once I've Auth, then I attempt to get a Token.

      I've added grant_type and the redirect is there.

      Here I executed a stack trace in my jdbcClientDetailsService
      'loadClientByClientId(JdbcClientDetailsService.jav a:35),org.springframework.security.oauth2.provider .endpoint.TokenEndpoint.getAccessToken(TokenEndpoi nt.java:79), sun.reflect.NativeMethodAccessorImpl.invoke0(Nativ e Method),'

      This is what's driving me crazy. The client_id I would except to be FOO, but the client_id ends up being my basic auth loginID.

      There is something really stupid going on. I was thinking about tearing up the oauth.jar and dumping out the Request params but how client_id is becoming the basic auth id is beyond me.

      Thanks again for all your help.

      Comment


      • #4
        No one should be asking the token endpoint for an authorization code - that comes from the authorization endpoint and always has done. So your client is doomed to fail anyway because of that, but then because you entered that URL in a browser (I guess) it is prompting you for basic auth credentials and you are entering user credentials, not client credentials. The realm in the basic auth should alert you to the fact that the 401 response requires client credentials to be presented.

        Comment


        • #5
          Originally posted by Dave Syer View Post
          No one should be asking the token endpoint for an authorization code - that comes from the authorization endpoint and always has done. So your client is doomed to fail anyway because of that, but then because you entered that URL in a browser (I guess) it is prompting you for basic auth credentials and you are entering user credentials, not client credentials. The realm in the basic auth should alert you to the fact that the 401 response requires client credentials to be presented.
          Doom and Gloom baby!

          Had to go back and double check the spec because my translation here is terrible.

          From the Client I'm submitting a Authorization Code and Redirect URL. The 'client_id' for which you see in that URL (FOO) is what I would expect TokenEndPoint to grab and submit to loadClientByClientId when it is reached. However, like I have stated, the ClientID being grabbed is the Basic Auth ID. These two, Basic Auth ID and Client ID, are not the same...and they were certainly not the same when we used Spring OAuth V1 M3.

          Code:
          ublic ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal,
                                  @RequestParam("grant_type") String grantType, @RequestParam Map<String, String> parameters) {
           
                          if (!(principal instanceof Authentication)) {
                                  throw new InsufficientAuthenticationException(
                                                  "There is no client authentication. Try adding an appropriate authentication filter.");
                          }
           
                          Authentication client = (Authentication) principal;
                          if (!client.isAuthenticated()) {
                                  throw new InsufficientAuthenticationException("The client is not authenticated.");
                          }
                          HashMap<String, String> request = new HashMap<String, String>(parameters);
                          String clientId = client.getName();
                          request.put("client_id", clientId);
          So in here when client_id is pulled from the request, it's not the client id. It's the Basic Auth ID.

          Any suggestions here expect DOOM or RTFM? Really stumped on this one. Log out the Params see if something is getting wack? Like I was saying, I'm sure I missed something while trying to migrate from M3 to RC3. Splitting the packages up and then M5 being a train wreck just sucked.

          Thanks for any help Dave.

          Comment


          • #6
            I don't think we are on the same page here regarding the operation of the /token endpoint. It has to be authenticated, so you must send the client secret if there is one. Per the spec, your client should send its id (and secret) as part of the Basic authorization header. If you don't like that (I think it wasn't the default in older milestones) you can add a ClientCredentialsTokenEndpointFilter to the endpoint instead of the <http-basic/> filter.

            Comment


            • #7
              Dave thanks for the suggestion. Here is a stack trace to help (I hope)

              Code:
                <security:http pattern="/access_token" authentication-manager-ref="authenticationManager">
              	<security:intercept-url pattern="/access_token" access="ROLE_USER" />
              	<security:custom-filter ref="customClientCredentialsTokenEndpointFilter" before="BASIC_AUTH_FILTER" />
              	    <security:form-login authentication-success-handler-ref="customSavedRequestAwareAuthenticationHandler" authentication-failure-handler-ref="customSimpleUrlAuthenticationFailureHandler" login-page="/index.jsp" login-processing-url="/login.do" />
                  <security:logout logout-success-url="/index.jsp" logout-url="/logout.do"/>
              </security:http>
              DEBUG [http-8080-5] (CustomSavedRequestAwareAuthenticationSuccessHandl er.java:79) - login = [Login]
              DEBUG [http-8080-5] (CustomSavedRequestAwareAuthenticationSuccessHandl er.java:79) - j_username = [jasonmcdonald]
              ..
              ...
              DEBUG [http-8080-5] (FilterChainProxy.java:337) - /access_token?response_type=code&client_id=FOO&gran t_type=authorization-code&redirect_uri=http://localhost:8080/FOO/setup-oauth.jsp&code=ROLE_USER at position 4 of 10 in additional filter chain; firing Filter: 'CustomClientCredentialsTokenEndpointFilter'
              DEBUG [http-8080-5] (CustomClientCredentialsTokenEndpointFilter.java:3 6) - JASON:requiresAuthentication:response_type = [code]
              DEBUG [http-8080-5] (CustomClientCredentialsTokenEndpointFilter.java:3 6) - JASON:requiresAuthentication:grant_type = [authorization-code]
              DEBUG [http-8080-5] (CustomClientCredentialsTokenEndpointFilter.java:3 6) - JASON:requiresAuthentication:redirect_uri = [http://localhost:8080/FOO/setup-oauth.jsp]
              DEBUG [http-8080-5] (CustomClientCredentialsTokenEndpointFilter.java:3 6) - JASON:requiresAuthentication:client_id = [FOO]
              DEBUG [http-8080-5] (CustomClientCredentialsTokenEndpointFilter.java:3 6) - JASON:requiresAuthentication:code = [ROLE_USER]
              ..
              .
              Obviously at this point the client_id = FOO.
              .
              DEBUG [http-8080-5] (AbstractSecurityInterceptor.java:215) - Authorization successful
              DEBUG [http-8080-5] (AbstractSecurityInterceptor.java:227) - RunAsManager did not change Authentication object
              DEBUG [http-8080-5] (FilterChainProxy.java:323) - /access_token?response_type=code&client_id=FOO&gran t_type=authorization-code&redirect_uri=http://localhost:8080/FOO/setup-oauth.jsp&code=ROLE_USER reached end of additional filter chain; proceeding with original chain
              DEBUG [http-8080-5] (DispatcherServlet.java:819) - DispatcherServlet with name 'oauth' processing GET request for [/oauth/access_token]
              DEBUG [http-8080-5] (AbstractHandlerMethodMapping.java:209) - Looking up handler method for path /access_token
              DEBUG [http-8080-5] (AbstractHandlerMethodMapping.java:219) - Did not find handler method for [/access_token]
              DEBUG [http-8080-5] (AbstractHandlerMethodMapping.java:209) - Looking up handler method for path /access_token
              DEBUG [http-8080-5] (AbstractHandlerMethodMapping.java:216) - Returning handler method [public org.springframework.http.ResponseEntity<org.spring framework.security.oauth2.common.OAuth2AccessToken > org.springframework.security.oauth2.provider.endpo int.TokenEndpoint.getAccessToken(java.security.Pri ncipal,java.lang.String,java.util.Map<java.lang.St ring, java.lang.String>)]
              DEBUG [http-8080-5] (AbstractBeanFactory.java:245) - Returning cached instance of singleton bean 'oauth2TokenEndpoint'
              DEBUG [http-8080-5] (DispatcherServlet.java:902) - Last-Modified value for [/oauth/access_token] is: -1
              DEBUG [http-8080-5] (WebContentInterceptor.java:145) - Looking up cache seconds for [/access_token]
              DEBUG [http-8080-5] (WebContentInterceptor.java:157) - Applying default cache seconds to [/access_token]
              DEBUG [http-8080-5] (JdbcClientDetailsService.java:57) - Looking up ClientID:jasonmcdonald

              After this point you can see I am authorized and the client_id is no longer FOO, but it is my Login name from the BASIC Auth.

              Anything glaring? Maybe something else I can try or dig up to help you help me figure out WTH I'm doing wrong.

              Thanks for the speedy response.

              Jason

              Comment


              • #8
                Dave here is a question:

                Code:
                     @RequestMapping
                        public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal,
                                        @RequestParam("grant_type") String grantType, @RequestParam Map<String, String> parameters) {
                 
                                if (!(principal instanceof Authentication)) {
                                        throw new InsufficientAuthenticationException(
                                                        "There is no client authentication. Try adding an appropriate authentication filter.");
                                }
                 
                                Authentication client = (Authentication) principal;
                                if (!client.isAuthenticated()) {
                                        throw new InsufficientAuthenticationException("The client is not authenticated.");
                                }
                                HashMap<String, String> request = new HashMap<String, String>(parameters);
                                String clientId = client.getName();
                                request.put("client_id", clientId);
                
                               getAuthorizationRequestManager().validateParameters(parameters,
                                                getClientDetailsService().loadClientByClientId(clientId));
                Shouldn't the clientId be pulled from the RequestParam and not the Principal when you are trying to loadClientByClientId?

                Comment


                • #9
                  In a word, no.

                  I don't think you understood my last point. Your /access_token endpoint should not be protected by a <form-login/>. It looks like you have a custom filter in there, so that's a start, and I assume it's a version of ClientCredentialsTokenEndpointFilter, but you didn't say exactly. If it is then you would need to send the client_secret as a request parameter (and note that it is bad practice to do this as a query parameter because your logs will contain secrets in plain text).

                  Additionally, your request should not contain a response_type, and it looks to me like the authorization code (ROLE_USER) must be wrong.

                  Comment


                  • #10
                    If I want to just bypass the Basic Auth (for now), all I would have to do is:

                    Using my CustomClientCredentialsTokenEndpointFilter.java which extends ClientCredentialsTokenEndpointFilter.java

                    implement attemptAuthentication
                    Code:
                    UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(clientId,
                                                    clientSecret);
                     
                                    return this.getAuthenticationManager().authenticate(authRequest);
                    and just fake out the client secret for now.

                    Correct? I think I get what you are saying now, and yeah this was not mandated with M3.

                    Thanks for the patiences.

                    Comment


                    • #11
                      Good, I think we are starting to understand each other. You are of course free to implement authentication in any way you like. As long as the authenticated principal name is the client id the token endpoint is going to work.

                      Comment


                      • #12
                        Exactly! haha. Ok, thanks for the help.

                        Comment

                        Working...
                        X