Webservice Authentication with Delphi for .NET Part I

By: Dave Nottage

Abstract: This article describes how to use the SoapHeader attribute to authenticate calls to your Delphi for .NET webservice

Introduction

Webservices have been around for a few years now, and I familiarized myself with them relatively early on. My work and other interests have meant that I’ve been on hiatus from webservices, however recently I had the need to write one to support our clients.

Since we’re hosting the webservice, I was able choose the development tool myself. Since I am most familiar with Delphi, I chose to write it using the Delphi for .NET personality of BDS 2006. This meant that it would be easy for me to develop, and I could learn some more about the .NET framework in the process.

Note: In this article, I’ll be focusing mainly on the authentication technique using SOAP headers. Please refer to the help and other articles available on creating webservices with Delphi for .NET.

Authentication techniques

Since the webservice needed to be relatively secure, I used some routines that manage a session id for the calls to the webservice, including a Login method on the webservice that returns the session id.

I started encountering problems with interop between the Win32 test client I wrote and the webservice, and so I had some discussions with Chee Wee Chua from Borland Tech Support about the problems. He noted that I was passing a session id for each call to the webservice and asked whether I had considered using a soap header for authentication. I vaguely recalled from years ago about soap headers, however I hadn’t thought of using them for authentication for this project.

SoapHeader attribute

When building webservices for .NET, it is possible to make any or all methods require a SoapHeader. Here’s an example in Delphi for .NET:

    [WebMethod]
    [SoapHeader('Auth', Direction = SoapHeaderDirection.InOut)]
    function GetData: TBytes;

Note that the method has the [WebMethod] attribute which indicates to .NET remoting that the method is to be published by the webservice. The [SoapHeader(‘Auth’, Direction = SoapHeaderDirection.InOut)] attribute signifies that the method is expecting a soap header for that method call, and the values for that header will be passed to a reference named Auth, which needs to be a descendant of SoapHeader. The Direction parameter value of InOut means that whatever values the Auth reference has will be passed from the client to the webservice, then passed back to the client when the result is sent.
Adding SoapHeaders to your webservice

For this webservice, it requires a username and password for authentication, so I created a descendant of SoapHeader that looks like this:

  TAuthHeader = class(SoapHeader)
  public
    UserName: string;
    Password: string;
    SessionID: string;
  end;

This is intended for a UserName and Password to be passed to the webservice, and a SessionID to be passed back to the client.

The attribute in the “SoapHeader attribute” section refers to a SoapHeader instance called Auth, which needs to be public, eg:

  [WebService(Namespace = 'http://mydomain.com /webservices')]
  TDotNetWebService= class(System.Web.Services.WebService)

  // Other code omitted for brevity

  public
    Auth: TAuthHeader;
    constructor Create;

  // Other code omitted for brevity

    [WebMethod]
    [SoapHeader('Auth', Direction = SoapHeaderDirection.InOut)]
    function GetData: TBytes;
  end;


Adding an authentication method

When a client calls the GetAppUpdateList method, .NET remoting automatically retrieves the properties for the soap header, in this case, the UserName and Password properties for Auth. Since I am using the same method for authenticating for every method call that requires it, I wrote a separate routine:

function TDotNetWebService.Authenticate: boolean;
var
  SessionID: string;
begin
  SessionID := Auth.SessionID;
  // SessionID is a var parameter, and is filled in by the Login routine
  Result := dmMain.Login(Auth.UserName, Auth.Password, SessionID);
  if Result then
  begin
    Auth.SessionID := SessionID;
    // Don’t pass the UserName and Password back
    Auth.UserName := '';
    Auth.Password := '';
  end
  else
    raise ELoginException.Create('Login failed', XmlQualifiedName.Create('Authentication'));
end;  

Having a single routine for authentication means that it can be replaced by some other process without the need for changing any other code within the webservice. The UserName and Password values are automatically filled by .NET remoting, as it looks for the required header and passes the values to the Auth reference.

Including authentication in your webservice methods

Now that there’s an authentication method, each webservice method that requires the method can call it, eg:

function TAppUpdatesService.GetData: TBytes;
begin
  SetLength(Result, 0);
  if Authenticate then
    Result := dmMain.GetData;
end;

As per the Authenticate method above, if the authentication fails, it throws a soap exception, so there’s no need to do anything here if the result is false.

More on security

If you’re paranoid about passing usernames and passwords unencrypted over the wire in SOAP packets, you could always host the webservice using SSL (if you’re willing to pony up for a certificate), or you could use encryption techniques at the client and server end for the password and/or username.
Conclusion

I hope this article sheds some light for you on using soap headers for authentication; researching and writing the article certainly has for shed some light for me, and it seems a far better solution than “polluting” (as someone I know has put it) your methods with passing a session id as a parameter for each method that requires authentication.

An example webservice in Delphi for .NET that uses this technique is available on CodeCentral here.

In the next installment of this series, I’ll be discussing creating webservice clients that use this authentication method, particularly Win32 clients, and all that it entails.

Acknowledgements

Thanks go to Chee Wee Chua (especially) from Borland Tech Support Asia Pacific, and Deepak Shenoy from TeamB for their help.

References:

SoapHeaderAttribute class on MSDN:

http://msdn2.microsoft.com/en-us/library/system.web.services.protocols.soapheaderattribute(VS.80).aspx


Server Response from: ETNASC03