Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

This request contains a client_id and client_secret (Authorization header), the grant_type, the grant value (code)
plus the redirect URI the authorization grant was returned to which is needed for the additional validation.
Note that the alternative client authentication methods are also possible, in this case the token service will expect a mapping between the client credentials and the client_id representing the client registration available.

...

Finally OAuthDataProvider may need to convert opaque scope values such as "readCalendar" into a list of OAuthPermissions. AuthorizationCodeGrantService and OAuth2 security filters will depend on it (assuming scopes are used in the first place). In the former case AuthorizationCodeGrantService will use this list to populate OAuthAuthorizationData - the reason this bean only sees Permissions is that some of the properties OAuthPermission keeps are of no interest to OAuthAuthorizationData handlers.

Default Providers

...

Starting from CXF 3.0.2 default Ehcache-based and encrypting  OAuthDataProvider and AuthorizationCodeDataProvider implementations are shipped.

...

org.apache.cxf.rs.security.oauth2.grants.code.DefaultEncryptingCodeDataProvider and org.apache.cxf.rs.security.oauth2.provider.DefaultEncryptingOAuthDataProvider use an auto-generated or provided Java symmetric SecretKey to store grant, access and refresh token data.

...

Code Block
xml
xml
<!-- implements OAuthDataProvider -->
<bean id="oauthProvider" class="oauth.manager.OAuthManager"/>
     
<bean id="accessTokenService" class="org.apache.cxf.rs.security.oauth2.services.AccessTokenService">
  <property name="dataProvider" ref="oauthProvider"/>
</bean>
<bean id="accessTokenValidateService" class="org.apache.cxf.rs.security.oauth2.services.AccessTokenValidatorService">
  <property name="dataProvider" ref="oauthProvider"/>
</bean>


<jaxrs:server id="oauthServer" address="/oauth">
   <jaxrs:serviceBeans>
      <ref bean="accessTokenService"/>
      <ref bean="accessTokenValidateService"/>
  </jaxrs:serviceBeans>
</jaxrs:server>

...

If a 2-way TLS is sued to authenticate a client and Client has a Base64 encoded representations of its X509Certificates available in its "applicationCertificates" property then AccessTokenService will do the additional comparison of these certificates against the ones available in the current TLS session.

 

Please see JAXRS OAuth2 Assertions section for more information on how it may work.

 

User Session Authenticity

...

OAuth2 tokens and SOAP endpoints

 

If you use HTTP Authorization header or WS-Security Binary token to pass OAuth2 tokens to SOAP endpoints then OAuthRequestInterceptor can be used to validate such tokens. It is OAuthRequestFilter running as CXF interceptor which will work OOB for tokens passed with Authorization header and it can be easily extended to support WS-Security binary tokens

...

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:

...

Code Block
java
java
public class OAuthClientManager {
	
	private WebClient accessTokenService;
        private String authorizationServiceURI;
        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 ClientAccessToken getAccessToken(AuthorizationCodeGrant codeGrant) {
	    try {
	        return OAuthClientUtils.getAccessToken(accessTokenService, consumer, codeGrant);
	    } catch (OAuthServiceException ex) {
	        return null;
	    }
	}
	
	public String createAuthorizationHeader(ClientAccessToken token) {
		return OAuthClientUtils.createAuthorizationHeader(consumer, token);
	}
}

...

Code Block
java
java
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.core.HttpHeaders;

import org.apache.cxf.rs.security.oauth2.client.OAuthClientUtils;
import org.apache.cxf.rs.security.oauth2.client.OAuthClientUtils.Consumer;
import org.apache.cxf.rs.security.oauth2.grants.code.AuthorizationCodeGrant;
import org.apache.cxf.rs.security.oauth2.grants.refresh.RefreshTokenGrant;
import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;



// the pseudo-code for getting the access token
Consumer consumer = ...
AuthorizationCodeGrant codeGrant = ...

ClientAccessToken accessToken = OAuthClientUtils.getAccessToken(codeGrant, consumer);

WebClient endUserResourceClient = WebClient.create(endUserServerAddress);

endUserResourceClient.header(HttpHeaders.AUTHORIZATION, 
                             OAuthClientUtils.createAuthorizationHeader(accessToken));
try {
   return endUserResourceClient.get();
} catch (NotAuthorizedException ex) {
    String refreshToken = accessToken.getRefreshToken();
    if (refreshToken != null) {
        // retry once

        // refresh the token
        accessToken = OAuthClientUtils.getAccessToken(new RefreshTokenGrant(refreshToken), consumer);

        // reset Authorization header
        endUserResourceClient.replaceHeader(HttpHeaders.AUTHORIZATION, 
                             OAuthClientUtils.createAuthorizationHeader(accessToken)); 

        // try to access the end user resource again
        return endUserResourceClient.get();
        
    } else {
        throw ex;
    }

}



The client code directly dealing with OAuth2 specifics can be the most flelxible option: the client which has both access and refresh tokens can check the current access token expiry time and if it is known to have expiried then it can proactively

...

The filter is configured as follows: 

 

Code Block
languagexml
<beans>
    
<jaxrs:server id="reservationsServer" address="/reservations">
        <jaxrs:serviceBeans>
           <ref bean="restaurantReserveService"/>
        </jaxrs:serviceBeans>
        <jaxrs:providers>
           <!-- other providers -->

           <bean class="oauth2.thirdparty.CustomClientTokenContextProvider"/>
           <bean class="org.apache.cxf.rs.security.oauth2.client.ClientCodeRequestFilter">
               <property name="authorizationServiceUri" value="http://localhost:8080/services/authorize"/>
               <property name="accessTokenServiceClient" ref="atServiceClient"/>
               <property name="startUri" value="reserve/table"/>
               <property name="clientCodeStateManager" ref="codeManager"/>
               <property name="consumer" ref="consumer"/>
           </bean>
        </jaxrs:providers>
     </jaxrs:server>
     
<bean id="codeManager" class="oauth2.thirdparty.ClientCodeStateManagerImpl"/>

     <!-- the consumer pre-registered with OAuth2 servers -->
     <bean id="consumer" class="org.apache.cxf.rs.security.oauth2.client.Consumer">
         <property name="key" value="123456789"/>
         <property name="secret" value="987654321"/>
     </bean>

     <!-- WebClient for communicating with OAuth2 AccessTokenService -->
    <jaxrs-client:client id="atServiceClient" serviceClass="org.apache.cxf.jaxrs.client.WebClient"
         address="http://localhost:8080/services/oauth2Token/token">
         <jaxrs-client:headers>
            <entry key="Accept" value="application/json"/>
         </jaxrs-client:headers>
     </jaxrs-client:client>

</beans>


 ClientCodeRequestFilter redirects to 'authorizationServiceUri' when a 'startUri' is matched.

...

Code Block
languagexml
<beans>
<bean id="consumer" class="org.apache.cxf.rs.security.oauth2.client.Consumer">
   <property name="clientId" value="1"/>
   <property name="clientSecret" value="2"/>
</bean>
<bean id="bearerAuthSupplier" class="org.apache.cxf.rs.security.oauth2.client.BearerAuthSupplier">
   <!-- access token -->
   <property name="accessToken" value="12345678"/>
   <!-- refresh token and the info needed to use it to refersh the expired access token proactively or in response to 401 --> 
   <property name="refreshToken" value="87654321"/>
   <!-- 
       Set this property for the authenticator to check the access token expiry date and refresh the token proactively.
       Note that this property can also become effective after the first token refresh as it is not known in advance when 
       the injected access token will expire
   -->
   <property name="refreshEarly" value="true"/>
   <!-- client OAuth2 id and secret - needed to use a refresh token grant --> 
   <property name="consumer" ref="consumer"/>
   <!-- address of OAuth2 token service that supports a refresh token grant
   <property name="accessTokenServiceUri" value="https://server/oauth2/accessToken"/>
</bean>
<conduit name="*.http-conduit" xmlns="http://cxf.apache.org/transports/http/configuration">
  <authSupplier>
     <ref bean="bearerAuthSupplier"/>
  </authSupplier>
</conduit>
</beans>

...

org.apache.cxf.rs.security.oauth2.client.CodeAuthSupplier is also shipped. It is similar to BearerAuthSupplier except that it is initailized with an authorization code grant obtained out of band, uses this grant to get the tokens and then delegates to BearerAuthSupplier. Example:

...