World.NET
About .NET
Data integrity check using hashing
Oct 31st
Introduction
When data is moved across a network, the recipient might require a verification to ensure that data has not been modified after send. For example, in a client/server scenario where a client application sends some updates to a remote database via a web service, the web service might need to verify whether the data has not been modified while travelling across the network, either due to corruption or because somebody changed them. A simple way to implement such kind of verification is utilizing hashing.
How Hashing works
The idea behind hashing is to generate a (ideally) unique and non–reversible code generated from the data being sent. Unique means that the codes generated for 2 different sets of data must differ each other, even if each set of data has a size of 1 Mb and they differ by 1 byte only. Non-reversible means that the hash code cannot be used to recreate the original data.
For the purpose of signing data to check for modification, the non-reversibility feature is unnecessary, as the hash code travels with the data, but there are other cases where it is undoubtedly useful. For example, one of the easiest way to prevent passwords from being stored in a database is to store a hash code rather than the password. This way when a user is authenticating the password is used to generate an hash key, which is compared to the hash key stored in the database (previously generated when the user account has been created, or the last time the user has changed his password) – if they match, authentication succeeds. The drawback of this method is that a “recall password” feature cannot be implemented, as the password is not stored anywhere, and the non reversibility property of the hash code prevents it from being used to recreate the original data – in such case a “reset password” feature is more appropriate.
Hashing and Salting
Using the model described above data is protected against corruption but not against changes. In fact, whereas a corruption is immediately detected once the recipient verifies that the hash codes don’t match, an hacker attempting to modify the data simply has to create a new hash code. Since this code is sent with the data, and since the hashing algorithm is deterministic, whoever wants to modify the data has all ingredients to do so, with no risk of being detected.
So hashing is not enough to guarantee data integrity. One common practice is to use a random or predefined string, which is appended to the data before calculating the hash code. This additional string is known as salt. If the salt is known by sender and recipient only, an hacker has no way to generate an hash code that won’t be immediately detected by the recipient during hash verification.
In this scenario the salt can be thought as a private key shared between sender and recipient.
For improved security, it’s better if the salt is not 100% static – for example, a dynamic string depending from the date and/or time can be mixed to the static salt in order to generate a dynamic salt, harder for an hacker to guess. For this method to work both sender and recipient must generate the same salt in a wide enough time window. For example, supposing that sender and recipient have their clocks synchronized, if the salt is composed by current hour and minute, the widest time window is 60 seconds – but the recipient could generate a different salt just 1 second after the sender generated the salt code, if this occurs, for example, at 10:20:59. In this case the sender use 10:20 to generate the salt – if the recipient receives the message at least 1 second later, it will use 10:21 to generate the salt. And this obviously leads to a completely different hash code.
Implementing a data integrity check
There are several algorithms that can be use to generate hash codes. One of them is MD5, implementable as follows:
using System;
using System.Security.Cryptography;
using System.Text;
namespace Hash
{
public class HashGenerator
{
public static string GenerateMd5Hash(string salt, string data)
{
byte[] buffer;
byte[] binaryHash;
string stringHash;
MD5 md5;
md5 = MD5.Create();
// Combine salt + data and convert to array of bytes
buffer = Encoding.Unicode.GetBytes(salt + data);
// Create the hash code from salt + data
binaryHash = md5.ComputeHash(buffer);
// The hash code is in binary format - convert
// it in readable string
stringHash = Convert.ToBase64String(binaryHash);
return stringHash;
}
}
}
The code is pretty simple: salt + data are combined together and converted to array of bytes, then the ComputeHash method of the MD5 instance creates the hash code given the array of bytes. Last, since the hash generated by ComputeHash is in binary format, it is converted to string and returned. To test this code we can create a simple console application like the following one:
using System;
using Hash;
namespace HashSample
{
public class Program
{
static void Main(string[] args)
{
string test;
string hash;
string salt;
test = "This is a sample data";
salt = "FTSA74D71KSAP";
hash = HashGenerator.GenerateMd5Hash(salt, test);
Console.Out.WriteLine(
"The hash code for string '{0}' and salt '{1}' is:\n\t{2}",
test, salt, hash
);
Console.In.Read();
}
}
}
The output produced by this application is the following:
The hash code for string 'This is a sample data' and salt 'FTSA74D71KSAP' is:
s2gxEi9C3QSRGFy+cZheWA==
To prove how the hash code changes by simply changing 1 byte in the original data, let’s see what happens if we slightly change the test string to “This is b sample data”:
The hash code for string 'This is b sample data' and salt 'FTSA74D71KSAP' is:
FJr7RUcSYJMJBdfCg6DCXQ==
We can see that these 2 hashes are completely different.
IHashable interface
If we need to generate an hash code on instances of a class, why don’t we define a proper interface? This way we can just implement the interface and write a single function to generate the hash code. The interface name is, of course, IHashable:
public interface IHashable
{
string GetHashableRepresentation();
string GenerateSalt();
}
and consists of 2 methods: GetHashableRepresentation and GenerateSalt. The latter should be implemented in order to generate a proper salt code, whereas the former should return a representation of the class instance in a string format suitable for hashing – for example, a simple string concatenation of all data members.
As I mentioned earlier, there are several algorithms usable for hashing. Following a comprehensive implementation of the hash generation, which allows the caller to choose which algorithm to use:
public enum HashingAlgorithm
{
Md5,
Sha1,
Sha256,
Sha384,
Sha512,
RipeMd160
}
public static string GenerateHash(HashingAlgorithm alg, IHashable hashable)
{
HashAlgorithm algorithm;
algorithm = CreateHashAlgorithm(alg);
byte[] buffer;
byte[] binaryHash;
string stringHash;
// Combine salt + data and convert to array of bytes
buffer = Encoding.Unicode.GetBytes(hashable.GetHashableRepresentation() + hashable.GenerateSalt());
// Create the hash code from salt + data
binaryHash = algorithm.ComputeHash(buffer);
// The hash code is in binary format - convert
// it in readable string
stringHash = Convert.ToBase64String(binaryHash);
return stringHash;
}
private static HashAlgorithm CreateHashAlgorithm(HashingAlgorithm alg)
{
HashAlgorithm algorithm;
switch (alg)
{
default:
case HashingAlgorithm.Md5:
algorithm = MD5.Create();
break;
case HashingAlgorithm.Sha1:
algorithm = SHA1.Create();
break;
case HashingAlgorithm.Sha256:
algorithm = SHA256.Create();
break;
case HashingAlgorithm.Sha384:
algorithm = SHA384.Create();
break;
case HashingAlgorithm.Sha512:
algorithm = SHA512.Create();
break;
case HashingAlgorithm.RipeMd160:
algorithm = RIPEMD160.Create();
break;
}
return algorithm;
}
The GenerateHash method requires 2 parameters: the algorithm to use and an instance of IHashable. In return, the method provides an hash code generated using the specified algorithm.
Last thing to mention, a helpful method to compare an hash code with an IHashable instance – its implementation is really very simple:
public static bool CompareHashes(HashingAlgorithm alg, string hash, IHashable hashable)
{
return GenerateHash(alg, hashable).Equals(hash);
}
Digital signature
When used alone, hashing is good but it’s not the best. There are better ways, out of the scope of this article – but I want to spend a few words about them.
The safest and more secure way to sign data is using digital signature, which make use of asymmetric cryptography (also known as public key cryptography). The idea is to encrypt the hash code so that it cannot be modified.
In cryptography a key is used to encrypt and decrypt data – it’s not an algorithm, but a secret data used for encryption: using a different key different encrypted data is generated. A (very simple) example of key is the salt we’ve seen earlier: changing the salt will produce a different hash code.
Keys are usually used to encrypt and decrypt data. In symmetric cryptography the same key is used to encrypt and decrypt data. This is the biggest limitation, as the key must be owned by both parties involved in the encryption and decryption process, or sent along with the encrypted data if the recipient doesn’t have the key.
In public key cryptography a key is composed by a public key and a private key. The private key, as the name implies, is private and should never be sent to anybody. On the other hand, the public key can be published and made available to anybody. The key owner uses his private key to encrypt data – anybody can decrypt the data using the public key, but the cool feature is that the public key can be used to decrypt data encrypted by the corresponding private key only. This means that who is decrypting is sure that data has been encrypted by the owner of the private key only. Keys can also be used in the opposite way: data encrypted with the public key can only be decrypted by the corresponding private key. This way whoever encrypts data using the public key is sure that data will be available to the owner of the corresponding private key only.
In our hashing problem, if the sender encrypts the hash code using his private key, the recipient is able to detect any unauthorized data modification:
- if the data gets corrupted or changed, the hash code calculated from the data won’t match with the encrypted hash code provided with the data
- if the encrypted hash code provided with the data is modified, it won’t match with the hash code calculated from the data
Mark a C# class data member as readonly when it’s read only
Oct 1st
The readonly modifier is used to declare a constant data member in a class, but providing more flexibility than using the const modifier. The const modifier implicitly defines the data member as static, and must be initialized along with its declaration, as follows:
public const int myConst = 5;
If the const is not initialized along with the declaration, the compiler will generate an error.
Moreover, a const data member must be initialized with a compile-time constant – in other words there is no way to initialize a constant using a runtime value. For instance, the following code doesn’t compile:
public class MyClass
{
public static string nonConstValue = "This is a variable initialized at runtime";
public const string constValue = nonConstValue; // Compilation error
...
}
as the compiler will inform that “The expression being assigned to ‘MyClass.constValue’ must be constant”.
The advantage of defining a data member as const is that it cannot be changed – the drawback being the inability to initialize the field in a different place other than the declaration.
On the other hands, the readonly modifier extends the const modifier by allowing the data member to be initialized in any class constructor. This allows a data member to be initialized when a class is being instantiated, preventing it from being modified in any other class method – in other words, once the const data member is initialized it never changes during the class instance lifetime. As opposed to the const modifier, the readonly modifier creates an instance-specific data member, so each class instance will have its own readonly data member instances. This doesn’t mean that a readonly data member cannot be static – simply it must explicitly be declared as such.
All the following declarations and initializations are valid:
public class MyClass
{
public readonly int _myIntField = 5; // Allowed
public readonly string _myStringField; // Allowed
public MyClass()
{
_myIntField = 8; // Allowed
_myStringField = "Initialized in the constructor"; // Allowed
}
public static void Main()
{
MyClass myClass;
myClass = new MyClass();
System.Console.Out.WriteLine(string.Format("myIntField = {0}", myClass._myIntField));
System.Console.Out.WriteLine(string.Format("myStringField = {0}", myClass._myStringField));
}
}
The member declared at line 4 is initialized in the constructor with a string constant, whereas the int member declared at line 3 is initialized both in the declaration and in the constructor. In this case, the constructor initialization takes precedence, as it is executed after the data member instantiation.
The following case instead is not valid
public class MyClass
{
public readonly string _myStringField; // Allowed
public MyClass()
{
Init();
}
private void Init()
{
_myStringField = "Cannot be initialized here"; // Compilation error
}
...
}
That’s because the readonly member is initialized in a class member which is not a constructor, even if this method is called from the constructor. The reason why this is not allowed is that the Init() method can be called from anywhere within the class, or even outside if it is protected, internal or public rather than private.
Another advantage of the readonly modifier is that it can be initialized with a runtime value or an expression evaluated at runtime, for instance:
public class MyClass
{
public readonly int _myIntField; // Allowed
public MyClass(int runtimeValue)
{
_myIntField = 10 * runtimeValue;
}
public static void Main()
{
MyClass myClass;
myClass = new MyClass(5);
System.Console.Out.WriteLine(string.Format("myIntField = {0}", myClass._myIntField));
}
}
As we can expect, the _myIntField readonly data member will be initialized to 10 * 5 = 50.
Readonly data members are not limited to basic data types – it can be used for class instances. For example, when writing a database accessor class we might want to provide a database connection:
public class MyDatabaseAccessor
{
private readonly SqlConnection _connection;
public MyDatabaseAccessor (SqlConnection connection)
{
_connection = connection;
...
}
...
}
public class MyApp
{
public static void Main()
{
SqlConnection connection = new SqlConnection();
// Initialize the connection
...
MyDatabaseAccessor myAccessor = new MyDatabaseAccessor(connection);
// Do something
...
}
}
The above code would work without declaring the _connection data member as readonly – but in such case the connection can be changed anywhere in the class.
It’s important to keep in mind that when a readonly data member is of a reference type (i.e. instance of a class and not either a struct or a base data type) the reference is constant, but the instance it points to is not. So the internal status of a readonly instance can change and can be changed, as proven by the following sample:
// Class with a data member
public class MyClassType
{
private string _internalStatus;
public string InternalStatus {
get {return _internalStatus;}
set {_internalStatus = value;}
}
}
// Class with a readonly data member
public class MyClassWithReadonlyMember
{
private readonly MyClassType _readonlyInstance;
public MyClassWithReadonlyMember(string value)
{
_readonlyInstance = new MyClassType();
_readonlyInstance.InternalStatus = value;
}
public void SetStatus(string message)
{
_readonlyInstance.InternalStatus = message;
}
public void OutputStatus()
{
System.Console.Out.WriteLine(string.Format(
@"Internal status = '{0}'", _readonlyInstance.InternalStatus));
}
}
public class MyApp
{
public static void Main()
{
MyClassWithReadonlyMember classInstance;
// Create an instance of MyClassWithReadOnlyMember class
// The instance of MyClassType is initialized to the default
// value provided in the constructor
classInstance = new MyClassWithReadonlyMember("Initial value");
classInstance.OutputStatus();
// The internal status of the readonly data member of
// MyClassWithReadonlyMember is changed
classInstance.SetStatus("New Value");
classInstance.OutputStatus();
}
}
If this code is compiled and run. it will generate the following output:
Internal status = ‘Initial value’
Internal status = ‘New Value’
2009.01.30 Addendum
Thanks to what Chris Marisic highlighted in his comments below, I need to clarify a few points to prevent some misleading assumptions.
#1 – readonly members can be modified using reflection. The readonly modifier is a compiler directive, so any attempt to modify a readonly data member is detected during compilation. A consequence is that using reflection it’s possible to modify a readonly data member
#2 – I wrote above that the const modifier "allows a data member to be initialized when a class is being instantiated, preventing it from being modified in any other class method". This statement doesn’t mean that the data member is immutable, at least not always. .NET supports two kinds of data types: value types and reference types.:
- value types are allocated on the thread’s stack and they hold the actual value
- reference types are allocated on the thread’s stack and they hold a pointer to an object allocated in the managed heap
In both cases variables are allocated on the thread’s stack, the difference being that a value type holds the actual value (for example, an integer, a struct), whereas the reference type holds a pointer to the memory area where the actual value is stored (for example, an instance of a StringBuilder class).
When a value type is declared as readonly, its value is immutable, meaning that once assigned it cannot be modified outside constructors.
When a reference type is declared as readonly, the pointer is immutable, but not the object it points to. This means that:
- a reference type data member can be initialized in order to point to an instance of a class, but once this is done it’s not possible to make it point to another instance of a class outside of constructors
- the readonly modifier has no effect on the object the readonly data member points to.
Maybe a real example helps to better understand the difference. Let’s create a simple class and a simple struct. They have one int data member only.
public class MyClass
{
public int IntField;
}
public struct MyStruct
{
public int IntField;
}
Now let’s use them in a test class:
public class MyTestClass
{
private readonly MyClass _myClass;
private readonly MyStruct _myStruct;
public MyTestClass()
{
_myClass = new MyClass();
_myClass.IntField = 4;
_myStruct.IntField = 5;
}
}
The _myClass data member is a reference type, so it must be initialized by allocating a new instance of MyClass and assigning its pointer, as seen at line 8. On the other hand, the _myStruct data member is a value type, meaning that it is already an instance of MyStruct. Initialization of their respective fields are done at lines 9 and 11.
Let’s add a method to the MyTestClass class.
public class MyTestClass
{
private readonly MyClass _myClass;
private readonly MyStruct _myStruct;
public MyTestClass()
{
_myClass = new MyClass();
_myClass.IntField = 4;
_myStruct.IntField = 5;
}
public void TestReadonly()
{
_myClass.IntField = 7; // Valid statement
_myStruct.IntField = 10; // Error: _myStruct is readonly
_myClass = null; // Error: _myClass is readonly
}
}
If we try to compile this code, we get 2 errors:
MyTest.cs(26,3): error CS1648: Members of readonly field ‘MyTestClass._myStruct’
cannot be modified (except in a constructor or a variable initializer)
MyTest.cs(27,3): error CS0191: A readonly field cannot be assigned to (except ina constructor or a variable initializer)
MyStruct is a struct, hence a value type; _myStruct is immutable since declared as readonly, so any of its data members cannot be modified outside of a constructor.
MyClass is a class, hence a reference type; _myClass is immutable since declared as readonly, so it cannot be modified in order to point to another instance of a class (or to null) outside of a constructor.
DataGridView: how to bind nested objects
Jul 18th
The Problem
A DataGridView can be bound to a collection of objects to display any of its base data type (i.e. string, int, and so on) properties. For example, a collection of instances of the following class:
public class Customer
{
public string FirstName { get; set;}
public string LastName { get; set;}
public string Street { get; set;}
public string PostalCode { get; set;}
public string City { get; set;}
}
can be used to display any of the Customer class properties in the DataGridView.
But what happens when the class exposes other objects which are not base data type? If an Address class is created and used as a property of the Customer class:
public class Address
{
public string Street { get; set;}
public string PostalCode { get; set;}
public string City { get; set;}
}
public class Customer
{
public string FirstName { get; set;}
public string LastName { get; set;}
public Address Address { get; set;}
}
a DataGridView bound to a collection of Customer instances is not able to display any of the Address properties. In such cases the displayed cells are empty
The Solution
In order to enable the DataGridView to display properties exposed by class members, some custom development is required. The first step is to implement the DataGridView’s CellFormatting event handler
private void gridCustomers_CellFormatting(
object sender,
DataGridViewCellFormattingEventArgs e)
{
...
}
The CellFormatting event handler needs to differentiate whether the field to be displayed is one of the base data type or an object exposing its own properties. This is achieved by looking for the dot character ‘.’ in the Column‘s DataPropertyName field:
private void gridCustomers_CellFormatting(
object sender,
DataGridViewCellFormattingEventArgs e)
{
if ((gridCustomers.Rows[e.RowIndex].DataBoundItem != null) &&
(gridCustomers.Columns[e.ColumnIndex].DataPropertyName.Contains(".")))
{
...
}
}
If the DataPropertyName field doesn’t contain a dot character, then the actual value of the base data type property must be displayed – in this case no action is required, since it is already filled in e.Value.
On the other hand, if the DataPropertyName field contains one or more dot characters, then it points to a property exposed by one of the bound class properties. For example, Address.Street contains the dot character, and it points to the Street property of the Address property inside a Customer’s instance.
To handle this cases, a recursive function BindProperty is used:
private void gridCustomers_CellFormatting(
object sender,
DataGridViewCellFormattingEventArgs e)
{
if ((gridCustomers.Rows[e.RowIndex].DataBoundItem != null) &&
(gridCustomers.Columns[e.ColumnIndex].DataPropertyName.Contains(".")))
{
e.Value = BindProperty(
gridCustomers.Rows[e.RowIndex].DataBoundItem,
gridCustomers.Columns[e.ColumnIndex].DataPropertyName
);
}
}
The BindProperty function resolves the data property name and provides the actual value to be displayed in the grid’s cell by using reflection and (if required) recursion. Two arguments are passed to the BindProperty function: the class property value (which is an instance of a class, in the above example an instance of the Address class) and the DataPropertyName:
private string BindProperty(object property, string propertyName)
{
...
}
The first thing to check is whether the property name contains the dot character – although this never happen when called directly from the CellFormatting event handler, since the if statement prevent this, it may happen when BindProperty calls itself recursively.
private string BindProperty(object property, string propertyName)
{
if (propertyName.Contains("."))
{
...
}
else
{
...
}
}
If the property name doesn’t contain any dot character, then the propertyName variable contains the name of the property object to be displayed in the grid. Reflection is used to read the property value, obtained by retrieving the PropertyInfo from the property variable, and then getting the property value by calling the GetValue() method of the PropertyInfo instance:
private string BindProperty(object property, string propertyName)
{
string retValue = "";
if (propertyName.Contains("."))
{
...
}
else
{
Type propertyType;
PropertyInfo propertyInfo;
propertyType = property.GetType();
propertyInfo = propertyType.GetProperty(propertyName);
retValue = propertyInfo.GetValue(property, null).ToString();
}
}
This completes the else branch. As for the if branch, it is executed when the property name contains at least one dot character. In this case, still using reflection, the PropertyInfo of the desired property is retrieved, and using recursion, passed to the same BindProperty function.
private string BindProperty(object property, string propertyName)
{
string retValue = "";
if (propertyName.Contains("."))
{
PropertyInfo[] arrayProperties;
string leftPropertyName;
leftPropertyName = propertyName.Substring(0, propertyName.IndexOf("."));
arrayProperties = property.GetType().GetProperties();
foreach (PropertyInfo propertyInfo in arrayProperties)
{
if (propertyInfo.Name == leftPropertyName)
{
retValue = BindProperty(
propertyInfo.GetValue(property, null),
propertyName.Substring(propertyName.IndexOf(".") + 1));
break;
}
}
}
else
{
Type propertyType;
PropertyInfo propertyInfo;
propertyType = property.GetType();
propertyInfo = propertyType.GetProperty(propertyName);
retValue = propertyInfo.GetValue(property, null).ToString();
}
return retValue;
}
The leftPropertyName variable holds the name of the leftmost property, for instance if propertyName is Address.Street, it is filled in with Address. By looping through the array of PropertyInfo of the property object, the PropertyInfo instance of the leftPropertyName property is retrieved. Then, BindProperty is called again, passing the instance of the leftPropertyName property and the right part of propertyName (for instance, for Address.Street the provided property name is Street).
Using the above code, this is the result:
The code is available for download here (registration is required)
Single Sign On across multiple ASP.NET applications
Oct 1st
Machine Key
The machineKey element of the web.config file is used to specify keys for encryption and decryption of forms authentication cookies and view state data, and also when dealing with out of process session state.
The default configuration uses auto generated decryption key and validation key, and SHA1 encryption type.
Using the default configuration, different web applications have different decryption and validation key, since they are randomly generated. To force 2 applications to use the same keys, they must be explicitly defined in the web.config file
A complete description of the machineKey element is available here
Single Sign On
Single Sign On (SSO) is a term used to indicate when a pool of applications need a centralized authentication, so that users login once and access to any application.
Implementing a single sign on is quite simple, and can be done by configuring the applications using the web.config file.
A default configuration for forms authentication is defined as follows:
<configuration>
...
<system.web>
...
<authentication mode="Forms">
<forms name=".cookiename"
loginUrl="~/Login.aspx"
timeout="30"
path="/" />
</authentication>
...
</system.web>
...
</configuration>
Form Authentication
Forms Authentication uses an authentication ticket stored in either a cookie or embedded in the url.
When used in cookie mode, the cookie contains authentication data, encrypted so that data can be read by the application who has created the cookie.
Cookies are associated to 2nd level domains (example.com), and can be accessed from any 3rd level domain (www.example.com, test.example.com, and so on).
where .cookiename, by default, is .ASPXFORMSAUTH.
In order for authentication data to be recognized across multiple applications, each application must be configured to use the same values for cookie name, protection and path attributes. But this isn’t enough – in fact, they must also have the same machine key values (see the side box for more info about the machineKey element). These information are used to encrypt the forms authentication cookie, as mentioned in the "Forms Authentication" side box.
Below a sample web.config excerpt which must be added to each application we want single sign on enabled. In this sample, the validationKey and encryptionKey attributes must be replaced with unique values you have to generate for your applications pool.
<configuration>
...
<system.web>
...
<authentication mode="Forms">
<forms name=".cookiename"
loginUrl="~/Login.aspx"
timeout="30"
path="/"
/>
</authentication>
...
<machineKey
validationKey=
"F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902"
encryptionKey=
"F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902F8D923AC"
validation="SHA1"
/>
...
</system.web>
...
</configuration>
Second and third level domains
If the cooperating applications are installed under the same 2nd and 3rd level domain, but on different virtual folders, then no additional code is required.
If applications are installed on different second level domains (www.domain1.com and www.domain2.com), the SSO method described in this article won’t work, since cookies cannot be read by applications under different second level domains. For example, if application A in domain1.com issues a cookie, the cookie may be read by A itself and any application hosted under www.domain1.com and any other 3rd level domain (test.domain1.com, beta.domain1.com, and so on). Application B in domain2.com isn’t able to read the cookie, since it is hosted under a different second level domain.
If cooperating applications are installed on different third level domains, then we need to add some code in order to make SSO work. The code simply has to add the domain name to the authentication cookie, as outlined below
protected void Login (string strUserName, ...)
{
...
System.Web.HttpCookie cookie;
cookie = FormsAuthentication.GetAuthCookie(strUserName, False);
cookie.Domain = "domain1.com";
cookie.Expires = DateTime.Now.AddDays (-1);
Response.AppendCookie (cookie);
...
}
Cookie Expiration
If different applications set different cookie expirations, the actual expiration value is the one set by the application which issued it. So if application A is configured to set an expiration of 1 hour and application B 2 hours, and the user signs in using application B, then the cookie expiration is set to 2 hours.
Logging out
Usually, in order to log out a user, a call to the Authentication.SignOut() method is used – this isn’t enough when using SSO. In order to perform a single sign out, the quickest way is to set the cookie expiration to a past date – this ensures that the cookie won’t be used by any application for authentication.
protected void Logout (string strUserName)
{
System.Web.HttpCookie cookie;
cookie = FormsAuthentication.GetAuthCookie(strUserName, false);
cookie.Domain = "domain1.com";
cookie.Expires = DateTime.Now.AddDays (-1);
Response.AppendCookie (cookie);
}
Integrating Web Applications
What said so far is valid if applications use the same database to store user profiles. But what if 2 applications use each one their own database?
In this case, the SSO works, but sooner or later one of the applications will throw exceptions due to missing data in its database. If a user registers in application A, once he signs in he can access to application B – but he never registered in application B, so application B doesn’t have this user in its database.
This is the case when, for example, we have to integrate 2 existing applications, which already have their own authentication and registration implemented.
To solve this problem, we have 2 choices:
- modify both application in order to use a single authentication and registration process, and having a shared user profile repository
- choose one application as the master application, and remove the authentication and registration process from the other application (the slave application)
We’ll focus on the second solution.
This method requires that the database used by slave applications is accessible by the master application. This can be achieved by either:
- Create a single database which holds both application databases. In this case it would be good to use different prefixes for database entities to avoid naming conflicts – this could happen if both databases have a Users table. If we choose mst and slv as prefixes, we should rename the Users table to mst_Users for the master database and slv_Users for the slave database. This requires that we modify the source code and stored procedures.
- Use 2 different databases, but the master application must be able to access to the slave’s database.
Authentication should be performed in the following way:
- The user accesses to the master application, and signs in
- The master application verifies the user’s credentials
- The master application verifies whether the logged user is defined in the slave database – if not, accesses to the slave’s database and creates the new user
- The master application calls (if existing) a slave’s stored procedure which performs post-authentication processing (such as setting a "logged in" field, inserting a new row in a history table, and so on)
- The master application generates the SSO cookie
User profile creation on the slave database requires that:
- the master application is able to access to the slave’s database
- the slave’s database exposes a stored procedure which handles user registration (we may need to write it by ourselves)
The second requirement isn’t mandatory, since it could also be achieved by using inline SQL – but I usually prefer the stored procedure solution.
Final touch
There would be a few final things to do on the slave application:
- Removal of all login links
- Replacement of logout links with a "Back to the Master application" link
- Replacement of all "User’s profile" links to point to the master application user’s profile page
These steps ensure that navigation is consistent with integration – we’re supposing that all user’s info (credentials, profile, user’s preferences) are handled by the master application, so we need to modify the slave application accordingly. It is responsibility of the master application to update user’s profile in the slave application.
What if different cookies are used?
There may be cases where we want to keep authentication cookies separated from master and slaves applications. In this case we can’t share the authentication cookie among cooperating applications.
In this case the solution is to create an authentication cookie for the slave application from within the master application.
The code below creates an authentication cookie from the slave application:
FormsAuthenticationTicket ticket;
HttpCookie cookie;
string cookiestr;
ticket = new FormsAuthenticationTicket(
1,
userId,
DateTime.Now,
DateTime.Now.AddYears (120),
true,
"User Data",
"cookie_path"
);
cookiestr = FormsAuthentication.Encrypt(ticket);
cookie = new HttpCookie("cookie_name", cookiestr);
cookie.Expires = ticket.Expiration;
cookie.Path = "cookie_path";
Response.Cookies.Add(cookie);
Forms Authentication Ticket
The FormsAuthenticationTicket class is used to create and read the values of a forms authentication cookie identifying an authenticated user.
Forms authentication tickets must be encrypted using the FormsAuthentication.Encrypt() method before being issued as a cookie.
More information about the FormsAuthenticationTicket class can be found here.
The FormsAuthenticationTicket, as its name says, is a class used to generate authentication tickets (see the side box for more details). The code sample above shows that the following parameters are used to create the ticket:
- ticket version number
- user id
- date and time at which the ticket was generated
- ticket expiration
- whether creating a persistent cookie or limited to the current browser’s session
- user specific data to be stored in the ticket (for example, this could be a user class)
- cookie path

![Reblog this post [with Zemanta]](http://img.zemanta.com/reblog_e.png?x-id=de66927d-6964-4cc6-b3bf-67f49fa55c44)