You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@cxf.apache.org by "Sergey Beryozkin (JIRA)" <ji...@apache.org> on 2012/05/11 14:37:50 UTC

[jira] [Commented] (CXF-4309) OAuth2 Access Token Service: returned ClientAccessToken is not JAXB compliant

    [ https://issues.apache.org/jira/browse/CXF-4309?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13273203#comment-13273203 ] 

Sergey Beryozkin commented on CXF-4309:
---------------------------------------

Can you please try org.apache.cxf.rs.security.oauth2.provider.OAuthJSONProvider ?

The problem with using JSONProvider or Jackson dealing with OAuth2 access tokens or errors is that they are represented as flat sequences, and access tokens (thinking of MAC for ex) can have few extra properties, so it it is tricky to figure out where is what when reading with generic providers like JSONProvider/etc

                
> OAuth2 Access Token Service: returned ClientAccessToken is not JAXB compliant
> -----------------------------------------------------------------------------
>
>                 Key: CXF-4309
>                 URL: https://issues.apache.org/jira/browse/CXF-4309
>             Project: CXF
>          Issue Type: Bug
>          Components: JAX-RS Security
>    Affects Versions: 2.6
>            Reporter: Jordi Torrente
>              Labels: jax-rs, jaxb, oauth2
>
> The OAuth2 Access Token Service current implementation (class org.apache.cxf.rs.security.oauth2.services.AccessTokenService) processes a request inside "handleTokenRequest()" and this method returns the successfully generated token using an instance of ClientAccessToken.
> But that class has two problems or limitations:
> 1) It is not a JAXB-annotated bean so the error "No message body writer has been found for response class ClientAccessToken" is raised. This can be solved adding the "jaxbElementClassMap" property to the default JSON provider (jettison):
> <bean id="jsonProvider" class="org.apache.cxf.jaxrs.provider.json.JSONProvider">
>   <property name="jaxbElementClassMap" ref="myElementClassMap"/>
> </bean>
> <util:map id="myElementClassMap">
>   <entry key="org.apache.cxf.rs.security.oauth2.common.ClientAccessToken" value="ClientAccessToken"/>
> </util:map>
> <jaxrs:server id="oauth2Server" address="/oauth2">
>   <jaxrs:serviceBeans>
>     <ref bean="accessTokenService2"/>
>   </jaxrs:serviceBeans>
>   <jaxrs:providers>
> 	<ref bean="jsonProvider"/>
>   </jaxrs:providers>    
> </jaxrs:server>
> Unluckily, after doing that change we find out the second problem:
>  
> 2) ClientAccessToken does not have a no-arg default constructor, so it's not JAXB compliant, and the default JSON provider is unable to serialize the data:
> org.apache.cxf.rs.security.oauth2.common.AccessToken does not have a no-arg default constructor.
> 	this problem is related to the following location:
> 		at org.apache.cxf.rs.security.oauth2.common.AccessToken
> 		at org.apache.cxf.rs.security.oauth2.common.ClientAccessToken
> 		
> The only way I've found to overcome both limitations is changing the JSON provider to Codehaus jackson:
> <bean id="jacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper"/>
> <bean id="jsonProvider" class="org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider">
>   <property name="mapper" ref="jacksonObjectMapper" />
> </bean>
> <jaxrs:server id="oauth2Server" address="/oauth2">
>   <jaxrs:serviceBeans>
>     <ref bean="accessTokenService2"/>
>   </jaxrs:serviceBeans>
>   <jaxrs:providers>
>     <ref bean="jsonProvider"/>
>   </jaxrs:providers>    
> </jaxrs:server>
> Here you can see a response generated using this new provider:
> Response-Code: 200
> Content-Type: application/json
> Headers: {Cache-Control=[no-store], Pragma=[no-cache], Date=[Fri, 11 May 2012 11:11:29 GMT]}
> Payload: {"tokenKey":"e91ffcefb133de5eb7ebd02c25e7886e","tokenType":"bearer","parameters":{},"approvedScope":null,"refreshToken":null}
> So far all the work is done at the server side, but what about the client side?
> If we try to de-serialize an access token response using CXF client capabilities (org.apache.cxf.jaxrs.client.WebClient), we will find the same already known problems: 
> a) With jettison: 
> No message body reader has been found for class : class org.apache.cxf.rs.security.oauth2.common.ClientAccessToken, ContentType : application/json
> Adding the "jaxbElementClassMap" property to the provider, will stop us at the no-arg default constructor problem:
> JSONProvider<?> provider = new JSONProvider<Object>();
> provider.setJaxbElementClassMap(Collections.singletonMap("org.apache.cxf.rs.security.oauth2.common.ClientAccessToken", "ClientAccessToken"));
> WebClient client = WebClient.create("http://localhost:8080/fwmobisecurity2/services/oauth2", Collections.singletonList(provider));
> [...]
> ClientAccessToken obj = client.post(formData, ClientAccessToken.class);
> 	
> b) With jackson:
> No suitable constructor found for type [simple type, class org.apache.cxf.rs.security.oauth2.common.ClientAccessToken]: can not instantiate from JSON object (need to add/enable type information?)
> Luckily, jackson offers "Mix-in Annotations" that allow us to define which constructor to use, and its parameter binding:
> First we must create a class with the following content:
> import org.codehaus.jackson.annotate.JsonCreator;
> import org.codehaus.jackson.annotate.JsonProperty;
> public abstract class ClientAccessTokenDeserializeInfo {
> 	@JsonCreator
> 	ClientAccessTokenDeserializeInfo(
> 			@JsonProperty(value="tokenType") String tokenType, 
> 			@JsonProperty(value="tokenKey") String tokenKey) { }
> }
> And then we map it to the ClientAccessToken class:
> JacksonJsonProvider provider = new JacksonJsonProvider();
> ObjectMapper mapper = new ObjectMapper();
> mapper.getDeserializationConfig().addMixInAnnotations(ClientAccessToken.class, ClientAccessTokenDeserializeInfo.class);
> provider.setMapper(mapper);
> WebClient client = WebClient.create("http://localhost:8080/fwmobisecurity2/services/oauth2", Collections.singletonList(provider));
> [...]
> ClientAccessToken obj = client.post(formData, ClientAccessToken.class);
> Conclusion: Without changing ClientAccessToken source code, jackson JSON provider MUST be used at server and client sides

--
This message is automatically generated by JIRA.
If you think it was sent incorrectly, please contact your JIRA administrators: https://issues.apache.org/jira/secure/ContactAdministrators!default.jspa
For more information on JIRA, see: http://www.atlassian.com/software/jira