Versions Compared

Key

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

...

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

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 filters

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

CXF HTTPConduit HttpAuthSupplier supporting access and refresh tokens is shipped starting from CXF 3.0.5 .

org.apache.cxf.rs.security.oauth2.client.BearerAuthSupplier supports creating HTTP Authorization header from bearer access tokens, refreshing them proactively or in response to 401 failures and recreating HTTP Authorization from the refreshed token.

It is not possible to refresh a token from a JAX-RS ClientRequestFilter because such a filter does not handle HTTP responses so it can not detect 401 (returned by a server if the access token has expired), while HTTPConduit HttpAuthSupplier gets a chance to react to 401 and retry.

Here is a configuration example:

 

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>

 

At the moment only BearerAuthSupplier supporting bearer access tokens is available; authenticators supporting other well known token types will be provided in the future.

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:

 

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="codeAuthSupplier" class="org.apache.cxf.rs.security.oauth2.client.CodeAuthSupplier">
   <!-- authorization code -->
   <property name="code" value="12345678"/>

   <!-- Set this property for the authenticator to check the access token expiry date and refresh the token proactively -->
   <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="codeAuthSupplier"/>
  </authSupplier>
</conduit>
</beans>

 

Additionally, a basic JAX-RS 2.0 ClientRequestFilter, org.apache.cxf.rs.security.oauth2.client.BearerClientFilter, is shipped and is initialized with an "accessToken" property only. It might be used in cases where only a non-expiring access token is available.

Using a token that expires within ClientRequestFilter does not work as explained above. However BearerClientFilter might be enhanced to support the pro-active refreshment of access token in the future.

OAuth2 without the Explicit Authorization

...