Autenticación de formularios Asp.Net al usar iPhone UIWebView

Estoy escribiendo una aplicación Asp.net MVC 2 que usa la Autenticación de formularios y actualmente estoy teniendo un problema con nuestra aplicación de iPhone con respecto a la autenticación / inicio de sesión en la web. Hemos desarrollado una aplicación de iPhone simple que utiliza el control UIWebView. En esta etapa, todo lo que hace la aplicación es navegar a nuestro sitio web Asp.Net. Simple, ¿verdad? El problema es que el usuario no puede pasar de la página de inicio de sesión. Los pasos de repro son:

  • Abra la aplicación de iPhone.
  • La aplicación navega a la página de inicio.
  • el usuario no está autenticado, por lo que se le redirige a la pantalla / página de inicio de sesión
  • El usuario ingresa el nombre de usuario y la contraseña correctos. clics enviar.
  • en el lado del servidor, el usuario se autentica y se genera una cookie que se envía al cliente utilizando FormsAuthentication.GetAuthCookie.
  • Los envíos del servidor son redirigidos para enviar al usuario a la página de inicio correcta.

¡Pero el usuario es redirigido de regreso a la pantalla de inicio de sesión!

He hecho algunas depuraciones extensas sobre esto y lo que sí sé es:

La cookie se envía al cliente y el cliente almacena la cookie. Verificado esto en el depurador de iPhone y también mediante el uso de Javsascript para mostrar datos de cookies en la página. La cookie se envía de vuelta al servidor. Verificado esto en el depurador de Visual Studio. Es la cookie correcta (es la misma que se configuró). La propiedad User.Identity.IsAuthenticated devuelve false por algún motivo, aunque la cookie de autenticación esté contenida en el objeto Request. He verificado que la aplicación de iPhone está configurada para aceptar cookies, y están en el cliente.

Aquí está lo gracioso: funciona bien si abre el navegador Safari en el iPhone y va directamente a nuestro sitio.

También tiene el mismo comportamiento en el iPad, ya que no pasa de la pantalla de inicio de sesión. Esto repros en los emuladores y en los dispositivos.

Este mismo sitio web ha sido probado con IE 7-8, Safari (para Windows), Blackberry, IEMobile 6.5, Phone 7 y funciona find. La única circunstancia en la que no funciona es UIWebView en la aplicación iPhone.

Tenía exactamente el mismo problema, pero con otro dispositivo (NokiaN8), y también seguí el problema hasta el User-Agent.

IIS usa expresiones regulares para hacer coincidir con la cadena User-Agent. La raíz del problema era que no tenía expresiones regulares coincidentes para el dispositivo específico y terminaba en uno de los niveles más bajos de coincidencia, donde se usaban las propiedades predeterminadas. Las propiedades predeterminadas indicaban que el navegador no admitía cookies.

Solución:

  1. Agregue una carpeta en su proyecto web llamado App_Browsers (haga clic con el botón derecho en el proyecto, elija: Add > Add ASP.NET Folder > App_Browsers ).
  2. Agregue un archivo en esa carpeta (haga clic con el botón derecho, elija: Add > New Item ). El archivo puede tener cualquier nombre, pero debe tener el final del .browser .
  3. Agregue una buena expresión coincidente y las capacidades correctas (o agregue cambios al Default ).

Dos ejemplos:

            

O cambie el valor predeterminado:

        

Más información: Esquema del archivo de definición del navegador

La solución que encontramos fue crear un archivo (generic.browser) e incluir este xml para decirle al servidor web que “Mozilla” y la configuración predeterminada del navegador deberían ser compatibles con las cookies.

      

Esto se soluciona en ASP.NET 4.5 y se supone que todos los navegadores admiten cookies, por lo que no se necesitará el archivo .browser adicional.

A partir de la investigación que realicé, la razón por la que no puede configurar el User-Agent es que UIWebView está configurando el valor User-Agent justo antes de enviar la solicitud, es decir, después de que haya realizado su solicitud desde su código .

El truco para evitar este problema es usar algo llamado “método swizzling”, un concepto Objective-C avanzado y potencialmente peligroso que intercambia un método estándar con uno que usted proporciona. El resultado final es que cuando se envía su solicitud y el código de la infraestructura agrega el User-Agent, se lo engañará utilizando el método que proporcionó.

A continuación, se explica lo que hice para implementar esto, pero no soy un experto en Objective-C y le sugiero que investigue un poco para familiarizarse con la técnica. En particular, había un enlace explicando mejor que yo lo que está pasando aquí, pero por el momento no puedo encontrarlo.

1) Agregue una categoría en NSObject para permitir swizzling.

 @interface NSObject (Swizzle) + (BOOL) swizzleMethod:(SEL)origSelector withMethod:(SEL)newSelector; @end @implementation NSObject (Swizzle) + (BOOL) swizzleMethod:(SEL) origSelector withMethod:(SEL)newSelector { Method origMethod= class_getInstanceMethod(self, origSelector); Method newMethod= class_getInstanceMethod(self, newSelector); if (origMethod && newMethod) { if (class_addMethod(self, origSelector, method_getImplementation(newMethod), method_getTypeEncoding(newMethod))) { class_replaceMethod(self, newSelector, method_getImplementation(origMethod), method_getTypeEncoding(origMethod)); } else { method_exchangeImplementations(origMethod, newMethod); } return YES; } return NO; } @end 

2) Subclase NSMutableURLRequest para permitir el swizzle:

 @interface NSMutableURLRequest (MyMutableURLRequest) + (void) setupUserAgentOverwrite; @end @implementation NSMutableURLRequest (MyMutableURLRequest) - (void) newSetValue:(NSString*)value forHTTPHeaderField:(NSString*)field { if ([field isEqualToString:@"User-Agent"]) { value = USER_AGENT; // ie, the value I want to use. } [self newSetValue:value forHTTPHeaderField:field]; } + (void) setupUserAgentOverwrite { [self swizzleMethod:@selector(setValue:forHTTPHeaderField:) withMethod:@selector(newSetValue:forHTTPHeaderField:)]; } @end 

3) Llame al método estático para cambiar el método. Hice esta llamada en didFinishLaunchingWithOptions:

 // Need to call this method so that User-Agent get updated correctly: [NSMutableURLRequest setupUserAgentOverwrite]; 

4) Y luego lo usó así. (El delegado de conexión guarda los datos en una matriz mutable y luego establece manualmente UIWebView usando su método loadData cuando termina de cargarse).

 - (void)loadWithURLString:(NSString*)urlString { NSURL *url = [NSURL URLWithString:urlString]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; _connection = [NSURLConnection connectionWithRequest:request delegate:self]; [_connection start]; } 

Tuve el mismo problema exacto, investigué y consolidé una solución completa (de las respuestas anteriores y otros hilos) aquí: http://www.bloggersworld.com/index.php/asp-net-forms-authentication-iphone-cookies/

  1. ¿Ha especificado un DestinationPageUrl en el marcado?

  2. ¿Ha especificado el URL predeterminado en web.config?

Ejemplo web.config

    

Ejemplo DestinationPageUrl

   

Por último, ¿has mirado en el contenedor de cookies y has visto si tu cookie de sesión realmente existe?

¿Dónde se almacenan las cookies de UIWebView?

La razón de que esto suceda aparentemente tiene que ver con el hecho de que si el usuario-agente no es conocido, se supone que el navegador no acepta cookies (como otros han respondido), y en su lugar IIS pone el valor de ASPXAUTH en la URL.

Sin embargo, el sistema de enrutamiento MVC aparentemente no se dio cuenta de esa posibilidad, que claramente es un error, y por lo tanto se está desordenando.

Si bien la adición de .browser con un agente de usuario personalizado resuelve el problema, no garantiza que otros usuarios-agentes también se resuelvan, y de hecho he encontrado que el navegador K9 para Android también tiene este problema, y ​​como tal es solo una solución si uno tiene un sistema de registro como elmeh para rastrear tales errores.

Por otro lado, al agregar un valor predeterminado aparece la pregunta si es cierto que todos los navegadores aceptan cookies, que aparentemente es la razón por la cual IIS no lo asume.

Sin embargo, además de agregar explícitamente los user-agents, se puede agregar en el método global.asax RegiterRoutes () un controlador explícito para ignorarlo, de la siguiente manera:

  routes.MapRoute( "CookieLess", // Route name "(F({Cookie}))/{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults ); 

Sin embargo, en este caso, deberá copiar todas las entradas de la ruta para que coincidan con la situación sin cookies, a menos que esté a punto de escribir un controlador de ruta personalizado.

O podemos utilizar la ruta anterior sin cookies para enviar al usuario a una página de error explicando que su navegador no es compatible en el momento, y enviar una alerta al maestro web con el agente de usuario para que lo maneje.