Return-Path: X-Original-To: apmail-cxf-commits-archive@www.apache.org Delivered-To: apmail-cxf-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id CCFD69EF0 for ; Thu, 12 Apr 2012 21:49:08 +0000 (UTC) Received: (qmail 53459 invoked by uid 500); 12 Apr 2012 21:49:08 -0000 Delivered-To: apmail-cxf-commits-archive@cxf.apache.org Received: (qmail 53404 invoked by uid 500); 12 Apr 2012 21:49:08 -0000 Mailing-List: contact commits-help@cxf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cxf.apache.org Delivered-To: mailing list commits@cxf.apache.org Received: (qmail 53397 invoked by uid 99); 12 Apr 2012 21:49:08 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 12 Apr 2012 21:49:08 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 12 Apr 2012 21:49:05 +0000 Received: from eris.apache.org (localhost [127.0.0.1]) by eris.apache.org (Postfix) with ESMTP id 29F5723889E0 for ; Thu, 12 Apr 2012 21:48:45 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r812599 - in /websites/production/cxf/content: cache/docs.pageCache docs/jax-rs-oauth2.html Date: Thu, 12 Apr 2012 21:48:45 -0000 To: commits@cxf.apache.org From: buildbot@apache.org X-Mailer: svnmailer-1.0.8-patched Message-Id: <20120412214845.29F5723889E0@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: buildbot Date: Thu Apr 12 21:48:44 2012 New Revision: 812599 Log: Production update by buildbot for cxf Modified: websites/production/cxf/content/cache/docs.pageCache websites/production/cxf/content/docs/jax-rs-oauth2.html Modified: websites/production/cxf/content/cache/docs.pageCache ============================================================================== Binary files - no diff available. Modified: websites/production/cxf/content/docs/jax-rs-oauth2.html ============================================================================== --- websites/production/cxf/content/docs/jax-rs-oauth2.html (original) +++ websites/production/cxf/content/docs/jax-rs-oauth2.html Thu Apr 12 21:48:44 2012 @@ -125,11 +125,11 @@ Apache CXF -- JAX-RS OAuth2 +

Introduction

-

CXF 2.6.0 provides an initial implementation of OAuth 2.0. See also the [JAXRS OAuth] page for the information about OAuth 1.0.

+

CXF 2.6.0 provides an initial implementation of OAuth 2.0. See also the JAX-RS OAuth page for the information about OAuth 1.0.

Authorization Code, Implicit and Client Credentials grants are currently supported with the new grant handlers to be added later.
Custom grant handlers can be registered.

@@ -439,50 +439,40 @@ Most likely, you'd want to deploy Access

Protecting resources with OAuth filters

-

OAuthRequestFilter request handler can be used to protect the resource server when processing the requests from the third-party consumers. Add it as a jaxrs:provider to the endpoint which deals with the consumers requesting the resources.

+

OAuthRequestFilter request handler can be used to protect the resource server when processing the requests from the third-party clients. Add it as a jaxrs:provider to the endpoint which deals with the consumers requesting the resources.

When checking a request like this:

-Address: http://localhost:8080/services/user/calendar
+Address: http://localhost:8080/services/thirdPartyAccess/calendar
 Http-Method: GET
-Headers: {
-Accept=[application/XML], 
-Authorization=[OAuth oauth_signature_method="HMAC-SHA1", 
-                     oauth_consumer_key="123456789", 
-                     oauth_token="abc15aca-2073-4bde-b1be-1a02dc7ccafe", 
-                     oauth_version="1.0", 
-                     oauth_signature="dU%2BhXPNFfFpX2sC74IOxzTjdVrY%3D"]
+Headers: 
+{
+  Authorization=[Bearer 5b5c8e677413277c4bb8b740d522b378], 
+  Accept=[application/xml]
 }
 

the filter will do the following:

-

1. It will validate the signature and will get Client and AccessToken from OAuthDataProvider.

+

1. Retrieve a ServerAccessToken by delegating to a matching registered AccessTokenValidator. AccessTokenValidator is expected to check the validity of the incoming token parameters and possibly delegate to OAuthDataProvider to find the token representation - this is what the filter will default to if no matching AccessTokenValidator is found and the Authorization scheme is 'Bearer'.

-

2. It will check if AccessToken have a "uris" property set and if yes then it will validate the current request URI against it.

+

2. Check the token has not expired

-

3. If AccessToken has a list of OAuthPermissions. For every permission it will:

+

3. AccessToken may have a list of OAuthPermissions. For every permission it will:

  • If it has a uri property set then the current request URI will be checked against it
  • If it has an httpVerb property set then the current HTTP verb will be checked against it
-

4. Finally, it will create a CXF SecurityContext using this list of OAuthPermissions and the Client loginName property.

- -

This loginName property is something that can be optionally associated with the new Client during the registration - if it is not set then the filter will use a Client "applicationName" property instead. The application code checking the user Principal will see the chosen value. Additionally every OAuthPermission may have a list of application-specific roles such as "consumer", etc, which will be added to SecurityContext and will be checked during SecurityContext.isUserInRole(roleName) calls.

- -

If a "useUserSubject" property is set on the filter then a UserSubject associated with the access token will be used to populate the SecurityContext.

+

4. Finally, it will create a CXF SecurityContext using this list of OAuthPermissions, the UserSubject representing the client or the end user who authorized the grant used to obtain this token.

-

This SecurityContext will not necessarily be important for some of OAuth applications. Most of the security checks will be done by OAuth filters and security filters protecting the main application path the end users themselves use. Only if you would like to share the same JAX-RS resource code and access URIs between end users and consumers then it can become handy. More on it below.

- -

Note that OAuthServletFilter can be deployed instead. It will need the OAuthDataProvider full class name referenced as an "oauth.data.provider-class" servlet context parameter.

+

This SecurityContext will not necessarily be important for some of OAuth2 applications. Most of the security checks will be done by OAuth2 filters and security filters protecting the main application path the end users themselves use. Only if you would like to share the same JAX-RS resource code and access URIs between end users and consumers then it can become handy. More on it below.

How to get the user login name

-

When one writes a custom server application which needs to participate in 3-leg OAuth flows, the major question which needs to be addressed is
-how one can access a user login name that was used during the end-user authorizing the third-party client. This username will help to uniquely identify the resources that the 3rd party client is now attempting to access.
-The following code shows one way of how this can be done starting from CXF 2.5.1:

+

When one writes a custom server application which needs to participate in OAuth2 flows, the major question which needs to be addressed is
+how one can access a user login name that was used during the end-user authorizing the third-party client. This username will help to uniquely identify the resources that the 3rd party client is now attempting to access. The following code shows one way of how this can be done:

 
@@ -512,16 +502,11 @@ The following code shows one way of how 
 
 

The above shows a fragment of the JAX-RS service managing the access to user resources from authorized 3rd-party clients (see the Design Considerations section for more information).

-

The injected MessageContext provides an access to OAuthContext which has been set by OAuth filters described in the previous section. OAuthContext will act as a container of the information which can be useful to the custom application code which do not need to deal with the OAuth internals which will likely change between OAuth 1.0 and OAuth 2.0. At the moment OAuthContext provides an access to UserSubject which is created by CXF AuthorizationService at the moment of the end user authorizing the third-party client and captures the end user's login name (and roles which will be available if CXF JAASLoginInterceptor is used to authenticate end users) and associates it with the current RequestToken. It will be a responsibility of custom OAuthDataProviders to make sure this UserSubject bean is copied across to a corresponding AccessToken. OAuthContext also references the list of the permissions which have been validated againt the current client request.

- -

Additionally you may get OAuth filters to set up a SecurityContext which will use the information available in UserSubject, in other words, get the 3rd-party client impersonating the end user (which authorized this client in the first place) for the duration of the current request. Set a jaxrs contextual "org.apache.cxf.rs.security.oauth.use_user_subject" property to 'true'
-for this to happen.

+

The injected MessageContext provides an access to OAuthContext which has been set by OAuth2 filters described in the previous section. OAuthContext will act as a container of the information which can be useful to the custom application code which do not need to deal with the OAuth2 internals.

Client-side support

-

When developing a third party application which needs to participate in OAuth flows one has to write the code that will redirect users to OAuth AuthorizationRequestService, interact with RequestTokenService and AccessTokenService in order to get request and access tokens as well as correctly build Authorization OAuth headers when accessing the end users' resources. JAX-RS makes it straightforward to support the redirection, while OAuthClientUtils class makes it possible to encapsulate most of the complexity away from the client application code.

- -

OAuthClientUtils has utility methods for getting request and access tokens, the consumer is expected to provide a properly initialized WebClient pointing to either RequestTokenService and AccessTokenService, Consumer bean containing the registration key and secret, a callback URI for requesting a request token and the request Token and the verifier for requesting the access token which is all quite straightforward. It also helps to create a proper URI for redirecting to AuthorizationRequestService. A correct Authorization header will also need to be used when accessing the user resources at the resource server and OAuthClientUtils will help with creating this header as well.

+

When developing a third party application which needs to participate in OAuth2 flows one has to write the code that will redirect users to OAuth2 AuthorizationCodeGrantService, interact with AccessTokenService in order to exchange code grants for access tokens as well as correctly build Authorization OAuth2 headers when accessing the end users' resources. JAX-RS makes it straightforward to support the redirection, while OAuthClientUtils class makes it possible to encapsulate most of the complexity away from the client application code.

For example, the following custom code can be used by the third-party application:

@@ -529,87 +514,42 @@ for this to happen.

public class OAuthClientManager { private WebClient accessTokenService; - private WebClient requestTokenService; private String authorizationServiceURI; - - // inject properties... - - public URI getAuthorizationServiceURI(String token) { - return OAuthClientUtils.getAuthorizationURI(authorizationServiceURI, token); - } - - public Token getRequestToken(URI callback) { - try { - return OAuthClientUtils.getRequestToken(requestTokenService, consumer, callback, null); - } catch (OAuthServiceException ex) { - return null; - } + private Consumer consumer; + + // inject properties, register the client application... + + public URI getAuthorizationServiceURI(ReservationRequest request, + URI redirectUri, + /* state */String reservationRequestKey) { + String scope = OAuthConstants.UPDATE_CALENDAR_SCOPE + request.getHour(); + return OAuthClientUtils.getAuthorizationURI(authorizationServiceURI, + consumer.getKey(), + redirectUri.toString(), + reservationRequestKey, + scope); } - - public Token getAccessToken(Token requestToken, String verifier) { + public ClientAccessToken getAccessToken(AuthorizationCodeGrant codeGrant) { try { - return OAuthClientUtils.getAccessToken(accessTokenService, consumer, requestToken, verifier); + return OAuthClientUtils.getAccessToken(accessTokenService, consumer, codeGrant); } catch (OAuthServiceException ex) { return null; } } - public String createAuthorizationHeader(Token token, String method, String requestURI) { - return OAuthClientUtils.createAuthorizationHeader(consumer, token, method, requestURI); + public String createAuthorizationHeader(ClientAccessToken token) { + return OAuthClientUtils.createAuthorizationHeader(consumer, token); } }
-

The reason such a simple wrapper can be introduced is to minimize the exposure to OAuth of the main application code to the bare minimum, this is why
+

The reason such a simple wrapper can be introduced is to minimize the exposure to OAuth2 of the main application code to the bare minimum, this is why
in this example OAuthServiceExceptions are caught, presumably logged and null values are returned which will indicate to the main code that the request failed. Obviously, OAuthClientUtils can be used directly as well.

-

2-leg OAuth Flow

- -

Please see this blog entry for a good overview of the OAuth 2-leg flow.
-Here are the variations of the 2-leg flow that CXF supports:

- -

Client requests PreAuthorized RequestToken

- -

In this variation the client accesses the request token as usual but skips the explicit authorization step.
-Instead, after the request token has been obtained, it requests the access token without providing an "oauth_verifier" parameter.
-For this to work RequestToken needs to be pre-authorized and have its UserSubject property set.

- -

This is the only distinction from the typical 3-OAuth flow. OAuth filters will validate the request as usual.

- -

Signature with Consumer Key and Secret

- -

In this mode the consumer key and secret pairs are used to create the "oauth_signature", Authorization header will look pretty much the same as it does during RequestToken requests.

- -

In this mode the Client is expected to reference a pre-authorized AccessToken. OAuth filters validate this token as usual.

- -

Only Consumer Key and Secret in Authorization header

- -
-
 
-Address: http://localhost:8080/services/user/calendar
-Http-Method: GET
-Headers: {
-Accept=[application/XML], 
-Authorization=[OAuth oauth_consumer_key="123456789",oauth_consumer_secret="987654321"] 
-}
-
-
- -

Alternatively, the consumer key and secret can be joined by a ":" character and encoded with the Base64 encoding:

- -
-
 
-Address: http://localhost:8080/services/user/calendar
-Http-Method: GET
-Headers: {
-Accept=[application/XML], 
-Authorization=[Basic base64EncodedValue] 
-}
-
-
+

OAuth2 without the Explicit Authorization

-

In this mode the Client is expected to reference a pre-authorized AccessToken. OAuth filters validate this token as usual.

+

Client Credentials is one of OAuth2 grants that does not require the explicit authorization and is currently supported by CXF.

OAuth Without a Browser

@@ -630,7 +570,7 @@ However, supporting other types of end u

In the former case the way the authentication is managed is completely up to the resource server application: basic authentication, two-way TLS, OpenId (more on it below), you name it.

-

In the latter case an OAuth filter must enforce that the 3rd party consumer has been registered using the provided consumer key and that it has a valid access token (authorization key in OAuth 2.0) which represents the end user's approval. It's kind of the authentication and the authorization check at the same time.

+

In the latter case an OAuth filter must enforce that the 3rd party consumer has been registered using the provided consumer key and that it has a valid access token which represents the end user's approval. It's kind of the authentication and the authorization check at the same time.

Letting both parties access the resource server via the same URI(s) complicates the life for the security filters but all the parties are only aware of the single resource server URI which all of them will use.

@@ -641,17 +581,17 @@ However, supporting other types of end u

Sharing the same access path between end users and consumers

The first problem which needs to be addressed is how to distinguish end users from third-party consumers and get both parties authenticated as required.
-Perhaps the simplest option is to extend a CXF OAuth filter (JAX-RS or servlet one), check Authorization header, if it is OAuth then delegate to the superclass, alternatively - proceed with authenticating the end users:

+Perhaps the simplest option is to extend a CXF OAuth2 filter (JAX-RS or servlet one), check Authorization header, if it is OAuth2 then delegate to the superclass, alternatively - proceed with authenticating the end users:

-public class SecurityFilter extends org.apache.cxf.rs.security.oauth.filters.OAuthRequestFilter {
+public class SecurityFilter extends org.apache.cxf.rs.security.oauth2.filters.OAuthRequestFilter {
    @Context
    private HttpHeaders headers;
 
    public Response handleRequest(ClassResourceInfo cri, Message message) {
        String header = headers.getRequestHeaders().getFirst("Authorization");
-       if (header.startsWith("OAuth ")) {
+       if (header.startsWith("Bearer ")) {
            return super.handleRequest(cri, message);
        } else {
            // authenticate the end user
@@ -695,7 +635,7 @@ For example, consider the following JAX-
 1. update the calendar available at the same path 
2. read the private Calendars available at "/calendar/{id}/private"

-

As noted above, Client, AccessToken (in its Token superclass) and OAuthPermission all have an optional URIs property. Thus one way to solve the problem with the private calendar is to add, say, a uri "/calendar/{id}" or "/calendar/1" (etc) property to OAuthPermission (representing a scope like "readCalendar") and the OAuth filter will make sure no subresources beyond "/calendar/{id}" can be accessed. Note, adding a "*" at the end of a given URI property, for example, "/a*" will let the consumer to access "/a", "/a/b", etc.

+

As noted above, OAuthPermission has an optional URIs property. Thus one way to solve the problem with the private calendar is to add, say, a uri "/calendar/{id}" or "/calendar/1" (etc) property to OAuthPermission (representing a scope like "readCalendar") and the OAuth filter will make sure no subresources beyond "/calendar/{id}" can be accessed. Note, adding a "*" at the end of a given URI property, for example, "/a*" will let the consumer to access "/a", "/a/b", etc.

Solving the problem with preventing the update can be easily solved by adding an httpVerb property to a given OAuthPermission.

@@ -713,8 +653,8 @@ For example, consider the following JAX-

What Is Next

-

Fine tuning the current OAuth 1.0 will be continued and the feedback from the implementers will be welcomed.
-OAuth 2.0 is going to become a very major specification in the whole RESTful space and CXF will implement the selected OAuth 2.0 profiles. Among other things, OAuth 2.0 will also rely on SAML in one of the extensions and we'll look into it too. Writing a complete OAuth application will most likely require some SSO solution so some support from CXF can be expected.

+

Fine tuning the current OAuth 2.0 implementation will be continued and the feedback from the implementers will be welcomed.
+OAuth 2.0 grants based on SAML2 or JWT assertions, OAuth 2.0 extensions - are all of interest to CXF.