Versions Compared

Key

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

...

JAXRS: OAuth2 Assertions

Table of Contents

Introduction

...

Maven dependencies

Code Block
xml
xml

<dependency>
  <groupId>org.apache.cxf</groupId>
  <artifactId>cxf-rt-rs-security-oauth2-saml</artifactId>
  <version>2.7.4</version>
</dependency>

...

The additional restriction is that the assertions have to be encoded using Base64Url encoding.
Here is how a request may look like:

Code Block

POST /token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Asaml2-bearer&
assertion=Base64UrlEncoded-SAML2-Bearer-Assertion

...

The following example shows how to use SAML2 Bearer assertion as a grant with CXF OAuth2 client code:

Code Block
java
java

import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.rs.security.common.CryptoLoader;
import org.apache.cxf.rs.security.oauth2.client.OAuthClientUtils;
import org.apache.cxf.rs.security.oauth2.common.AccessTokenGrant;
import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
import org.apache.cxf.rs.security.oauth2.grants.saml.Saml2BearerGrant;
import org.apache.cxf.rs.security.saml.SAMLUtils;
import org.apache.cxf.rs.security.saml.SAMLUtils.SelfSignInfo;
import org.apache.ws.security.components.crypto.Crypto;

//1: create web client
String address = "https://localhost:8080/oauth2/token";
WebClient wc = WebClient.create(address);
wc.type(MediaType.APPLICATION_FORM_URLENCODED).accept(MediaType.APPLICATION_JSON);

//2. Create and self-sign SAML assertion        
Crypto crypto = new CryptoLoader().loadCrypto(CRYPTO_RESOURCE_PROPERTIES);
SelfSignInfo signInfo = new SelfSignInfo(crypto, "alice", "password"); 
        
String assertion =  SAMLUtils.createAssertion(new SamlCallbackHandler(),
                                              signInfo).assertionToString();

//3. Send it as a token grant to Access Token Service and get some access token back
AccessTokenGrant grant = new Saml2BearerGrant(assertion);
ClientAccessToken at = OAuthClientUtils.getAccessToken(wc, 
                                                       new OAuthClientUtils.Consumer("alice", "alice"), 
                                                       grant,
                                                       false);

...

Here is how one may configure Access Token Service:

Code Block
xml
xml

<bean id="dataProvider" class="org.apache.cxf.systest.jaxrs.security.oauth2.OAuthDataProviderImpl"/>
<bean id="samlGrantHandler" class="org.apache.cxf.rs.security.oauth2.grants.saml.Saml2BearerGrantHandler">
  <property name="dataProvider" ref="dataProvider"/>
</bean>
<bean id="oauthJson" class="org.apache.cxf.rs.security.oauth2.provider.OAuthJSONProvider"/>

<bean id="serviceBean" class="org.apache.cxf.rs.security.oauth2.services.AccessTokenService">
  <property name="dataProvider" ref="dataProvider"/>
  <property name="grantHandlers">
     <list>
       <ref bean="samlGrantHandler"/>
     </list>
  </property>
</bean>

<jaxrs:server address="https://localhost:${testutil.ports.jaxrs-oauth2}/oauth2">
   <jaxrs:serviceBeans>
      <ref bean="serviceBean"/>
   </jaxrs:serviceBeans>
   <jaxrs:providers>
      <ref bean="oauthJson"/>
   </jaxrs:providers>
   <jaxrs:properties>
     <entry key="ws-security.signature.properties" value="org/apache/cxf/systest/jaxrs/security/alice.properties"/>
   </jaxrs:properties>
</jaxrs:server>

...

As noted in the introduction, SAML2 Bearer assertions may also act as client authentication credentials, when requesting an access token, irrespectively of the actual grant type. For example:

Code Block

POST /token HTTP/1.1
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
&code=12345678
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Asaml2-bearer
&client_assertion=Base64UrlEncoded-SAML2-Bearer-Assertion

...

The following example shows how to use SAML2 Bearer assertion as an authentication token:

Code Block
java
java

import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.rs.security.common.CryptoLoader;
import org.apache.cxf.rs.security.oauth2.client.OAuthClientUtils;
import org.apache.cxf.rs.security.oauth2.common.AccessTokenGrant;
import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
import org.apache.cxf.rs.security.oauth2.grants.saml.Saml2BearerGrant;
import org.apache.cxf.rs.security.oauth2.saml.Base64Utility;
import org.apache.cxf.rs.security.oauth2.saml.Constants;
import org.apache.cxf.rs.security.saml.SAMLUtils;
import org.apache.cxf.rs.security.saml.SAMLUtils.SelfSignInfo;
import org.apache.ws.security.components.crypto.Crypto;

//1: create web client
String address = "https://localhost:8080/oauth2/token";
WebClient wc = WebClient.create(address);
wc.type(MediaType.APPLICATION_FORM_URLENCODED).accept(MediaType.APPLICATION_JSON);

//2. Create and self-sign SAML assertion        
Crypto crypto = new CryptoLoader().loadCrypto(CRYPTO_RESOURCE_PROPERTIES);
SelfSignInfo signInfo = new SelfSignInfo(crypto, "alice", "password"); 
        
String assertion =  SAMLUtils.createAssertion(new SamlCallbackHandler(),
                                              signInfo).assertionToString();

// 3. Base64Url-encode it
String encodedAssertion = Base64UrlUtility.encode(assertion);
        
Map<String, String> extraParams = new HashMap<String, String>();
extraParams.put(Constants.CLIENT_AUTH_ASSERTION_TYPE, Constants.CLIENT_AUTH_SAML2_BEARER);
extraParams.put(Constants.CLIENT_AUTH_ASSERTION_PARAM, encodedAssertion);

// Use whatever token grant is required 
AccessTokenGrant accessTokenGrant = ...
       
ClientAccessToken at = OAuthClientUtils.getAccessToken(wc, 
                                                       accessTokenGrant,
                                                       extraParams);

...

A different approach to dealing with the assertion directly in the client code is to use org.apache.cxf.rs.security.oauth2.auth.saml.Saml2BearerAuthOutInterceptor interceptor which will add the assertion to the existing form payload, for example:

Code Block
java
java

JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();

Map<String, Object> properties = new HashMap<String, Object>();
properties.put("ws-security.callback-handler", 
               "org.apache.cxf.systest.jaxrs.security.saml.KeystorePasswordCallback");
properties.put("ws-security.saml-callback-handler", 
               "org.apache.cxf.systest.jaxrs.security.oauth2.SamlCallbackHandler2");
properties.put("ws-security.signature.username", "alice");
properties.put("ws-security.signature.properties", CRYPTO_RESOURCE_PROPERTIES);
properties.put("ws-security.self-sign-saml-assertion", "true");
bean.setProperties(properties);
        
bean.getOutInterceptors().add(new Saml2BearerAuthOutInterceptor());
        
WebClient wc = bean.createWebClient();
wc.type(MediaType.APPLICATION_FORM_URLENCODED).accept(MediaType.APPLICATION_JSON);

// Use whatever token grant is required 
AccessTokenGrant accessTokenGrant = ...
       
ClientAccessToken at = OAuthClientUtils.getAccessToken(wc, 
                                                       accessTokenGrant);

...

Here is how one may configure Access Token Service:

Code Block
xml
xml

<bean id="dataProvider" class="org.apache.cxf.systest.jaxrs.security.oauth2.OAuthDataProviderImpl"/>
<bean id="oauthJson" class="org.apache.cxf.rs.security.oauth2.provider.OAuthJSONProvider"/>
<bean id="samlAuthHandler" class="org.apache.cxf.rs.security.oauth2.auth.saml.Saml2BearerAuthHandler"/>

<bean id="serviceBean" class="org.apache.cxf.rs.security.oauth2.services.AccessTokenService">
  <property name="dataProvider" ref="dataProvider"/>
  <property name="grantHandlers">
     <list>
       <!-- list of required grant handlers -->
     </list>
  </property>
</bean>

<jaxrs:server 
       address="https://localhost:${testutil.ports.jaxrs-oauth2}/oauth2-auth"> 
       <jaxrs:serviceBeans>
          <ref bean="serviceBean"/>
       </jaxrs:serviceBeans>
       <jaxrs:providers>
          <ref bean="oauthJson"/>
          <ref bean="samlAuthHandler"/>
       </jaxrs:providers>
       
       <jaxrs:properties>
           <entry key="ws-security.signature.properties" 
                  value="org/apache/cxf/systest/jaxrs/security/alice.properties"/>
       </jaxrs:properties>
        
</jaxrs:server>

...

In the Client Acting on Behalf of Itself use either org.apache.cxf.rs.security.oauth2.grants.saml.Saml2BearerClientCredentialsGrant :

Code Block
java
java

import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.rs.security.common.CryptoLoader;
import org.apache.cxf.rs.security.oauth2.client.OAuthClientUtils;
import org.apache.cxf.rs.security.oauth2.common.AccessTokenGrant;
import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
import org.apache.cxf.rs.security.oauth2.grants.saml.Saml2BearerClientCredentialsGrant;
import org.apache.cxf.rs.security.saml.SAMLUtils;
import org.apache.cxf.rs.security.saml.SAMLUtils.SelfSignInfo;
import org.apache.ws.security.components.crypto.Crypto;

//1: create web client
String address = "https://localhost:8080/oauth2/token";
WebClient wc = WebClient.create(address);
wc.type(MediaType.APPLICATION_FORM_URLENCODED).accept(MediaType.APPLICATION_JSON);

//2. Create and self-sign SAML assertion        
Crypto crypto = new CryptoLoader().loadCrypto(CRYPTO_RESOURCE_PROPERTIES);
SelfSignInfo signInfo = new SelfSignInfo(crypto, "alice", "password"); 
        
String assertion =  SAMLUtils.createAssertion(new SamlCallbackHandler(),
                                              signInfo).assertionToString();

AccessTokenGrant accessTokenGrant = new Saml2BearerClientCredentialsGrant(assertion);
       
ClientAccessToken at = OAuthClientUtils.getAccessToken(wc, 
                                                       accessTokenGrant,
                                                       extraParams);

or ClientCredentialsGrant in combination with Saml2BearerAuthOutInterceptor:

Code Block
java
java

JAXRSClientFactoryBean bean = new JAXRSClientFactoryBean();

Map<String, Object> properties = new HashMap<String, Object>();
properties.put("ws-security.callback-handler", 
               "org.apache.cxf.systest.jaxrs.security.saml.KeystorePasswordCallback");
properties.put("ws-security.saml-callback-handler", 
               "org.apache.cxf.systest.jaxrs.security.oauth2.SamlCallbackHandler2");
properties.put("ws-security.signature.username", "alice");
properties.put("ws-security.signature.properties", CRYPTO_RESOURCE_PROPERTIES);
properties.put("ws-security.self-sign-saml-assertion", "true");
bean.setProperties(properties);
        
bean.getOutInterceptors().add(new Saml2BearerAuthOutInterceptor());
        
WebClient wc = bean.createWebClient();
wc.type(MediaType.APPLICATION_FORM_URLENCODED).accept(MediaType.APPLICATION_JSON);

// Use whatever token grant is required 
AccessTokenGrant accessTokenGrant = new ClientCredentialsGrant();
       
ClientAccessToken at = OAuthClientUtils.getAccessToken(wc, accessTokenGrant);