We have a service that handles authorization based on a User Name and Password. Instead of making the username and password part of the call, we place it in the SOAP header.
In a typical scenario, a Web Service calls the Authorization service at the start of execution to check that the caller is allowed to call it. The problem is though that some of these Web services call each other, and it would mean that on every sub-call the user's permissions are checked, and that can be very expensive.
What I thought of doing was to have the Authorization service return a Security Token after the first call. Then, instead of having to call the Authorization service each time, the Web Service can validate the Security Header locally.
The Security Header looks something like this (C# code - trimmed to illustrate the essential concept):
public sealed class SecurityHeader : SoapHeader
{
public string UserId; // Encrypted
public string Password; // Encrypted; Just realized this field isn't necessary [thanks CJP]
public DateTime TimeStamp; // Used for calculating header Expiry
public string SecurityToken;
}
The general idea is that the SecurityHeader gets checked with every call. If it exists, hasn't expired, and the SecurityToken is valid, then the Web Method proceeds as normal. Otherwise it will either return an error, or it will attempt to re-authorize and generate a new SecurityHeader
The SecurityToken is based on a salted hash of the UserId, Password, and TimeStamp. The salt is changed every day to prevent replays.
The one problem I do see is that a user might have permission to access Web Service A, but not Web Service B. If he calls A and receives a security token, as it stands now it means that B will let him through if he uses that same token. I have to change it so that the security token is only valid from Web Service to Web Service, rather than User to Web Service ie. It should be OK if the user calls A which calls B, but not OK if the user calls Service A and then Service D. A way around that is to assign a common key (or a set of keys) to logically related services. (ie. if client can do A then logically he can do B as well).
Alternatively, I'd have to encode the user's entire permission set as part of the security header. I'll have to investigate what the overhead will be.
Edit:
Several people have mentioned looking at other security schemes like WS-Security and SAML etc. I already have. In fact, I got the idea from WS-Security. The problem is that other schemes don't provide the functionality I need (caching authorization information and protecting against replays without an intemediary database). If someone knows of a scheme that does then I will glady use it instead. Also, this is not about authentication. That is handled by another mechanism beyond my control.
If it turns out that there is no way to cache authorization data, then it means that I'll just have to incur the overhead of authorization at each level.