Wednesday, 22 June 2016

Integrate new Web API with old WebForms Cookies

To make Web API 2 with Identity 2.0 and OWIN accept default cookies from WebForms you need to configure CookieAuthentication in the following way:

public partial class Startup
{
 public void ConfigureAuth(IAppBuilder app)
 {
  app.UseCookieAuthentication(new CookieAuthenticationOptions()
  {
   CookieName = FormsAuthentication.FormsCookieName,
   CookieDomain = FormsAuthentication.CookieDomain,
   CookiePath = FormsAuthentication.FormsCookiePath,
   CookieSecure = CookieSecureOption.SameAsRequest,
   AuthenticationMode = AuthenticationMode.Active,
   ExpireTimeSpan = FormsAuthentication.Timeout,
   SlidingExpiration = true,
   AuthenticationType = CustomAuthenticationTypes.FormsAuthenticationType,
   TicketDataFormat = new SecureDataFormat(
    new FormsAuthTicketSerializer(),
    new FormsAuthTicketDataProtector(),
    new HexEncoder())
  });
 }
}


First you have to add a reference to "System.Web.Security" to be able to reference the FormsAuthentication object with has static fields that expose the default cookie name, domain, path and timeout. Note if you have set a non default value for these options in your WebForms Project you need to set them to your new settings.

The AuthenticationType is just a string to identify between types of authentication in your OWIN pipeline

The SecureDataFormat is an object allocates how the incoming data (a FormsAuthenticationTicket from WebForms  in this case) should be deserialized, unprotected and decoded or the opposite for an outgoing Identity

The FormsAuthTicketSerializer, FormsAuthTicketDataProtector, and HexEncoder are all custom classes which are described below


HexEncoder
public class HexEncoder : ITextEncoder
{
 public string Encode(byte[] data)
 {
  return ToHexadecimal(data);
 }

 public byte[] Decode(string text)
 {
  return ToBytesFromHexadecimal(text);
 }

 public static string ToHexadecimal(byte[] ba)
 {
  StringBuilder hex = new StringBuilder(ba.Length * 2);
  foreach (byte b in ba)
   hex.AppendFormat("{0:x2}", b);
  return hex.ToString();
 }

 public static byte[] ToBytesFromHexadecimal(string hex)
 {
  int NumberChars = hex.Length;
  byte[] bytes = new byte[NumberChars / 2];
  for (int i = 0; i < NumberChars; i += 2)
   bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
  return bytes;
 }


The HexEncoder is a class that is used by CookieAuthentication and the FormsAuthTicketDataProtector to simply convert a hexadecimal string to bytes and vice versa


FormsAuthTicketDataProtector
public class FormsAuthTicketDataProtector : IDataProtector
{
 public byte[] Protect(byte[] userData)
 {
  FormsAuthenticationTicket ticket;
  using (var memoryStream = new MemoryStream(userData))
  {
   var binaryFormatter = new BinaryFormatter();
   ticket = binaryFormatter.Deserialize(memoryStream) as FormsAuthenticationTicket;
  }

  if (ticket == null)
  {
   return null;
  }

  try
  {
   return HexEncoder.ToBytesFromHexadecimal(FormsAuthentication.Encrypt(ticket));
  }
  catch
  {
   return null;
  }
 }

 public byte[] Unprotect(byte[] protectedData)
 {
  FormsAuthenticationTicket ticket;
  try
  {
   ticket = FormsAuthentication.Decrypt(HexEncoder.ToHexadecimal(protectedData));
  }
  catch
  {
   return null;
  }

  if (ticket == null)
  {
   return null;
  }

  using (var memoryStream = new MemoryStream())
  {
   var binaryFormatter = new BinaryFormatter();
   binaryFormatter.Serialize(memoryStream, ticket);

   return memoryStream.ToArray();
  }
 }
}


The FormsAuthTicketDataProtector is a class that encrypts a FormsAuthenticationTicket to a byte stream and decrypts a FormsAuthenticationTicket from a byte stream


FormsAuthTicketSerializer
public class FormsAuthTicketSerializer : IDataSerializer
{
 public AuthenticationTicket Deserialize(byte[] data)
 {
  using (var dataStream = new MemoryStream(data))
  {
   var binaryFormatter = new BinaryFormatter();
   var ticket = binaryFormatter.Deserialize(dataStream) as FormsAuthenticationTicket;
   if (ticket == null)
   {
    return null;
   }

   var identity = AccountService.CreateIdentity(ticket.Name, CustomAuthenticationTypes.FormsAuthenticationType);
   var authTicket = new AuthenticationTicket(identity, new AuthenticationProperties());

   authTicket.Properties.IssuedUtc = new DateTimeOffset(ticket.IssueDate);
   authTicket.Properties.ExpiresUtc = new DateTimeOffset(ticket.Expiration);
   authTicket.Properties.IsPersistent = ticket.IsPersistent;
   return authTicket;
  }
 }

 public byte[] Serialize(AuthenticationTicket model)
 {
  var userTicket = new FormsAuthenticationTicket(
    2,
    model.Identity.Claims.Single(c => c.Type == ClaimTypes.Name).Value,
    new DateTime(model.Properties.IssuedUtc.Value.UtcDateTime.Ticks, DateTimeKind.Utc),
    new DateTime(model.Properties.ExpiresUtc.Value.UtcDateTime.Ticks, DateTimeKind.Utc),
    model.Properties.IsPersistent,
    "",
    FormsAuthentication.FormsCookiePath);

  using (var dataStream = new MemoryStream())
  {
   var binaryFormatter = new BinaryFormatter();
   binaryFormatter.Serialize(dataStream, userTicket);

   return dataStream.ToArray();
  }
 }
}


Finally the FormsAuthTicketSerializer serialises the FormsAuthenticationTicket from bytes to an OWIN based AuthenticationTicket and vice versa. This new AuthenticationTicket is what OWIN will use to create the users identity further down the process.

This technique is useful if you are wanting to migrate a large WebForms site to the newer MVC and Web API standards incrementally because it allows you to move all the business logic to a WebAPI while still maintaining the WebForms front end interface

8 comments:

  1. IEEE Final Year projects Project Centers in Chennai are consistently sought after. Final Year Students Projects take a shot at them to improve their aptitudes, while specialists like the enjoyment in interfering with innovation. For experts, it's an alternate ball game through and through. Smaller than expected IEEE Final Year project centers ground for all fragments of CSE & IT engineers hoping to assemble. Final Year Project Domains for IT It gives you tips and rules that is progressively critical to consider while choosing any final year project point.

    JavaScript Training in Chennai

    JavaScript Training in Chennai

    ReplyDelete