Versions Compared

Key

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

...

When developing a third party application which needs to participate in OAuth2 flows one has to write the code that will redirect deal with redirecting users to OAuth2 AuthorizationCodeGrantService, interact interacting with AccessTokenService in order to exchange code grants for access tokens as well as correctly build building Authorization OAuth2 headers when accessing the end users' resources.

Advanced OAuth2 client applications

In a number of cases an OAuth2 client application supporting the code flow needs to have an OAuth2-specific code written directly. Such clients qualify as advanced given that writing such a code requires thel understanding of OAuth2 specifics. That said,

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, so ultimately the code required to support is typically not that complex at all, while at the same it offers the most flexibility.

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

...

refresh the tokens, avoiding doing a futile HTTP request that is bound to return 401. Or/and indeed it can take care of JAX-RS NotAuthorizedException (401) and refresh the tokens. Sophisticated clients might want to check which scopes have been approved for a given access token and dynamically decide if a given HTTP service call can be made or not. Clients can also proactively revoke the tokens using a token revocation mechanism.

OAuth2 client

...

applications with code-grant filters

The code in the previous section shows the client application code directly supporting OAuth2 dynamics (redirection, the access token acquisition). Starting from CXF 3.0.6 a simpler option is possible with the help of ClientCodeRequestFilter. This filter manages the initial redirection, exchanging code grants for tokens, persisting the request state, and then making the token and the state available to the application code, for example, the following code does not deal itself with teh redirection or interacting with OAuth2 servers:

Code Block
java
java
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.rs.security.oauth2.client.ClientTokenContext;

@Path("reserve")
public class ReservationService {

private WebClient socialService;
private WebClient restaurantService;

@GET
@Path("table")
@Produces("text/html")
public Response reserve(@Context ClientTokenContext context) {
    // Check if token is available
    if (context.getToken() == null) {
       return redirectToFailureHandler(NO_CODE_GRANT);
    }
    // Prepare Authorization header
    socialService.authorization(context.getToken());
    
    // Get the state that was captured by the filter before redirecting the user to OAuth2 server
    ReservationRequest request = context.getState(ReservationRequest.class);
    
    // Work with the service on behalf of a user    
    Calendar c = null;
    try {
      c = socialService.get(Calendar.class);
    } catch (RuntimeException ex) {
      return redirectToFailureHandler(CALENDAR_ACCESS_PROBLEM);
    }
        
    CalendarEntry entry = c.getEntry(request.getHour());
    if (entry.getEventDescription() == null || entry.getEventDescription().trim().isEmpty()) { 
        String address = restaurantService.post(new Form().param("name", request.getReserveName()) 
                                         .param("phone", request.getContactPhone()) 
                                         .param("hour", Integer.toString(request.getHour())),
                                          String.class);
        if (address == null) {
            return redirectToFailureHandler(NO_RESERVATION);
        }
            
        // update the user's calendar
        Response response = socialService.form(new Form().param("hour", Integer.toString(request.getHour()))
                                         .param("description", "Table reserved at " + address));
        boolean calendarUpdated = response.getStatus() == 200 || response.getStatus() == 204;
            
        return Response.ok(new ReservationConfirmation(address, request.getHour(), calendarUpdated))
                           .build();
        } else {
            return redirectToFailureHandler(CALENDAR_BUSY);
        }
    }
}

 

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.ClientTokenContextProviderImpl"/>
           <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.

In the above example the filter uses a custom 'clientCodeStateManager' (org.apache.cxf.rs.security.oauth2.client.ClientCodeStateManager implementation) for keeping the original request state for it to be available later on to the applicationc code - this is optional and is only needed if the redirection request depends on the request parameters (example, Oauth2 scope values are dynamic such as updateCalendar-7 where '7' is the hour) . By default, CXF ships some state managers out of the box, at the moment these are MemoryClientCodeStateManager and JoseClientCodeStateManager implementations, the latter signs and/or encrypts the request state and saves it in the HTTP session.

OAuth2 client authenticators for non-dynamic clients

Not all clients that may need to access an OAuth2-protected application server can be modified. FurthermoreNot all clients that may need to access an OAuth2-protected application server can be modified. Futhermore, not all OAuth2 clients can participate in advanced flows such as an authorization code flow and need to be initialized with access and refresh tokens.

...