Asp.Net Forms Authentication lors de l’utilisation de iPhone UIWebView

J’écris une application Asp.net MVC 2 qui utilise l’authentification par formulaire et, actuellement, j’ai un problème avec notre application iPhone en ce qui concerne l’authentification / connexion sur le Web. Nous avons développé une application iPhone simple qui utilise le contrôle UIWebView. À ce stade, toute l’application est de naviguer sur notre site Web Asp.Net. Simple, non? Le problème est que l’utilisateur ne peut pas passer la page de connexion. Les étapes de repro sont:

  • Ouvrez l’application iPhone.
  • L’application navigue vers la page d’accueil.
  • l’utilisateur n’est pas authentifié, il est donc redirigé vers la page de connexion
  • L’utilisateur entre le nom d’utilisateur et le mot de passe corrects. clique sur submit.
  • côté serveur, l’utilisateur est authentifié et un cookie est généré et envoyé au client à l’aide de FormsAuthentication.GetAuthCookie.
  • Le serveur envoie des messages de redirection pour envoyer l’utilisateur à la page d’accueil correcte.

Mais l’utilisateur est ensuite redirigé vers l’écran de connexion!

J’ai fait un débogage approfondi à ce sujet et je sais que:

Le cookie est envoyé au client et le client stocke le cookie. Vérifié ceci dans le débogueur d’iPhone et aussi en utilisant Javsascript pour afficher des données de cookie sur la page. Le cookie est renvoyé au serveur. Vérifié cela dans le débogueur Visual Studio. C’est le bon cookie (c’est le même qui a été défini). La propriété User.Identity.IsAuthenticated renvoie false pour une raison quelconque, même si le cookie d’authentification est contenu dans l’object Request. J’ai vérifié que l’application iPhone est configurée pour accepter les cookies et qu’ils se trouvent sur le client.

Voici ce qui est amusant: cela fonctionne très bien si vous ouvrez le navigateur Safari sur l’iPhone et accédez directement à notre site.

Il a le même comportement sur l’iPad car il ne dépasse pas l’écran de connexion. Ceci repros sur les émulateurs, et sur les appareils.

Ce même site Web a été testé avec IE 7-8, Safari (pour Windows), Blackberry, IEMobile 6.5, Phone 7 et il fonctionne. La seule circonstance sur laquelle il ne fonctionne pas est l’UIWebView dans l’application iPhone.

J’ai eu exactement le même problème, mais avec un autre périphérique (NokiaN8), et j’ai également renvoyé le problème à l’agent utilisateur.

IIS utilise des expressions régulières pour correspondre à la chaîne User-Agent. La source du problème était qu’il n’avait aucune expression régulière correspondante pour le périphérique spécifique et qu’il se trouvait dans l’un des niveaux de correspondance les plus bas, où les propriétés par défaut étaient utilisées. Les propriétés par défaut indiquaient que le navigateur ne supportait pas les cookies.

Solution:

  1. Ajoutez un dossier dans votre projet Web nommé App_Browsers (cliquez avec le bouton droit sur le projet, choisissez: Add > Add ASP.NET Folder > App_Browsers ).
  2. Ajoutez un fichier dans ce dossier (cliquez avec le bouton droit, choisissez: Add > New Item ). Le fichier peut avoir n’importe quel nom, mais doit avoir la fin de .browser .
  3. Ajoutez une bonne expression correspondante et les capacités correctes (ou ajoutez des modifications au Default ).

Deux exemples:

            

Ou changez la valeur par défaut:

        

Plus d’infos: Schéma du fichier de définition de navigateur

La solution que nous avons trouvée consistait à créer un fichier (generic.browser) et à inclure ce fichier XML pour indiquer au serveur Web que «Mozilla» et les parameters du navigateur par défaut devraient tous prendre en charge les cookies.

      

Ceci est corrigé dans ASP.NET 4.5 et tous les navigateurs sont supposés prendre en charge les cookies, donc le fichier .browser supplémentaire ne sera pas nécessaire.

D’après les recherches que j’ai effectuées, la raison pour laquelle vous ne pouvez pas définir l’agent utilisateur est que UIWebView définit la valeur User-Agent juste avant d’envoyer la demande, c’est-à-dire après avoir fait votre demande à partir de votre code. .

L’astuce pour contourner ce problème consiste à utiliser quelque chose appelé “swizzling de méthode”, un concept Objective-C avancé et potentiellement dangereux qui échange une méthode standard avec une autre que vous fournissez. Le résultat final est que lorsque votre demande est envoyée et que le code d’infrastructure ajoute l’agent utilisateur, il sera trompé en utilisant la méthode que vous avez fournie.

Ce qui suit explique ce que j’ai fait pour le mettre en œuvre, mais je ne suis pas un expert d’Objective-C et je vous suggère de faire des recherches pour vous familiariser avec la technique. En particulier, il y avait un lien expliquant mieux que moi ce qui se passait ici, mais pour le moment je ne le trouve pas.

1) Ajouter une catégorie sur NSObject pour autoriser le 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) Sous-classe NSMutableURLRequest pour autoriser le swizzle:

 @interface NSMutableURLRequest (MyMutableURLRequest) + (void) setupUserAgentOverwrite; @end @implementation NSMutableURLRequest (MyMutableURLRequest) - (void) newSetValue:(NSSsortingng*)value forHTTPHeaderField:(NSSsortingng*)field { if ([field isEqualToSsortingng:@"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) Appelez la méthode statique pour remplacer la méthode. J’ai fait cet appel dans didFinishLaunchingWithOptions:

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

4) Et ensuite utilisé comme ça. (Le délégué de connexion enregistre les données dans un tableau mutable, puis définit manuellement l’UIWebView à l’aide de sa méthode loadData à la fin du chargement).

 - (void)loadWithURLSsortingng:(NSSsortingng*)urlSsortingng { NSURL *url = [NSURL URLWithSsortingng:urlSsortingng]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; _connection = [NSURLConnection connectionWithRequest:request delegate:self]; [_connection start]; } 

J’ai eu le même problème exact, recherché et consolidé une solution complète (à partir des réponses ci-dessus et d’autres discussions) ici: http://www.bloggersworld.com/index.php/asp-net-forms-authentication-iphone-cookies/

  1. Avez-vous spécifié un DestinationPageUrl dans le balisage?

  2. Avez-vous spécifié le defaultURL dans web.config?

Exemple web.config

    

Exemple DestinationPageUrl

   

Enfin, avez-vous consulté le cookie jar et vu si votre cookie de session existe réellement?

Où sont stockés les cookies de UIWebView?

La raison de ceci se produit apparemment avec le fait que si l’agent utilisateur n’est pas connu, le navigateur est supposé ne pas accepter les cookies (comme d’autres ont répondu), et au lieu de cela, IIS place la valeur ASPXAUTH dans l’URL.

Cependant, le système de routage MVC a apparemment manqué cette possibilité, ce qui est clairement un bogue, et par conséquent, il est mis en déroute.

Bien que l’ajout de .browser à un agent utilisateur personnalisé résout le problème, cela ne garantit pas que les autres agents utilisateurs seront également résolus. En fait, j’ai trouvé que le navigateur K9 pour Android avait également ce problème, et que n’est qu’une solution si l’on dispose d’un système de journalisation tel qu’elmeh pour détecter de telles erreurs.

D’autre part, l’ajout d’une valeur par défaut soulève la question s’il est vrai que tous les navigateurs acceptent les cookies, ce qui est apparemment la raison pour laquelle IIS ne le suppose pas.

Cependant, en plus d’append explicitement les agents utilisateurs, il est possible d’append dans la méthode global.asax RegiterRoutes () un gestionnaire explicite pour l’ignorer, comme suit:

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

Cependant, dans ce cas, il faudra copier toutes les entrées de route pour qu’elles correspondent à la situation sans cookie, à moins que l’on ne soit sur le point d’écrire un gestionnaire de route personnalisé.

Ou nous pouvons utiliser la route sans cookie ci-dessus pour envoyer l’utilisateur à une page d’erreur expliquant que son navigateur n’est pas pris en charge pour le moment et envoyer une alerte au maître Web avec l’agent utilisateur pour le gérer.