Implémentation d’une solution fondée sur les Azure App Services : Gestion d’identité

Voici donc le sixième article d’une série d’articles consacrée à l'implémentation d’une solution fondée sur les API App Services et qui sera organisée de la façon suivante :

L’objectif est maintenant de filtrer les accès sur l’« API App », exposée au-dessus du service backend « Azure Search » et d’adapter l’application Windows Universelle pour qu’elle puisse utiliser cette « API App », en donnant la possibilité à l’utilisateur de s’identifier depuis cette application…

Services d’authentification « Azure App Service »

« Azure App Service » propose des services d'authentification intégrés qui implémentent nativement OAuth 2.0 et OpenID et peuvent s’intégrer avec plusieurs fournisseurs d'identité (interne comme « Azure Active Directory » ou externes comme Microsoft Account, Google, Twitter ou Facebook).
Dans l’implémentation proposée, l’accès à l’« API App » sera donc maintenant restreint aux utilisateurs authentifiés sur « Azure Active Directory » (AAD).

« Azure Active Directory »

« Azure Active Directory » est un service Cloud de gestion d'identité destiné à offrir un accès par authentification unique à des milliers d’Applications SaaS, comme par exemple « Office 365 » ou Salesforce.com, .... « Azure Active Directory » propose de nombreuses fonctions comme l’authentification multi-facteurs ou l'intégration avec « Windows Server Active Directory ». Dans le contexte de l’implémentation de cette solution, si le choix s'est porté sur ce service, c'est surtout parce qu'il permet de rapidement intégrer un mécanisme d'authentification fondé sur l'utilisation d'un annuaire facile à administrer (tout en offrant une interface de gestion de mot de passe, en libre-service pour les utilisateurs qui y sont déclarés).image

Configuration de l’« API App »

L’ « API App » propose trois paramètres d’authentification (« Access Level » dans les « Application Settings ») :

  • Public (anonyme) - n'importe qui peut appeler l’« API App ».
  • Public (authentifié) - seuls les utilisateurs authentifiés sont autorisés à appeler l’« API App » en dehors du groupe de ressources dans lequel est déployée l’« API App ».
  • Interne - l’« API App » peut uniquement être appelée par d'autres « API App » déployées dans le même groupe de ressources.

image

Lorsque l’on souhaite configurer l’« API App » pour accepter uniquement les requêtes authentifiées, la première étape consiste donc à paramétrer son niveau d’accès sur Public (authentifié). Et pour exiger une authentification auprès d'un fournisseur d’identité comme « Active Directory Azure », il faut maintenant configurer une passerelle : l’« API Gateway ».

Configuration de l’« API Gateway »

L’« API Gateway » est créée par Visual Studio lors de la publication de l’API. Il s’agit d’une application web dont le rôle est de filtrer toutes les requêtes HTTP entrantes destinées aux « API Apps » du même groupe de ressources pour y inclure diverses tâches administratives, dont l'authentification.
La passerelle gère les procédures de connexion, les jetons et empêche les appels non authentifiés d'atteindre l’« API App » configurée pour exiger ce type d’accès. 
image

Le fournisseur d’identité de notre solution est « Azure Active Directory ». Pour bénéficier de ces services, il est nécessaire d’enregistrer l’« API App » « Doc.Search.API » en tant qu’application AAD.
Pour ce faire, on peut utiliser le portail Azure actuel et renseigner les paramètres « Sign-on URL », « App ID URI », et « Reply URL » obtenus par simple copie depuis le nouveau portail Azure, dans la configuration AAD de la passerelle.

imageOn obtient alors un « Client ID » pour l’« API App » que l’on peut ensuite reporter, avec le « Allowed tenant » dans la configuration de la passerelle sur le nouveau portail.

image

Evolution du code de l’« API App »

La première évolution de l’« API App » consiste à modifier le fichier « apiapp.json » pour déclarer le fournisseur d’identité que l’on souhaite utiliser.

imageIl est également possible d’étendre l’« API App » pour ajouter une opération permettant d’obtenir le nom et l’email de la personne connectée. Ainsi, il sera possible depuis l’application universelle de faire un appel à cette méthode et d’afficher l’email de la personne identifiée. 
imagePour tester l’accès restreint sur l’« API App », on peut utiliser un browser, en déclenchant d’abord une authentification sur l’« API Gateway », via son URL suffixée par « /login/aad » et en utilisant un compte défini sur le « tenant » AAD cible.
https://nom-de-la-gateway.azurewebsites.net/login/aad
Une fois l’utilisateur identifié sur l’« API Gateway », il devient alors possible de faire usage de l’API.imageIl ne reste plus maintenant qu’à modifier le code de l’application cliente de l’API pour qu’elle puisse passer les paramètres d’authentification requis.

Evolution du code de l’application Universelle

L’application mobile peut donner la possibilité de s’authentifier vis-à-vis d’AAD en intégrant la page de Logon comme l’illustre la copie d’écran suivante. 
image
Le SDK d’« App Service » offre une syntaxe simplifiée pour la gestion du code liée à l’identification de l’utilisateur et au contrôle d’accès qui en découle.
Les URLs de l’« API Gateway » et de l’« API App » doivent être connues de l’application cliente, ainsi que le type de fournisseur d’identité cible, dans notre contexte, la chaîne identifiant l’AAD.

        public const string GATEWAY = "https://nom-de-la-gateway.azurewebsites.net";
        public const string API_APP = "https://nom-de-la-api-app.azurewebsites.net";
        public const string AUTH_PROVIDER = "WindowsAzureActiveDirectory";

C’est dans le constructeur du « MainViewModel » qu’est réalisée l’authentification, par appel au constructeur « AppServiceClient » qui intègre nativement un mécanisme de connexion sur l’« API Gateway ». 

imageL’authentification est réalisée par l’appel à la méthode asynchrone « LoginAsync(…) », dans laquelle on spécifie le fournisseur d’identité cible. 
imageIl est important de bien choisir les conditions permettant de déclencher cette authentification. Il est parfois délicat de se mettre en attente d'un retour d'appel asynchrone à l’intérieur d’un gestionnaire d’évènement associé à la logique de chargement des fenêtres Windows.

J’avais initialement prévu de lancer cet appel sur l’évènement « OnNavigatedTo(…) ». Mais il semble que le modèle de navigation (utilisation d’une page « Shell » permettant d’afficher la page cible en utilisant un contrôle « SplitView ») de l’application rende ce choix inapproprié [la fenêtre de saisie des « credentials » ne s'affichait que si l’appel de la méthode de « LoginAsync(…) » était précédé d’un « Task.Delay » de 50 ms]. Pour contourner cette problématique, l’appel à la méthode est donc déclenché sur la première occurrence de l’évènement « GotFocus(…) » de la page « Shell ». 

image
Une fois authentifié sur l’« API Gateway », on peut alors faire usage de l’« API App » en lui communiquant le jeton délivré par AAD.
La première étape consiste à appeler la méthode « GetUserName() » pour afficher l’email de l’utilisateur dans la page « Welcome ». 
image
La méthode « GetUserName() » [de même que la méthode « LaunchSearch() » d’invocation du service de recherche] s’appuie sur un proxy que l’on peut obtenir par la méthode « CreateDocSearchAPI() ». Cette dernière est automatiquement générée par la fonction d’ajout « Azure API App Client » de Visual Studio. Son appel est réalisé depuis l’objet « _appServiceClient() » [lui-même à l’origine de l’appel de la méthode « LoginAsnc() »].
imageElle offre la possibilité de mettre en cache des jetons d'authentification, afin que l'application n’ait pas besoin d'authentifier l'utilisateur à chaque appel grâce à l’utilisation de la classe « TokenExpiredHandler() ». Le code utilisé pour cette classe est issu de l’exemple Azure Cards. Il s’agit d’une évolution d’une classe fournie initialement pour l’authentification sur « Azure Mobile Services » (nom de code « Zumo », que l’on retrouve d’ailleurs dans l’implémentation…).

Pour gérer le cas où ce jeton expire, la classe « TokenExpireHandler() » dérive de la classe « DelegatingHandler() », une sous-classe de « HttpMessageHandler() ». 
imageLorsque sa méthode « SendAsync » est appelée, elle envoie d'abord le message vers le reste du pipeline HTTP, et si elle obtient une réponse non autorisée (« HttpStatusCode.Unauthorized »), elle tente d'abord de se connecter au service, puis envoie à nouveau la requête avec le jeu d'en-têtes d'authentification approprié.
image

Comme la demande est susceptible d’être envoyée plusieurs fois, elle et clonée…
 
Nous venons donc d’intégrer un mécanisme d’authentification vis-à-vis d’« Azure Active Directory » sur un service « API App » avec mise en cache des jetons d’authentification depuis une application universelle.
Le prochain et dernier article devrait être plutôt court, je vous indiquerai les éléments permettant de récupérer le code et de déployer rapidement cette application….