Announcement Announcement Module
Collapse
No announcement yet.
Defining response formats, response fields and HTTP methods? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Defining response formats, response fields and HTTP methods?

    Hi, I am evaluating the brand new 1.0.0.RELEASE without previous experience from the module. Couple of questions:

    I registered Auhtorization Server as one web application and Resource Server as another one. Both are deployed to the same container. Configuration is in the bottom of this post.

    1) How to force library to use only JSON? Out of box, the responses are in the format set in Accept-header. For application/json I get JSON but for application/xml I get XML (error messages, at least). The OAuth spec doesn't even define/allow XML format.

    For example, requesting for oauth/token without Authorization-header I get:

    Code:
    <oauth><error_description>An Authentication object was not found in the SecurityContext</error_description><error>unauthorized</error></oauth>
    When I want:

    Code:
    {"error": "unauthorized", "error_description": "An Authentication object was not found in the SecurityContext" }
    Also, if you invoke oauth/token with invalid credentials in Authorization-header from browser, I get browser asking for proper username/password. If you click cancel, the response is a HTML page.

    I haven't enabled the OAuth2AccessDeniedHandler.

    2) Successful access token query always returns "scope" field in JSON. "scope" field is specified as "OPTIONAL, if identical to the scope requested by the client, otherwise REQUIRED". Looks like the implementation always returns scope (it's ok) but how can I configure to omit that? By specifying a custom TokenGranter?

    3) How can I add extra fields to the error response? By specifying a custom TokenGranter?

    4) Token endpoint (oauth/token) answers succesfully to GET, POST, DELETE and PUT. I didn't test Authroize endpoint. OAuth2 spec (v31) defines:

    "The authorization server MUST support the use of the HTTP "GET" method [RFC2616] for the authorization endpoint, and MAY support the use of the "POST" method as well."

    "The client MUST use the HTTP "POST" method when making access token requests."

    How can I limit the endpoints to only respond to GET/POST (authorize) and just GET (token)? Do I need to deny all PUT/DELETE/(GET) via http intercept entries in XML? If so, why doesn't library automatically block the invalid

    X) Core confs for refrence:

    Authorization server:

    Code:
        <!-- Secure Token Endpoint behind HTTP Basic -->
        <sec:http
                pattern="/oauth/token"
                create-session="stateless"
                authentication-manager-ref="clientAuthenticationManager">
            <sec:intercept-url pattern="/oauth/token" access="IS_AUTHENTICATED_FULLY" />
            <sec:anonymous enabled="false" />
            <!-- Enable HTTP Basic authentication -->
            <sec:http-basic />
            <sec:access-denied-handler/>
        </sec:http>
    
        <!-- Authentication manager for Clients -->
        <sec:authentication-manager id="clientAuthenticationManager">
            <sec:authentication-provider user-service-ref="clientDetailsUserService" />
        </sec:authentication-manager>
    
        <!-- Authentication manager for Users -->
        <sec:authentication-manager alias="authenticationManager">
            <sec:authentication-provider>
                <sec:user-service>
                    <sec:user name="marissa" password="koala" authorities="ROLE_USER" />
                    <sec:user name="paul" password="emu" authorities="ROLE_USER" />
                </sec:user-service>
            </sec:authentication-provider>
        </sec:authentication-manager>
    
        <!-- Base configuration -->
        <oauth:authorization-server
                client-details-service-ref="clientDetails"
                token-services-ref="tokenServices"
                token-endpoint-url="/oauth/token">
            <oauth:authorization-code disabled="true" />
            <oauth:implicit disabled="true" />
            <oauth:refresh-token />
            <oauth:client-credentials />
            <oauth:password />
        </oauth:authorization-server>
    
        <!-- Client details service -->
        <bean id="clientDetailsUserService" class="org.springframework.security.oauth2.provider.client.ClientDetailsUserDetailsService">
            <constructor-arg ref="clientDetails" />
        </bean>
    
        <!-- Token services -->
        <jee:jndi-lookup id="dataSource" jndi-name="jdbc/oneidDs"/>
        <bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.JdbcTokenStore">
            <constructor-arg ref="dataSource"/>
        </bean>
        <bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
            <property name="tokenStore" ref="tokenStore" />
            <property name="supportRefreshToken" value="true" />
            <property name="clientDetailsService" ref="clientDetails"/>
        </bean>
    
        <oauth:client-details-service id="clientDetails">
            <oauth:client client-id="client" secret="client" authorized-grant-types="client_credentials"
                authorities="ROLE_USER" scope="read,write" access-token-validity="600"/>
        </oauth:client-details-service>
    Resource Server:

    Code:
        <!-- Defines OAuth2 Resource Server -->
        <oauth:resource-server id="resourceServerFilter" resource-id="resourceServer" token-services-ref="tokenServices" />
    
        <sec:http
                pattern="/**"
                create-session="never"
                entry-point-ref="oauthAuthenticationEntryPoint"
                use-expressions="true">
            <sec:anonymous enabled="false" />
            <sec:intercept-url pattern="/formula/teams/**"
                    access="#oauth2.clientHasRole('ROLE_USER') and #oauth2.hasScope('read')" />
            <sec:custom-filter ref="resourceServerFilter" before="PRE_AUTH_FILTER" />
            <sec:expression-handler ref="oauthWebExpressionHandler" />
        </sec:http>
    
        <!-- Adds WWW-Authenticate header to response suggesting location of where to authenticate -->
        <bean id="oauthAuthenticationEntryPoint" class="org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint">
            <property name="realmName" value="sparklr2" />
        </bean>
    
        <!-- Token services -->
        <jee:jndi-lookup id="dataSource" jndi-name="jdbc/oneidDs"/>
        <bean id="tokenStore" class="org.springframework.security.oauth2.provider.token.JdbcTokenStore">
            <constructor-arg ref="dataSource"/>
        </bean>
        <bean id="tokenServices" class="org.springframework.security.oauth2.provider.token.DefaultTokenServices">
            <property name="tokenStore" ref="tokenStore" />
        </bean>
    
        <!-- Dummy authentication manager. Not really needed/used for Resource Server, but required by Spring Security -->
        <sec:authentication-manager />

  • #2
    Originally posted by tuukka.mustonen View Post
    1) How to force library to use only JSON? Out of box, the responses are in the format set in Accept-header. For application/json I get JSON but for application/xml I get XML (error messages, at least). The OAuth spec doesn't even define/allow XML format.
    The spec used to mention alternative response formats. I guess they dropped it. Anyway XML output is standard with Spring MVC so we aim to meet normal Spring REST client assumptions. If you want to switch it off you have to remove the XML HttpMessageConverters, from the <mvc:message-converters/> or define your own HandlerAdapter (see standard references and seek help on Spring MVC generally for that) and also from the OAuth2 specific error renderers (the access denied handler and auth entry point that you aren't using but maybe want to later).

    Also, if you invoke oauth/token with invalid credentials in Authorization-header from browser, I get browser asking for proper username/password. If you click cancel, the response is a HTML page.
    That's probably normal Spring Security behaviour for HTTP basic if you don't provide any custom handlers. The OAuth2AuthenticationEntryPoint is provided as a basic handler for this use case essentially. Browser clients won't get much in the way of UX by default, but you can change it if you don't like it.

    2) Successful access token query always returns "scope" field in JSON. "scope" field is specified as "OPTIONAL, if identical to the scope requested by the client, otherwise REQUIRED". Looks like the implementation always returns scope (it's ok) but how can I configure to omit that? By specifying a custom TokenGranter?
    The scope in the token response is only for information for the client, so I can't really understand why you wouldn't include it. But if you don't want to it would be the renderer that I would change, not the token itself (otherwise resource servers won't know what scope has been granted). The renderer in this case is the HttpMessageConverter.

    3) How can I add extra fields to the error response? By specifying a custom TokenGranter?
    Again, I would change the renderer not the generator if I were you. But if you need to change the generator it would be the *Endpoint not the TokenGranter (which doesn't generate error responses only exceptions).

    4) Token endpoint (oauth/token) answers succesfully to GET, POST, DELETE and PUT. I didn't test Authroize endpoint.
    I don't really want to restrict HTTP method access explicitly in Spring OAuth when it is trivial to do it in Spring Security. If I were you I would add specific <intercept-url/> declarations in your security configuration.
    Last edited by Dave Syer; Nov 20th, 2012, 07:59 AM. Reason: missing response in original

    Comment


    • #3
      Originally posted by Dave Syer View Post
      The spec used to mention alternative response formats. I guess they dropped it. Anyway XML output is standard with Spring MVC so we aim to meet normal Spring REST client assumptions. If you want to switch it off you have to remove the XML HttpMessageConverters, from the <mvc:message-converters/> or define your own HandlerAdapter (see standard references and seek help on Spring MVC generally for that) and also from the OAuth2 specific error renderers (the access denied handler and auth entry point that you aren't using but maybe want to later).
      I found AbstractJaxbMessageConverter and its' children JaxbOAuth2AccessTokenMessageConverter and JaxbOAuth2ExceptionMessageConverter. I tried resetting the converters by something like:

      Code:
          <mvc:annotation-driven>
              <mvc:message-converters register-defaults="false">
                  <bean class="org.springframework.http.converter.StringHttpMessageConverter" />
              </mvc:message-converters>
          </mvc:annotation-driven>
      But no matter what I put there, it doesn't seem to have effect. Apparently it's not possible to define an empty list of converters. Tips?

      Originally posted by Dave Syer View Post
      That's probably normal Spring Security behaviour for HTTP basic if you don't provide any custom handlers. The OAuth2AuthenticationEntryPoint is provided as a basic handler for this use case essentially. Browser clients won't get much in the way of UX by default, but you can change it if you don't like it.
      I think I was mixing couple of separate items here. First, Spring responds with WWW-Authenticate: Basic header by default and it's browser behavior to popup for username/password when that happens. If I want to remove the browser popup I should remove the WWW-Authenticate header.

      Then there is the response type. In the original response (with WWW-Authenticate: Basic) there is also a HTML body. To remove that I need to do something like what's described in http://stackoverflow.com/questions/4...pring-security

      What is responded after passing invalid credentials as Authorization header actually comes from OAuth module (OAuth2AccessDeniedHandler?).

      Isn't OAuth2AuthenticationEntryPoint supposed to be specified only for resources in Resource Server (because it adds WWW-Authenticate: Bearer) and not for Token endpoint in Authorization Server?

      Originally posted by Dave Syer View Post
      The scope in the token response is only for information for the client, so I can't really understand why you wouldn't include it. But if you don't want to it would be the renderer that I would change, not the token itself (otherwise resource servers won't know what scope has been granted). The renderer in this case is the HttpMessageConverter.
      Originally posted by Dave Syer View Post
      Again, I would change the renderer not the generator if I were you. But if you need to change the generator it would be the *Endpoint not the TokenGranter (which doesn't generate error responses only exceptions).
      As you say, we probably don't want to remove the scope so it's not that important. Anyway, I'm a bit lost here - would you elaborate on how to add custom fields as HttpMessageConverters seem to just convert entities into one form from another. What do you mean by "you need to change the generator it would be the *Endpoint not the TokenGranter" ?

      Then there is OAuth2AccessTokenSerializer that actually serializes the entity into JSON - shouldn't I need to override that?

      Originally posted by Dave Syer View Post
      I don't really want to restrict HTTP method access explicitly in Spring OAuth when it is trivial to do it in Spring Security. If I were you I would add specific <intercept-url/> declarations in your security configuration.
      Understandable. Might be sensible to add a note in documentation about that, though, as oauth2 specification is strict about HTTP methods.
      Last edited by tuukka.mustonen; Nov 20th, 2012, 11:07 AM.

      Comment


      • #4
        Originally posted by tuukka.mustonen View Post
        But no matter what I put there, it doesn't seem to have effect. Apparently it's not possible to define an empty list of converters. Tips?
        I would ask that question on the MVC forum or on stackoverflow. The converters in <mvc:/> elements only apply to the results of the endpoint executions though - if you are looking at an error response that doesn't come from an endpoint directly (e.g. it comes from a Spring Security filter), then the mechanism is different.

        If I want to remove the browser popup I should remove the WWW-Authenticate header.
        Why would you want to remove the header? Your clients should be expecting it.

        What is responded after passing invalid credentials as Authorization header actually comes from OAuth module (OAuth2AccessDeniedHandler?).
        Only if you configure an OAuth2AccessDeniedHandler specifically in your filter chain. It doesn't get used in the configuration you provided, for instance. Invalid credentials is not an access denied exception though, so it wouldn't be that handler that got the exception anyway. It should be an AuthenticationEntryPoint.

        Isn't OAuth2AuthenticationEntryPoint supposed to be specified only for resources in Resource Server (because it adds WWW-Authenticate: Bearer) and not for Token endpoint in Authorization Server?
        Correct. I guess that's not accurately reflected in the sample apps. It might be better to use a normal BasicAuthenticationEntryPoint.

        Anyway, would you elaborate on how to add custom fields as HttpMessageConverters seem to just convert entities into one form from another. Then there is OAuth2AccessTokenSerializer that actually serializes the entity into JSON - shouldn't I need to override that?
        I thought you said you wanted to add extra fields to the error responses (i.e. not an access token)? You can see the error responses from the endpoints being generated in @ExceptionHandler methods. You might be able to override those I suppose, but I'm not really sure what you want to add so I don't know if you can do it in the renderer or not (or maybe just a WebResponseExceptionTranslator injected into the endpoint). There are also error responses that are generated by Spring Security filters (e.g. access denied), and for those you need to plug in handlers in teh standard Spring Security places.

        If you do want to add extra fields to an access token then the default implementation has an additionalInformation map for precisely that purpose (e.g. through a custom TokenEnhancer). There are also opportunities to modify the token and authentication outputs in UserApprovalHandler and AuthorizationRequestManager.

        Understandable. Might be sensible to add a note in documentation about that, though, as oauth2 specification is strict about HTTP methods.
        The docs are in the github wiki and it is open for editing. Feel free to add your contribution. Please fill out the contributor's agreement first (link in README).

        Comment


        • #5
          Originally posted by Dave Syer View Post
          I would ask that question on the MVC forum or on stackoverflow. The converters in <mvc:/> elements only apply to the results of the endpoint executions though - if you are looking at an error response that doesn't come from an endpoint directly (e.g. it comes from a Spring Security filter), then the mechanism is different.
          Ok, I need to ask around then. Couldn't find anything on that in searches so far.

          Originally posted by Dave Syer View Post
          Why would you want to remove the header? Your clients should be expecting it.
          It's just to make behavior in web browser similar to as requests through curl etc. We want to experience immediate errors in browser as would client code requesting the endpoint. It may confuse people if they try the URL in browser and it asks for things. It's just a minor annoyance.

          Originally posted by Dave Syer View Post
          Only if you configure an OAuth2AccessDeniedHandler specifically in your filter chain. It doesn't get used in the configuration you provided, for instance. Invalid credentials is not an access denied exception though, so it wouldn't be that handler that got the exception anyway. It should be an AuthenticationEntryPoint.
          Originally posted by Dave Syer View Post
          Correct. I guess that's not accurately reflected in the sample apps. It might be better to use a normal BasicAuthenticationEntryPoint.
          Right, so I would need to override BasicAuthenticationEntryPoint if I wanted to remove that. Ok.

          Originally posted by Dave Syer View Post
          I thought you said you wanted to add extra fields to the error responses (i.e. not an access token)? You can see the error responses from the endpoints being generated in @ExceptionHandler methods. You might be able to override those I suppose, but I'm not really sure what you want to add so I don't know if you can do it in the renderer or not (or maybe just a WebResponseExceptionTranslator injected into the endpoint). There are also error responses that are generated by Spring Security filters (e.g. access denied), and for those you need to plug in handlers in teh standard Spring Security places.
          As it's valid for oauth2 spec to include extra fields to responses, I just want to know how to do it. I have no real use case for that at the moment.

          Luckily, I don't see need for customizing error responses as the lib already comes with a decent exception hierarchy/mapping. But yeah, good pointers.

          Originally posted by Dave Syer View Post
          If you do want to add extra fields to an access token then the default implementation has an additionalInformation map for precisely that purpose (e.g. through a custom TokenEnhancer). There are also opportunities to modify the token and authentication outputs in UserApprovalHandler and AuthorizationRequestManager.
          Ok, found OAuth2AccessToken#getAdditionalInformation() and indeed that looks suitable for adding extra fields to response. Looks convenient.

          Originally posted by Dave Syer View Post
          The docs are in the github wiki and it is open for editing. Feel free to add your contribution. Please fill out the contributor's agreement first (link in README).
          Sure thing, let's see if I can get a good grasp on the lib first

          Comment


          • #6
            Also, to ensure I got it right:

            1. If you omit credentials (no Authorization header) OAuth2 response should be JSON message with error of "invalid_request" or "invalid_client" (don't know which one). Now I get Spring Security's standard HTML page reporting 401 "An Authentication object was not found in the SecurityContext". I can fix this by overriding BasicAuthenticationEntryPoint.

            2. If you provide invalid credentials (as Authorization header) OAuth2 response should be JSON message with error of "unauthorized_client". Now I get Spring Security's standard HTML page reporting 401 "No client with requested id: asfasf". I can fix this by overriding BasicAuthenticationEntryPoint.

            3. If you authenticate successfully (correct Authorize header) but omit the required grant_type parameter, OAuth2 response should be JSON message with error of "invalid_request". Now I get is 400 "The request sent by the client was syntactically incorrect". I can fix this by overriding TokenEndpoint (and add ExceptionHandler for this exception, whatever it might be)? How should I register it in XML then?
            Last edited by tuukka.mustonen; Nov 21st, 2012, 07:41 AM.

            Comment


            • #7
              Are these all scenarios on the TokenEndpoint? If so then "yes" to (1) and (2) but "override" is probably the wrong word - you need to supply an authentication entry point instance, it doesn't have to override (in the Java language sense) BasicAuthenticationEntryPoint. Actually I think the framework should provide one, and OAuth2AuthenticationEntryPoint probably was supposed to be usable, but as your pointed out it sends the wrong prefix in the WWW-Authenticate header. Please open a JIRA ticket if you want to track the progress on that.

              There is an integration test for (3) which is passing so I assume it is already working (i.e. you should not need to change the TokenEndpoint). The response you describe might be expected if the client didn't send the correct Accept header, I suppose. What exactly did it send?

              Comment


              • #8
                Originally posted by Dave Syer View Post
                Are these all scenarios on the TokenEndpoint? If so then "yes" to (1) and (2) but "override" is probably the wrong word - you need to supply an authentication entry point instance, it doesn't have to override (in the Java language sense) BasicAuthenticationEntryPoint. Actually I think the framework should provide one, and OAuth2AuthenticationEntryPoint probably was supposed to be usable, but as your pointed out it sends the wrong prefix in the WWW-Authenticate header. Please open a JIRA ticket if you want to track the progress on that.
                Yeah these occur in TokenEndpoint. Guess I was bad with terminology because I wasn't really sure if providing new EntryPoint is the answer. But I had a look at AbstractOAuth2SecurityExceptionHandler and OAuth2AuthenticationEntryPoint and I guess I should do something like that.

                Originally posted by Dave Syer View Post
                There is an integration test for (3) which is passing so I assume it is already working (i.e. you should not need to change the TokenEndpoint). The response you describe might be expected if the client didn't send the correct Accept header, I suppose. What exactly did it send?
                It simply doesn't matter what I put in Accept header. No effect.

                Comment


                • #9
                  You are correct (the integration test was for something else). If there is no grant_type (as opposed to the wrong grant_type) then Spring MVC sends a 400 with no content type, so your servlet container will choose to render it in some random way. Raise a JIRA ticket if you want to track progress and submit a pull request if you like (but it's trivial, so I'll probably do it soon anyway).

                  If you care so much about this that you must work around it before 1.0.1 is out you can replace the token endpoint by providing a bean definition with id="oauth2TokenEndpoint" (see user guide on bean overriding).

                  Comment


                  • #10
                    JIRA ticket: https://jira.springsource.org/browse/SECOAUTH-359

                    Comment


                    • #11
                      Ok I also opened https://jira.springsource.org/browse/SECOAUTH-360

                      Now that I look at it, OAuth2 spec requires parameters to be given as "application/x-www-form-urlencoded" in request body. It should ignore URL parameters. Current implementation happily accepts URL parameters, maybe it even requires them (I didn't test yet in the proper way).

                      How can I configure module to ignore URL parameters?

                      Comment


                      • #12
                        Originally posted by tuukka.mustonen View Post
                        Now that I look at it, OAuth2 spec requires parameters to be given as "application/x-www-form-urlencoded" in request body. It should ignore URL parameters. Current implementation happily accepts URL parameters, maybe it even requires them (I didn't test yet in the proper way).
                        Just tested it, parameters in body are not recognized. Something needs to be done?

                        Comment


                        • #13
                          Originally posted by tuukka.mustonen View Post
                          Just tested it, parameters in body are not recognized. Something needs to be done?
                          Uh, I did declare the body but just forgot to add the body in the request So passing parameters in body works.

                          But I want to remove possibility to pass parameters in URL (as GET params, for example). How could I do that?

                          Comment


                          • #14
                            I suppse you could add a filter that rejected anything in which request.getQueryString was non-empty. It really doesn't seem all that important to me and you only annoy clients with incomprehensible errors when they don't think they've done anything bviously wrong. Your choice.

                            Comment


                            • #15
                              Maybe. But if you are after pure OAuth2-compliant implementation, you need to ignore URL parameters. The OAuth2 v31 spec says it:

                              Code:
                              4.1.3. Access Token Request
                              
                                 The client makes a request to the token endpoint by sending the
                                 following parameters using the "application/x-www-form-urlencoded"
                                 format per Appendix B with a character encoding of UTF-8 in the HTTP
                                 request entity-body:
                              Source: http://tools.ietf.org/html/draft-ietf-oauth-v2-31

                              If you don't strive for 100% compliancy, then of course it's more convenient to accept also URL parametrs. It's all about what the code is after I guess.

                              Btw. just saw that OAuth 2.0 spec has been turned into RFC lately: http://dickhardt.org/2012/10/oauth-2-0/

                              Comment

                              Working...
                              X