Welcome

You have reached the blog of Keith Elder. Thank you for visiting! Feel free to click the twitter icon to the right and follow me on twitter.

Take Securing Web Services With Username and Password One Step Further With a Custom SoapExtension

Posted by Keith Elder | Posted in Asp.Net, Programming | Posted on 09-01-2007

In a previous posts I showed how to secure your web services with a username and a password.  One of the readers added a comment and mentioned using an extension to provide authentication so I thought I would expand on how that is done.  If you haven’t read the previous posts do that first, then pickup with this.  And before we get started, thanks for the idea Kevin!

Have you ever wanted to intercept a web service method because you wanted to maybe log it or even as we are about to do authenticate a user?  Did you know you can intercept any incoming SoapMessage that is sent to your web service?  This is all possible because of the SoapExtension class in .Net. Not only can you intercept the incoming message but you can do it within one of four stages:

  1. AfterDeserialize
  2. AfterSerialize
  3. BeforeDeserialize
  4. BeforeSerialize

This allows us a lot of flexibility obviously.  In our example we are going to work with the AfterDeserialize stage which is after the message has been sent across the wire and serialized into a SoapMessage object.  Since we will have a full blown SoapMessage object, we can inspect the headers of the SoapMessage and take care of authentication then.  Our end goal with taking this approach is to allow us to authenticate a user with a WebMethod simply by adding an authentication attribute to the WebMethod like this highlighted in bold.

    1     [WebMethod]

    2     [SoapHeader(“CustomSoapHeader”)]

    3     [AuthenticatonSoapExtensionAttribute] (magic here)

    4     public int AddTwoNumbers(int x, int y)

    5     {

    6         return x + y;

    7     }

You’ll notice in this example we do not have to remember to call the code to authenticate the user manually as we saw in the previous article.  Instead by adding the attribute to the method it knows to call the authentication method.  To do this we need to first create a custom object that extends SoapExtensionAttribute and then another that extends SoapExtension.

Create a Custom SoapExtensionAttribute

In order to have the method call our custom SoapExtension we need to create an object that extends SoapExtensionAttribute.  It is a farily simple class with two overridden properties.  Here’s the code:

    1     [AttributeUsage(AttributeTargets.Method)]

    2     public class AuthenticatonSoapExtensionAttribute : SoapExtensionAttribute

    3     {

    4         private int _priority;

    5 

    6         public override int Priority

    7         {  

    8             get { return _priority; }

    9             set { _priority = value; }

   10         }

   11 

   12         public override Type ExtensionType

   13         {

   14             get { return typeof(AuthenticatonSoapExtension); }

   15         }

   16     }

You’ll notice about the only thing of real substance is the Extension type property which simply returns to us our custom extension.

Create a Custom SoapExtension

The last piece to pull this all together is a custom class which extends the SoapExtension class.  In this class we are going to write the code that does the actual authentication.  We are going to check for the AfterDeserialize stage and then first make sure we have a valid SoapHeader.   Once we do that we are going to call the static validation method and pass in the SoapHeader as we did above. 

    1 /// <summary>

    2     /// Custom SoapExtension that authenticates the method being called.

    3     /// </summary>

    4     public class AuthenticatonSoapExtension : SoapExtension

    5     {

    6 

    7         /// <summary>

    8         /// When overridden in a derived class, allows a SOAP extension to initialize data specific to an XML Web service method using an attribute applied to the XML Web service method at a one time performance cost.

    9         /// </summary>

   10         /// <param name=”methodInfo”></param>

   11         /// <param name=”attrib”></param>

   12         public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attrib)

   13         {

   14             return null;

   15         }

   16 

   17         /// <summary>

   18         /// When overridden in a derived class, allows a SOAP extension to initialize data specific to a class implementing an XML Web service at a one time performance cost.

   19         /// </summary>

   20         /// <param name=”WebServiceType”></param>

   21         public override object GetInitializer(Type WebServiceType)

   22         {

   23             return null;

   24         }

   25 

   26         /// <summary>

   27         /// When overridden in a derived class, allows a SOAP extension to initialize itself using the data cached in the GetInitializer method.

   28         /// </summary>

   29         /// <param name=”initializer”></param>

   30         public override void Initialize(object initializer)

   31         {

   32 

   33         }

   34 

   35         /// <summary>

   36         /// After the message is deserialized we authenticate it.

   37         /// </summary>

   38         /// <param name=”message”></param>

   39         public override void ProcessMessage(SoapMessage message)

   40         {

   41            

   42             if (message.Stage == SoapMessageStage.AfterDeserialize)

   43             {

   44                 Authenticate(message);

   45             }

   46         }

   47 

   48         public void Authenticate(SoapMessage message)

   49         {

   50             ServiceAuthHeader header = message.Headers[0] as ServiceAuthHeader;

   51             if (header != null)

   52             {

   53                 ServiceAuthHeaderValidation.Validate(header);

   54             }

   55             else

   56             {

   57                 throw new ArgumentNullException(“No ServiceAuthHeader was specified in SoapMessage.”);

   58             }

   59         }

   60     }

The method that we are really concerned with is the ProcessMessage which checks for the stage and then calls the Authenticate method.  This in turn calls our static validation method which checks for authentication.  At this point light bulbs should be going off!   Since we have a SoapMessage object do we not know which method is being called?  Yes!  Could we modify the ServiceAuthHeaderValidation to check for a database instead of hard coding things?  Yes!  Now you are starting to see where this could really go.   SoapExtensions are powerful and only limited to your imagination. 

When I Test It, It Doesn’t Work, Why?

Once you get your SoapExtension in your solution setup and press F5 to debug it within Visual Studio  it will launch a new web server on a random port and bring you to your service.  You enter the parameters and submit the form and it by passes your validation.  Why!? 

This is suppose to happen and here is why. If you go to the service invoked from VS through the browser interface it will not invoke the authentication and it isn’t suppose to either. The reason is you are not invoking the service via SOAP but rather just a standard POST from a form. Therefore, the SOAP extension is never going to fire. This should be disabled when you publish your web service to production as only SOAP messages should be allowed. If you have a case where you need to allow GET and POST calls then the method of a custom SoapExtension isn’t going to work. Go back to the previous way outlined that I talked about.

As a benefit, Visual Studio builds the form for you automatically when you press F5 and allows you to pass parameters to the web method, but it does it via POST. If you invoke the web method from a console application or a real client making a SOAP call, you have to pass in the username and password.  I actually consider this behavior a feature.  If we didn’t use the SoapExtension to secure the method, we’d be forced to pass in username and password all the time which would mean we’d have to always call the secured web method from a test client.   Speaking from experience this isn’t fun.  Of course you should have Unit Tests for each web method anyway but it is really easy to pass in the params to a web form while debugging.

I hope you find this useful and now don’t feel so daunted because your team leader asked you how you were going to authenticate web service methods via a database.  The only thing left is for you to implement your required features.  Of course, if you are on an Intranet, instead of using username and password as we did in the previous post you could at the point of Authenticate(SoapMessage message) use the user’s integrated credentials and check for various groups in Active Directory or even using Enterprise Library Security Block. 

Like this article?  Then kick it on DotNetKicks.com

Comments (22)

Hi,

First of all, thanks for the great article. It proved to be a big help! I love your solution but would like to make a modification to it. The problem is I’m not sure how to accomplish my goal.

I would like to return a 403 status code when the username /password is invalid. But how would I do this from the validate method and without the executing the webmethod that was called?

Ok, I promise this is the last one for today…

I just added a Header to the Message inside the AuthenticatonSoapExtension, after validation succeeded. It’s a basic sub-class of SoapHeader that holds an object containing some details about the user.

I had to add this SoapHeader to the list of attributes above the WebMethod and add an instance of it to the class, much like “CustomSoapHeader”. It is working, though, which I’m quite pleased with!

Is this a good technique to use? It seems quite tidy to me.

Just partly answering my own question here…

I have adapted the ServiceAuthHeaderValidation.Validate(ServiceAuthHeader) method to return an instance of my object rather than a bool. This means I’ve now got the thing I want sitting in an instance of AuthenticatonSoapExtension.

The thing I don’t know is how I can get access to that object from within my WebMethod. Could I find the instance of AuthenticatonSoapExtension that was just created? Maybe I should store my object somewhere else that can be read by the WebMethod.

I imagine there’s a Session or somewhere I could put it, but I’d like to go with best practices for WebServices and this isn’t my area of expertise, to be honest.

Thanks again for a really helpful couple of articles on this.

I like this approach. I was previously passing username and password as the first two parameters to each WebMethod, which became a bit overbearing at times.

I’d like to look at more regarding Authorization, though. I currently use the supplied username/password to locate the appropriate row in the database and throw an AuthenticationException if there is no match found. If authentication succeeded (i.e. there is a matching row) I then look at the returned object which I use for other things. I suppose I could re-use the same headers elsewhere to retrieve an object of sorts from the database, but I’d like most of all if the Authentication routine could return an entity of some kind that contains the details I’m interested in. Do you have any recommendations as to how I could do something like that?

@Arun

What you are describing has already been solved and there is a standard around it. With ASMX services it isn’t that fun to implement. WCF is a much better option.

http://en.wikipedia.org/wiki/WS-Security

-Keith

Really nice article…

but if i use soapextension to log the xml created then i will be able to see the username and password…

is there any way to encrypt the data being sent to the server so that the server can decrypt the data, execute and again encrypt the data and send response to the client….

That way it would be more secure….a 2 fold security can be implemented…

Todd,

Bottom line is yes, you should only have SOAP enabled on your service for this to work. If you absolutely have to enable POST for some unknown weird reason then you’ll have to make that apart of your message. Something like:

public class CreditRequest
{
string token;
string FirstName;
string Email;
string SSN;

string ETc….;
}

CreditResponse credit = proxy.PullCredit(myCreditRequest);

Remember that POST is POX (plain old XML), therefore they have to pass it in as part of the regular message.

So, if I allow HTTP-POST to the webservice, then I cannot use the custom SOAP header either, right?
You noted in the “why doesn’t this work section” that “The reason is you are not invoking the service via SOAP but rather just a standard POST from a form. Therefore, the SOAP extension is never going to fire.” So, you cannot really go back to the previous way outlined either because it also depends on the SOAP headers.
So if you are going to allow both SOAP and POST to your webservice, you will have to add the username and password to every method.
Or, am I missing something here. (Note I did try the previous way using the browser interface and the ServiceAuthHeader property is null)

do need add any sp elements in wsdl

Thank you so much for Nice concepts.

@Ghanshyam

Clarification real quick. You can attach credentials from the client side by adding them to the SoapHeader object in the client. The web service processes the soap header via the SoapExtension.

I wanted to clarify your first question because it didn’t make any sense to me the way it was worded.

In the client you would have code like this for your service:

MyService proxy = new MyService();
MyService.AuthHeader header = new MyService.AuthHeader();
header.Username = “foo”;
header.Password = “bar”;
proxy.AuthHeader = header;

proxy.CallSomeMethod();

This is what I mean by in the client you attach the credentials from the client side.

Hope that helps.

-Keith

@Arul

By you stating that Java is not sending the action header that tells me you are using Soap 1.1 protocol. I would highly suggest that you use Soap 1.2 protocol with the service and disable the 1.1 version. I have found this to be much easier for a vast majority of clients to work with than 1.1 since 1.1 requires the clients to send an action header.

I think that may help you. Hope that helps.

Hi, Is it possible to send in the credentials from the clientside by attaching them to the WebService call by SoapExtension? I have tried inserting header dynamically in BeforeSerialize stage in ProcessMessage, but the header shows up as SoapUnknownHeader on the WebService side in the AfterDeserialize stage of ProcessMessage. What I mean is that caller’s of the webservices need not attach the authheader value everytime themselves, rather the SoapExtension do it. Another thing, is it possible to send in also some other dynamic data like which is dependent on the current session using SoapExtension class. I could not find anything apart from using CurrentPrincipal. Please advise.

Hi, I am trying to use soapextension to intercept incoming request and append with Action header. Currently java client not sending the action along with the request so due to that getting an error “Action for Ultimate recipient is required not present”. So using soap extension to add that along with request but soap extension is not executed at all when i invoked the web service through XML Spy and other soap tools. I am not sure why.

If you really wanted to pass some type of access level for the method into the attribute, you could do something like this:

[MySecuritySoapExtension(AccessLevel=MyAccessLevel.Administrator)]

By doing something like this in your SoapExtension class:

private MyAccessLevel mAccessLevel = MyAccessLevel.User;

public override object GetInitializer(Type serviceType)
{
return mAccessLevel;
}

public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute)
{
return (MyAccessLevel)((AuthenticatonSoapExtensionAttribute)attribute).AccessLevel;
}

public override void Initialize(object initializer)
{
mAccessLevel = (MyAccessLevel)initializer;
}

And in your SoapExtensionAttribute class add a public property like so:
private MyAccessLevel mAccessLevel = MyAccessLevel.RegisteredUser;

public MyAccessLevel AccessLevel
{
get { return mAccessLevel; }
set { mAccessLevel = value; }
}

MyAccessLevel being an enum with all the levels you desire:
public enum MyAccessLevel
{
Administrator,
User
}

Then in your ProcessMessage you could pass in the access level as a parameter to do your validation during Authentication or whatever you like.

Your problem is probably simple. When you debug locally in VS, and you click on the submit form, you are in fact not doing a true Soap call, rather you are doing a straight post which does in fact by pass the soap authentication. Test it with code instead of the browser.

SOAP extension is never going to fire.

I decided to deploy it on real server for test SOAP extension. Running the client from my computer and it still pass the authentication with wrong password.

Please help me!

Thanks for your reply. Looking forward to reading your follow-up article. Hope it’ll be available soon…!

Sure, it isn’t that bad really just takes a bit of wiring up. For starters look at the last sample above starting at line 48. This method takes the SoapMessage as a parmeter.

From the SoapMessage you have a property called SoapMessage.MessageName. Instead of attributing the methods as you did like this:

[PermissionsAttribute(“admin”)]
void SetValue(int value);

I would simply use this property to determine the Method the user is requesting, and then do a lookup in the database to map their permissions. I’ll try to write up an example on how I would do this and get it posted, it is a good follow up article. Stay tuned!

First, thanks for a great article. It has certainly given me some ideas.

After your previous article you said:

“Not only does doing a SoapExtension provide automatic authentication, but it could also allow you to hook into a config file or database to validate each method differently dependent upon permissions.”

Now, I’d really be interested in something that would let me distinguish between different user groups, so that e.g. ‘admin’ has access to all functions, while ‘average-joe’ is limited to a certain subset, i.e.:

[PermissionsAttribute(“admin”)]
void SetValue(int value);

[PermissionsAttribute(“all”)]
int GetValue();

Any idea how to achieve this?

You’ve been kicked (a good thing) – Trackback from SecurityKicks.com

You’ve been kicked (a good thing) – Trackback from DotNetKicks.com

Write a comment