...
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 | ||||
---|---|---|---|---|
| ||||
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 | ||
---|---|---|
| ||
<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.
...