views:

1554

answers:

1

Okay, so we have a legacy ASMX web service that is currently running in .NET 3.5 and we're using Visual Studio 2008.

The problem is, we need to add authentication and would like to take advantage of the WS-Security model without breaking any existing internal clients who don't need to authenticate currently.

We've thought about adding custom headers, but that's not very WS-Security-ish. Also upgrading to WCF, while a long term goal, is not viable in the short-term.

Is there a way to access the UsernameToken (provided it's passed by the client) indirectly in the soap header of a VS2008 ASMX web service?

+1  A: 

You could try Web Services Enhancements (WSE) 3.0. This adds support for an old version of WS-Security (the 2004 version I think - WCF supports the 2005 and 2007 versions). It sits on top of ASMX without disturbing it, and does still work in .NET 3.5 / WS2008.

Now for the downsides:

  • VS2008 does not support adding or updating WSE-enabled web references in client code. It will happily create the normal ASMX proxy class, but not the extra WSE proxy class that is required for authentication. Any existing WSE proxy code you have does compile OK, but will be deleted if you try to update the web reference in the IDE. If you possess a copy of VS2005, you could use it to maintain or at least create the web reference on the client side.
  • AFAIK, the WSE implementation of WS-Security is not 100% forward-compatible with the WCF implementations. You will need to do some compatibility testing of your own with WCF to make sure.

Example

Specifying credentials on the client:

void SetUsernameCredential(WebServicesClientProtocol service, string userName, string password) {
    UsernameToken token = new UsernameToken(userName, password, PasswordOption.SendHashed);
    service.SetClientCredential(token);
}

Authenticating credentials on the server:

public class MyUsernameTokenManager : UsernameTokenManager {
    protected override string AuthenticateToken(UsernameToken token) {
        // Authenticate here.
        // If succeess, return an authenticated IPrincipal and the user's password as shown.
        // If failure, throw an exception of your choosing.
        token.Principal = principal;
        return password;
    }
}

Reading credentials on the server:

IPrincipal principal = RequestSoapContext.Current.IdentityToken.Principal;
Christian Hayter
-1 for recommending new development using WSE, which is obsolete.
John Saunders
I believe that my answer is helpful within the limits imposed by the question. I never said I recommended it for new development.
Christian Hayter
I consider "we need to add authentication" to be "new development" in this context. The -1 is for showing the OP how to put his head into a noose.
John Saunders