Coupler Basic Auth et formulaire au sein d'une application ASP.NET
J'ai pu être confronté dans le cadre de mon travail à une application devant présenter deux modes d'authentification : une première, classique, par formulaire, et une seconde, pour les utilisateurs accédant à l'application depuis l'intranet, par fenêtre Basic Auth, remplie automatiquement à l'aide d'un système de SSO (Single Sign On).
Prit séparément, ces deux systèmes ne présentent aucune difficulté particulière pour leur implémentation, mais les cumuler présente un certain nombre de difficultés, comme par exemple la configuration du serveur : si on active l'accès anonyme, la boite d'authentification Basic Auth n'apparait pas. En revanche, si on active l'authentification Basic Auth, les utilisateurs ne peuvent atteindre la page de saisie du login et mot de passe (pour l'authentification par formulaire) sans voir la popup d'authentification.
Après quelques recherches, notamment sur des blogs de développeurs ayant rencontré le même problème, j'ai pu trouver trois solutions.
Solution numéro 1 : Simuler un Basic Auth.
La première solution consiste à tout faire soi-même. Le principe est donc de désactiver l'authentification dans le web.config, activer l'accès anonyme dans IIS, et déterminer à l'aide de l'URL d'où vient l'utilisateur. S'il vient d'internet, on vérifie qu'il est authentifié, et si ce n'est pas le cas on le redirige vers la page de login. A partir de là, on retrouve un fonctionnement par formulaire classique, à ceci près qu'il faut gérer certains mécanismes soit même vu que l'authentification est désactivée dans le web.config. Si l'utilisateur vient de l'intranet, on renvoie un code d'erreur 401 pour déclencher l'affichage de la popup d'authentification sur le navigateur. Une fois que l'utilisateur entre ses informations, il est possible de récupérer le login et le mot de passe dans l'en-tête de la requête http (respectivement Request.ServerVariables["LOGON_USER"] et Request.ServerVariables["AUTH_PASSWORD"]). Il ne reste plus qu'à authentifier l'utilisateur.
Bien que pouvant être très couteuse en termes de temps de développement et de test (simuler l'authentification Basic Auth est plus difficile qu'il n'y parait), cette solution est de mon point de vue la plus souple. Il faut aussi garder à l'esprit que tout gérer soi-même expose considérablement l'application, le moindre bug pouvant se transformer en faille critique de sécurité.
Solution numéro 2 : Séparer le module d'authentification du reste de l'application
La seconde solution consiste à isoler le code d'authentification de l'application dans une nouvelle application (ou plus exactement : deux nouvelles applications). Une gérant le Basic Auth, et l'autre l'accès par formulaire. En fonction de l'URL, l'utilisateur est dirigé vers l'une ou l'autre. A partir de là, il faut effectuer une authentification classique, à ceci près qu'il faut ensuite rediriger l'utilisateur vers l'application principale. Cela implique donc de désactiver l'authentification sur cette dernière, et mettre en place un système couplant cookies et informations de sessions stockées dans la base de données pour transmettre les informations d'authentification d'une application à l'autre. L'avantage de cette solution est d'avoir une configuration cohérente entre le serveur et les applications, pas de difficulté particulière pour le développement (si ce n'est la transmission des informations de l'utilisateur), et un système robuste en termes de sécurité (utilisation des modes d'authentification de la manière prévue par .NET). Le principal problème est le surcout engendré par le déploiement et la maintenance de deux applications, même limitées au processus d'authentification.
Solution numéro 3 : Utiliser deux répertoires virtuels
La troisième et dernière solution consiste à créer dans IIS deux répertoires virtuels pointant vers la même application. L'un sera configuré en accès anonyme, et l'autre en accès Basic Auth. L'application est quant à elle configurée en formulaire dans le web.config. Il faut ensuite distinguer d'où vient l'utilisateur, soit à l'aide de l'URL, ou de manière plus sûre à l'aide du nom du répertoire virtuel. Si l'utilisateur vient d'internet, on se retrouve dans une authentification par formulaire classique. Si l'utilisateur vient de l'intranet, IIS se chargera de lui afficher la popup d'authentification Basic Auth pour qu'il saisisse ses identifiants, et vérifiera leur validité auprès de l'Active Directory. Il suffit ensuite de récupérer le login dans l'application. Attention toutefois, l'application étant configurée en mode formulaire dans le web.config, les informations de l'utilisateur ne sont pas renseignées dans la propriété CurrentPrincipal comme dans une authentification Windows classique. Par chance, après l'authentification de l'utilisateur, IIS transmet à l'application la requête HTTP telle qu'elle a été reçue. Il est donc possible de récupérer le login dans les en-têtes HTTP, comme dans le cas de la première solution. Je conseille d'ailleurs de récupérer dans la foulée le mot de passe pour effectuer une double validation (des fois qu'un utilisateur malveillant parvienne à faire croire à l'application qu'il vient de l'intranet alors qu'il est passé par le répertoire virtuel en accès anonyme).
Cette solution a l'avantage d'être la plus simple des trois. Elle a par contre un inconvénient majeur : si deux répertoires virtuels pointent vers les mêmes fichiers, cela implique que la même application soit chargée deux fois en mémoire, entrainant une forte surconsommation et une augmentation de la fragmentation. De plus, il faut faire attention à ce que les deux applications n'utilisent pas par exemple les mêmes fichiers de log, sous peine de voir l'une d'elles bloquée par le verrouillage en écriture de l'autre.
En bref
Chacune des trois solutions a ses avantages et ses inconvénients, aussi chaque situation est à étudier pour déterminer celle qui convient le mieux. Cependant, dans la plupart des cas, je recommanderai la seconde, car c'est celle qui respecte le mieux le cadre prévu des fonctions d'authentification de .NET. Cette liste de solution n'étant pas forcément exhaustive, je vous invite à réagir si vous envisageriez d'autres solutions pour faire cohabiter une authentification formulaire et Basic Auth au sein de la même application.
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :