Even though a lot of free, public web services are available on the Internet at the moment, the majority of all web service development is still in creating web services that can only be used by a certain set of users. In other words, clients need to be able to prove their eligibility to consume the service.
Using WSE (Web Services Extensions), a downloadable tool library from Microsoft, you can take advantage of the WS-Security standard and have some extremely powerful security features. WSE is beyond the scope of this chapter and often provides more functionality than is desired for simple authentication of clients. This section shows you a very good pattern for authenticating clients in a secure way without impacting the performance of the web service.
Before taking a look at the final pattern, you should know about some of the other alternatives that usually are not chosen. The simplest (for the client) means of authenticating clients is to pass the username and password in the SOAP header to every single method call, and then each web service method would validate those credentials and refuse to perform the work if the credentials fail to verify. There are a number of problems with this pattern. The biggest is that passwords are being transmitted in clear text via XML over the Internet. To fix that problem and protect passwords, you would have to make every single method call to the web service over SSL, or hash the password using an algorithm known to the service. No matter what you do, a lot of unnecessary overhead is still incurred for every web service method. There is already a lot of natural latency that occurs as part of using web services, so adding further overhead to method calls is not a viable option.
Another option would be to use session state. The client would make a call to a web method that validates credentials. If the credentials are valid, the client has access to the rest of the service's functionality for the remainder of the session. On the surface, this might appear to be a fine solution. However, session cookies can be faked by malicious software intercepting communications between the service consumer and service provider and session state incurs an SOA penalty on the server; SOA relies heavily on the idea that services should be as stateless as possible.
The pattern that combines the best of the previous recommendations is one that uses a concept called a token store. One service, often called Login.asmx or Security.asmx, is only accessible via SSL. It has a method for validating user credentials. If the credentials are verified, this service returns an authentication token that is really just an arbitrary string. This token often takes the form of a GUID (Globally Unique Identifier). That GUID is then passed to subsequent non-SSL calls to the real service in the SOAP header. The web method then processes the SOAP header and determines if the token is a valid token. If the token is invalid, the method throws an exception and no work is done. Figure 33.4 shows a conceptual diagram of this model.
Figure 33.4. Using the token store web services authentication model.
The model illustrated in Figure 33.4 should be sufficient for most medium-security requirements. However, a problem still exists because an intruder can intercept a SOAP envelope containing a valid authentication token and reuse that token to send altered messages to the server. In this case, messages can be digitally signed using certificates that verify not only the authenticity of the sender of the message but also that the message was not tampered in transit. If your security situation requires tight constraints such as this, you should consider using WSE for your web service security needs. You can find more information about WSE at http://msdn.microsoft.com/webservices/webservices/building/wse/default.aspx. If you want to download WSE 3.0, the version of WSE that works in conjunction with ASP.NET 2.0, you can get the library at http://www.microsoft.com/downloads/details.aspx?familyid=018a09fd-3a74-43c5-8ec1-8d789091255d&displaylang=en.