Introducción
Asp.net permite incorporar de una manera muy rápida la autenticación vía Forms Authentication, en este articulo veremos como configurar dos o más aplicaciones web diferentes para compartir el mismo token de autenticación generado.
Prerrequisitos
- Contar con una aplicación web configurada correctamente con Forms authentication
- Tener una segunda aplicación web en donde se necesite tener el mismo token de autentificación (es decir, no volverse a firmar nuevamente)
Escenario
Existe una aplicación web funcionando con Forms authentication(www.sitiowebficticio.com) , por cuestiones de diseño se decide separar dicha aplicación en dos diferentes sitios web mediante directorios virtuales www.sitiowebficticio.com/App1 y www.sitiowebficticio.com/App2
El objetivo es que ambas aplicaciones web compartartan el mismo token de autenticación generado por Forms authentication, de esta manera el usuario solo tendría que escribir sus credenciales una sola vez.
Manos a la obra
En App1 y App2 abrir el web.config, debería tener algo similar a lo siguiente (asumiendo que ya se encuentra configurado Forms authentication):
1: <connectionStrings>
2: <add name="MembershipCS" connectionString="Data Source=(local);Initial Catalog=kpmg;Integrated Security=true;"/>
3: </connectionStrings>
4: <system.web>
5: <!--
6: The <authentication> section enables configuration
7: of the security authentication mode used by
8: ASP.NET to identify an incoming user.
9: -->
10: <authentication mode="Forms"/>
11: <membership defaultProvider="SqlProv">
12: <providers>
13: <add name="SqlProv" type="System.Web.Security.SqlMembershipProvider,System.Web, Version=2.0.0.0, Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"
14: connectionStringName="MembershipCS"
15: enablePasswordRetrieval="false"
16: enablePasswordReset="true"
17: requiresQuestionAndAnswer="true" applicationName="/" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="1" passwordAttemptWindow="10" passwordStrengthRegularExpression=""/>
18: </providers>
19: </membership>
Suponiendo que dentro de app1 y app2 existen dos folders en donde se le niega el acceso a usuarios anónimos el path seria el siguiente:
- Usuario accede a www.sitiowebficticio.com/App1/CarpetaSegura/PaginaSegura.aspx, forms autentication redirecciona a login.aspx para que el usuario ingrese sus credenciales
- El usuario ingresa sus credenciales, MemberShipProvider valida y login.aspx redirecciona a www.sitiowebficticio.com/App1/CarpetaSegura/PaginaSegura.aspx
- El usuario ahora quiere ingresar a App2, www.sitiowebficticio.com/App2/CarpetaSegura2/otrapaginasegura.aspx, forms authentication redirecciona a login.aspx pero de App2
¿Por que sucede este comportamiento si en ambas aplicaciones web tenemos exactamente la misma configuración?
Este comportamiento se debe a que el cookie que genera asp.net está encriptado y para generar la encriptación / des encriptación asp.net utiliza los valores especificados en los elementos validationKey y decryptionKey. Asp.net por defecto genera esas claves a nivel aplicación de manera automatica, es por eso que cuando el usuario pasa de App1 a App2 el cookie no puede ser leio de manera correcta ya que las claves de encriptación son diferentes y por ende, se solicita nuevamente la autenticación ya que Forms authentication toma ese cookie como invalido al no poderlo desencriptar
Lo que haremos será especificar dichas claves, para ello crearemos una aplicación de consola que generara dichas claves criptograficas:
1: using System.Security.Cryptography;
2:
3: namespace GenerarClavesAspNet
4: { 5: class Program
6: { 7: static void Main()
8: { 9: string decryptionKey = CrearClave(System.Convert.ToInt32(24));
10: string validationKey = CrearClave(System.Convert.ToInt32(64));
11:
12: Console.WriteLine("<machineKey validationKey=\"{0}\" decryptionKey=\"{1}\" validation=\"SHA1\"/>", validationKey, decryptionKey); 13:
14: Console.ReadKey();
15:
16: }
17:
18: static String CrearClave(int numBytes)
19: { 20: RNGCryptoServiceProvider service = new RNGCryptoServiceProvider();
21: byte[] buff = new byte[numBytes];
22:
23: service.GetBytes(buff);
24: return ConvertirString(buff);
25: }
26:
27: static String ConvertirString(byte[] bytes)
28: { 29: StringBuilder cadena = new StringBuilder(64);
30:
31: for (int counter = 0; counter < bytes.Length; counter++)
32: { 33: cadena.Append(String.Format("{0:X2}", bytes[counter])); 34: }
35: return cadena.ToString();
36: }
37:
38: }
39: }
Al ejecutar la aplicación obtedremos un resultado similar al siguiente (las claves son generadas aleatoriamente)
1: <machineKey
2: validationKey="3E622C9B81A29248B61C4A7863F9632ED1A98574741D77954E7A3643F8FFAC84F11ED1D62B9D3A9813389FB969AEFBE95BCFFD46D50840E67A167AA987DDE2DB"
3: decryptionKey="5950EB5459814355C270F07761331247D83723D0701EBF21" validation="SHA1"/>
Nota: Los valores aceptados para decryptionKey es de 8 a 24 bytes (en el ejemplo se usan 24) y para validationKey es de 20 a 64 bytes ( en el ejemplo se usa el nivel mas alto 64 bytes)
El resultado lo incorporaremos en ambas aplicaciones de está manera el token será compartido por ambas aplicaciones, el código en ambos web.config deberia de quedar similar al siguiente:
1: <connectionStrings>
2: <add name="MembershipCS" connectionString="Data Source=(local);Initial Catalog=kpmg;Integrated Security=true;"/>
3: </connectionStrings>
4: <system.web>
5: <!--
6: The <authentication> section enables configuration
7: of the security authentication mode used by
8: ASP.NET to identify an incoming user.
9: -->
10: <authentication mode="Forms"/>
11: <membership defaultProvider="SqlProv">
12: <providers>
13: <add name="SqlProv" type="System.Web.Security.SqlMembershipProvider,System.Web, Version=2.0.0.0, Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"
14: connectionStringName="MembershipCS"
15: enablePasswordRetrieval="false"
16: enablePasswordReset="true"
17: requiresQuestionAndAnswer="true" applicationName="/" requiresUniqueEmail="false" passwordFormat="Hashed" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="7" minRequiredNonalphanumericCharacters="1" passwordAttemptWindow="10" passwordStrengthRegularExpression=""/>
18: </providers>
19: </membership>
20:
21: <machineKey validationKey="3E622C9B81A29248B61C4A7863F9632ED1A98574741D77954E7A3
22: 643F8FFAC84F11ED1D62B9D3A9813389FB969AEFBE95BCFFD46D50840E67A167AA987DDE2DB"
23: decryptionKey="5950EB5459814355C270F07761331247D83723D0701EBF21" validation="SHA1"/>
De esta manera el path queda de la siguiente manera:
Conclusión
Asp.Net permite configurar de una manera muy rápida el compartir un token de autentificación entre diferentes aplicaciones web.