Tuesday, October 31, 2006

Propagating Acegi's Security Context in a WSS UsernameToken SOAP Header via XFire using wss4j

XFire includes a ws-security example, which demonstrates how a SOAP Header with a Web Service Security (WSS) UsernameToken can be inserted into an outgoing request message, using Apache's wss4j library. (The XFire ws-security example also demonstrates how to sign and/or encrypt a SOAP message, and how to use the WSS timestamp mechanism.)

I needed this functionality to be available more easily for Java service consumers, in the sense of an implicit security context passing transparent to the programmer when invoking a service. As Java API to set up a security context, Acegi from the Spring Framework should be used.

Maybe first some brief background on WSS: A WSS UsernameToken is really just a username and password, in principle similar to HTTP BASIC authentication. However HTTP BASIC delivers the credential at the transport level, whereas a WSS Header has the advantage of propagating a credential at the (SOAP) message level, thus allowing it to travel forward through several intermediaries. For example, a message could move from a service consumer to a XML security gateway, to an ESB and then into a message queue, out of which it would go into another ESB, which would route it to a service provider. With transport-level instead of message level propagation each intermediary would have to ensure forwarding, or out-of-message storage (queue) of the credential.

The WSS standard standardizes the SOAP headers used for such message level credentials; in addition to a UsernameToken, it could also be a X.509 certificate or a Kerberos ticket or a SAML token.

As a UsernameToken contains a cleartext password, the message would typically be protected either through transport level point-to-point encryption via SSL, just as would be advisable when using HTTP BASIC. Additionally, the SOAP message could also be protected through message level XML encryption.

So, back Java/Acegi/XFire: What I really wanted was to be able to do the following at the service consumer (client):

import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;

new UsernamePasswordAuthenticationToken("uid", "pwd"));



and have the uid/pwd end up "transparently" in the outgoing message produced by XFire. The actual service provider (server) might or might not receive that header, e.g. if an intermediary (actual SOAP intermediary or just an in-process incoming message filter) authenticated and authorized the message, potentially stripping the WSS header. If however it did receive the SOAP header, it might have a need to get the credential back, again easily and using the same Acegi API, without having to deal with SOAP headers etc. directly, by doing:

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
assert auth != null; // Is null if no WSS Header present in SOAP message!
String uid = auth.getName();
String pwd = auth.getCredentials();

The acegi-ws-security-xfire-example contains a working example implementing just that. (I built it by extending the XFire jaxws-spring example I have contributed earlier to the project; but the Acegi/WSS integration itself is unrelated to that, and the Handler etc. infrastructure should be easily reusable in other uses of XFire, e.g. POJO without WSDL).


Anonymous Anonymous said...

I think this is a very good and usefull example.

What would be nice is to extends this example a little further so that you have 2 user that each can access a specific web method based on an User's role. A simple testcase could prove the positive and negative tests.



18 December, 2006 15:20  
Anonymous Anonymous said...

i've a problem.. when i call teh service i get this Caused by: javax.xml.stream.XMLStreamException: NamespaceURI cannot be null
at com.sun.xml.internal.stream.writers.XMLStreamWriterImpl.writeAttribute(Unknown Source)
at org.codehaus.xfire.util.STAXUtils.writeElement(STAXUtils.java:366)
at org.codehaus.xfire.util.STAXUtils.writeNode(STAXUtils.java:391)

can you please help me??

tnx a lot!!

02 May, 2007 17:07  
Anonymous Anonymous said...

I tried your xfire-acegi example but I can't get it to work, it gives me the error message - "Authentication Security Context lost in message transport?". Do you have a complete client example that I can use? I ma using Geronimo 1.1.1, xfire 1.2.4, spring 2.0.4.

02 May, 2007 20:00  
Anonymous Anonymous said...

Setting an Acegi token in the security context is only adding the token to a local thread variable. If you only do that and nothing else, the token will only be availalbe on that local thread instance and will be dereferenced when that thread terminates. This works well from the client perspective as it allows you to transparently propagate the token to a WS-S username token header in an outbound SOAP message. However, you need to have a matching implementation on the server side that uses an X-Fire InHandler to read the token in from the SOAP header, process it, and add it into the servers Acegi security context (if the credential is valid) for subsequent processing. If you do not do this, the token will not be avaiallbe on the server side (in the server JVM) using just the code sample described in this article.

19 June, 2007 06:27  
Anonymous Anonymous said...

Hello Michael,
Great post. Thanks. I have a question, our clients (external) can invoke our webservvices from either Java, .NET or any other platform. Will your approach work for a client different from Java.


10 August, 2007 00:55  
Anonymous Anonymous said...

it is really helpful, I wonder if the client side, who need consumes this service, is developed by dotnet or other java project like aixs, how can i generate a similar header for authentation?

thank you very much.

19 August, 2007 03:12  
Anonymous Anonymous said...

as acegi in the security for projects is used that work with xfire?

27 November, 2007 16:47  
Anonymous Anonymous said...

Ignore Richard's comment about "Authentication Security Context lost in message transport?" A look at the JUnit test shows that's an expected result because the context has been cleared.

08 April, 2008 21:25  
Anonymous Anonymous said...

great, realy great.

31 May, 2008 13:25  

Post a Comment

<< Home