Tuesday, August 12, 2008

Securing Web Services With Username and Password

keithelder's Blog.......

The article is short and doesn't give any examples of one of the methods I use a lot. I thought I would elaborate on one of the topics it touches on which is securing a web service with a Username and a Password. While there are other ways to secure web services I find this particular method works really well when dealing with internal systems that may not speak .Net. It is also simple to implement on both sides. These systems could be in Java, PHP or even 4GL. If you are a .Net developer and want to secure a web service with a username, password, or even other information, here is what you need to get going.

1. Create an object that extends SoapHeader


The first thing you need to do is create an object that extends the built-in SoapHeader object. The object can be simple or complex. Add the tokens you want to authenticate against. Here is a sample:

1 ///

2 /// Extends from SoapHeader and provides a username / password

3 /// for web methods that need authentication.

4 ///

5 public class ServiceAuthHeader : SoapHeader

6 {

7 public string Username;

8 public string Password;

9 }

2. Add a property to your service class that uses the class above


Once you've created your base soap header class, add it as a property to your service class.

// SoapHeader for authentication

public ServiceAuthHeader CustomSoapHeader;

3. Attribute the method you want to secure


Add the SoapHeader attribute to each or certain methods you wish to secure pass in the name of the property that is defined as the SoapHeader object in step 2.

1 [WebMethod]

2 [SoapHeader("CustomSoapHeader")]

3 public int AddTwoNumbers(int x, int y)

4 {

5

6 return x + y;

7 }

4. Create a method to process your SoapHeader for authentication

The last big step is to create a static method that will take in your custom soap header class and process it for validation. The first thing we want to do is make sure that some type of credentials were passed in the SoapHeader and that the properties we are looking for are not null. Finally we want to validate each property contains the information we are looking for. This could be read from the web.config file, database, or other places.

1 public class ServiceAuthHeaderValidation

2 {

3 ///

4 /// Validates the credentials of the soap header.

5 ///

6 ///

7 public static bool Validate(ServiceAuthHeader soapHeader)

8 {

9 if (soapHeader == null)

10 {

11 throw new NullReferenceException("No soap header was specified.");

12 }

13 if (soapHeader.Username == null)

14 {

15 throw new NullReferenceException("Username was not supplied for authentication in SoapHeader.");

16 }

17 if (soapHeader.Password == null)

18 {

19 throw new NullReferenceException("Password was not supplied for authentication in SoapHeader.");

20 }

21

22 if (soapHeader.Username != "myusername" || soapHeader.Password != "mypassword")

23 {

24 throw new Exception("Please pass the proper username and password for this service.");

25 }

26 return true;

27 }

28 }

5. Add validation to service method

1 [WebMethod]

2 [SoapHeader("CustomSoapHeader")]

3 public int AddTwoNumbers(int x, int y)

4 {

5 // Test to see if the proper credentials were passed in.

6 ServiceAuthHeaderValidation.Validate(CustomSoapHeader);

7

8 // If we get this far the user has been validated.

9 return x + y;

10 }

That's it. You now have all the pieces of the puzzle to process a request and validate the credentials of the calling client via a username and or password. If we launch the solution we will see that our XML for our service has been updated and now contains an XML Node called ServiceAuthHeader which contains two sub nodes: username, password.

Passing SoapHeader Credentials To Your Service

Now that we have our service secured, we need to now call the service and pass the credentials expected from a client. Based on the example above, once you add a web reference to the service and instantiate the service in code, the thing you want to look for is a new property of your service proxy called ServiceAuthHeader. This is converted into a property called ServiceAuthHeaderValue. This property needs to be an instantiation of the ServiceAuthHeader class where you set the username and password properties. Here is an example of a console application calling our service and passing the required information to authenticate.

1 using System;

2 using System.Collections.Generic;

3 using System.Text;

4

5 namespace ConsoleApplication1

6 {

7 class Program

8 {

9 static void Main(string[] args)

10 {

11 localhost.Service service = new ConsoleApplication1.localhost.Service();

12 localhost.ServiceAuthHeader header = new ConsoleApplication1.localhost.ServiceAuthHeader();

13 header.Username = "myusername";

14 header.Password = "mypassword";

15 service.ServiceAuthHeaderValue = header;

16 int x = service.AddTwoNumbers(1, 1);

17 Console.WriteLine(x);

18 Console.ReadLine();

19 }

20 }

21 }

The return result will be 2 of course and from the client side it is rather trivial to pass the credentials as you see. That's it. Happy authenticating!

[Related Link]
To take authenticating one step further with a custom SoapExtension read this follow up article.
http://keithelder.net/blog/archive/2007/01/09/Take-Securing-Web-Services-With-Username-and-Password-One-Step.aspx

No comments: