For my web app security I\'m using FormsAuthentication / MembershipProvider with a non persistant cookie.
My application interacts with some web services, these also use
I agree with @John's answer that using throwaway token is better than storing the credentials.
For the token you could generate some random GUIDs and store it in the database.
As an alternative that does not require coordination between your ASP.NET application and the WCF service, you could send a signed document as token.
All WCF has to do is validate the hash and the signature. So this does not involve hitting the same database. Using the signed time, you can expire the token in fixed time.
Edit: The idea is based on public-key cryptography (also known as asymmetric key algorithm, public/private key). If you encrypt something with a private key you can decrypt it back only using the corresponding public key; and if you encrypt something with a public key you can decrypt it only using the corresponding private key. See Implementing RSA in C# for how code would look like in C#. Why is this useful? Because we can use this to implement digital signatures. A digital signature is a way to prove that I and only I wrote something, and no one else.
Following the above mentioned step generates a signature. You first need to define a canonical form of "let this guy in" document. Usually an asymmetric key algorithm can't handle too big input, so you generate a hash out of it, and you encrypt the hash using your ASP.NET application's private key. The resulting signature can only be decrypted using you application's public key. Finally you can package all three components (original document, hash, and signature) into some format like XML or JSON and send it as token.
As an example, let's say you use JSON format for everything. First, the original "let this guy in document":
{"UserName":"Foo","SignedTime":"2009-07-09T00:00:00","Signer":"ASP.NET APP1"}
Next, you generate a SHA-1 hash of the above string, which is byte[]
and encode it with modified Base64 encoding or something, which would look something like:
b2YgYW55IGNhcm5hbCBwbGVhc3VyZS4
The above is bogus string, the actual stuff may look longer. You then take the hash byte[]
and encrypt it using RSA, which generates another byte[]
so encode that too with modified Base64:
mxlIGdlbmVyYXRpb24gb2Yga25vd2xfo34
Finally, you make another JSON document to store all the above.
{"UserName":"Foo","SignedTime":"2009-07-09T00:00:00","Signer":"ASP.NET APP1","Hash":"b2YgYW55IGNhcm5hbCBwbGVhc3VyZS4","Signature":"mxlIGdlbmVyYXRpb24gb2Yga25vd2xfo34"}
The final JSON document becomes your passwordless token. Pass it to WCF service. The WCF service takes the token, construct the original document by removing the hash and signature:
{"UserName":"Foo","SignedTime":"2009-07-09T00:00:00","Signer":"ASP.NET APP1"}
Follow the same algorithm to generate the hash and verify it's the same. Decrypt the Signature using the public key of the ASP.NET app and see if it becomes the hash. At this point, the document is verified to be signed by the signer. Check the current time and the signed time and see if the token is still valid. All you need is a way to distribute public keys between two code base, which could be loaded from XML.
The best practice would be to not require the user to authenticate with his username and password on every request.
Instead, on the first authentication, the web service should return some kind of authentication token. This is what should be stored somewhere. I would recommend storing it in Session state, rather than in the forms authentication ticket.
When the ticket from the web service expires, you might consider expiring the Forms Authentication ticket as well, which would cause the user to need to log in to your site again, providing username and password, which you would validate, and then use to authenticate to the web service again, storing the ticket from the web service, etc.
On the web application you set up the Authenticate event of the Login control to instantiate a new service proxy and set the username/password in the ClientCredentialsin the proxy.
Now when you make the call to the Service through the proxy WCF will pass these credentials through the secure channel to the service and use them for authentication.
Now you simply need to store the proxy in session and use it for future access to the service as it has the channel state and a private key.
protected void LoginControl_Authenticate(object sender, AuthenticateEventArgs e)
{
bool Authenticated = false;
try
{
MyServiceClient proxy = new MyServiceClient("MyServiceEndpoint");
proxy.ClientCredentials.UserName.UserName = LoginControl.UserName;
proxy.ClientCredentials.UserName.Password = LoginControl.Password;
//It doesn't really matter what is called or what it does because
//Membership Provider for the Service does the authentication.
string retval = proxy.login("Logging in");
//Now that channel is established the proxy needs to be kept
//since it contains the channel state which includes a private key
Session["MyServiceProxy"] = proxy;
Authenticated = true;
}
catch (Exception ex)
{
//Login Error...
}
e.Authenticated = Authenticated;
}