Centre de développement Sharepoint 2010
Sharepoint 2010 - MSDN
Tutoriels Sharepoint 2010 sur areaprog
Developper Top 10 Resource Center | Sharepoint
Le toolkit Azure pour Windows Phone simplifie grandement l’intégration d’Azure dans les applications. Depuis peu, il a été divisé en plusieurs packages NuGet permettant de cibler l’intégration d’une fonctionnalité précise (stockage, ACS, notifications Push, …)
L’un des packages permet d’intégrer les fonctionnalités d’une authentification auprès de providers d’identités existants (Facebook, Windows Live, Google,…) grâce à Access Control Services.
Dans mon application, l’utilisateur pourra s’authentifier auprès de ces providers et le jeton d’authentification sera réinjecté dans les requêtes OData envoyées à mon WCF Data Service. Au niveau de mon service, les données ajoutées par un utilisateur authentifié seront rattachées à celui-ci et lui seul y aura accès par la suite.
Revenons à notre Toolkit Azure : notre application utilisera donc ACS et proposera une mire de connexion en fonction du provider choisi. Voici le package NuGet à installer dans votre projet WP7.
Le mode d’emploi est explicite et il est donc relativement aisé d’intégrer une authentification OAuth 2.0 dans une application WP7.
Par contre, la procédure ne détaille pas comment mettre en place le service web pour lequel pour utilisera précisément cette authentification, en réinjectant le jeton dans la requête http OData.
En fait, la solution est un mix entre le tutoriel créé pour l’ancien toolkit et le mode d’emploi installé avec le package NuGet.
Voici comment faire, en quelques étapes…
Notre application WP7 manipule des données fournies par un service OData. Mais seul un utilisateur authentifié sera autorisé récupérer ces données, et seules les données le concernant lui seront renvoyées.
Ce package mettra en place tout ce qu’il faut dans votre projet pour qu’il puisse utiliser ACS et stocker le jeton qui sera réinjecté dans les requêtes OData, par la suite.
Le mode d’emploi ([Your WP7 Project]/App_Readme/Phone.Identity.Controls.BasePage.Readme.htm) s’affiche dès l’installation du package NuGet. Il suffit de suivre les étapes pour le faire fonctionner.
Vous aurez néanmoins besoin d’un ACS, que vous pouvez configurer en suivant les étapes mentionnées ici dans la Task 2.
La page de login s’affiche dans un web browser control, il faut donc le déclarer au niveau du manifest : WMAppManifest.xml
<Capability Name="ID_CAP_WEBBROWSERCOMPONENT"/>
Ajoutez la page de login en tant que page de démarrage de votre application, comme indiqué dans le mode d’emploi.
Lorsque vous démarrez l’application, si votre jeton est encore valide vous naviguerez directement à la page d’accueil de votre application. Dans ce cas, il faut prévoir de sortir de l’application sur le bouton Back. Une manière de réaliser cette opération est de vider l’historique de navigation lorsque l’on arrive sur la page d’accueil.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { base.OnNavigatedTo(e); while(this.NavigationService.BackStack.Any()) { NavigationService.RemoveBackEntry(); } }
A ce niveau, vous devriez être capable de vous logger en vous identifiant auprès d’un de vos providers préférés, par-exemple avec un Live Id:
Une fois que l’authentification a réussi, le jeton est stocké comme un SimpleWebTokenStore dans
Application.Current.Resources["swtStore"]
Le jeton doit être placé dans l’en-tête de la requête Http. Il faut s’abonner à l’évènement SendingRequest de votre contexte WCF Data Service et y mettre à jour l’en-tête en ajoutant l’”authorization”.
_dc = new YourDataServiceContext(new Uri("http://YourDataService.svc/")); _dc.SendingRequest += new EventHandler<SendingRequestEventArgs>(SendingRequest); void SendingRequest(object sender, SendingRequestEventArgs e) { var simpleWebTokenStore = Application.Current.Resources["swtStore"] as SimpleWebTokenStore; if (simpleWebTokenStore != null) { e.RequestHeaders["Authorization"] = "OAuth " + simpleWebTokenStore.SimpleWebToken.RawToken; } }
Attention, l’accès aux Resources ne peut se faire en dehors du thread de l’UI. Si c’est votre cas, il faut stocker le jeton ailleurs pour l’utiliser sans problème depuis l’événement SendingRequest.
Si votre service n’est pas configuré pour fonctionner avec OAuth 2.0, suivez l’étape Task 3 – Securing an OData Service with OAuth2 and Windows Identity Foundation du tutoriel mentionné précédemment.
Votre service utilisera une librairie développée par Microsoft DPE, qui étend le mécanisme de WIF pour supporter OAuth 2.0.
A ce niveau, vous devriez avoir:
L’identité de l’appelant (que l’on récupère depuis le header) va nous servir dans l’application des règles métier de notre service.
[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults = true)] public class YourDataService : DataService<[YourContext]> { // This method is called only once to initialize service-wide policies. public static void InitializeService(DataServiceConfiguration config) { config.SetEntitySetAccessRule("[YourEntity]", EntitySetRights.All); config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3; } string GetUserIdentity() { string userIdName = null; var claim = HttpContext.Current.User.Identity as IClaimsIdentity; if (HttpContext.Current.Request.IsAuthenticated) { userIdName = HttpContext.Current.User.Identity.Name; } return userIdName; }
Vous pouvez utiliser des QueryInterceptor et/ou ChangeInterceptor pour intercepter les requêtes, et ainsi modifier leur comportement par défaut suivant l’identité de l’appelant. Dans mon cas, je stocke l’identifiant de l’appelant dans chaque enregistrement qu’il ajoute dans la base.
[ChangeInterceptor("[YourEntity]")] public void OnChange([YourEntityType] updatedRecord, UpdateOperations operations) { if (operations == UpdateOperations.Add) { var userIdName = GetUserIdentity(); if (userIdName == null) { throw new DataServiceException(401, "Permission Denied you must be an authenticated user"); } updatedRecord.UserId = userIdName; } }
Les requêtes de sélection ne revoient que les enregistrements associés à l’identité de l’appelant. Seul le créateur des enregistrements pourra y accéder.
[QueryInterceptor("[YourEntity]")] public Expression<Func<[YourEntityType], bool>> OnQuery() { var userIdName = GetUserIdentity(); if (userIdName == null) { throw new DataServiceException(401, "Permission Denied you must be an authenticated user"); } return (b => b.UserId == userIdName); }
Placez un point d’arrêt dans le ChangeInterceptor ou le QueryInterceptor pour vérifier si tout ce petit monde fonctionne et si votre identité est récupérée correctement côté serveur dans votre service. Si ce n’est pas le cas, essayez d’utiliser IIS plutôt que le Visual Studio Development Server (merci à Benjamin Guinebertière pour l’astuce !)
Chaque requête OData effectuée depuis notre application Windows Phone inclut un jeton d’authentification, renseigné par le provider d’identité. Cette opération se fait par l’intermédiaire d’ACS. Cette identité peut être utilisée dans des règles métier au niveau du WCF Data Service. Ces règles seront appliquées quel que soit le client duquel provient la requête. Il est ainsi possible de contrôler l’accès et l’édition des données effectués depuis un client aussi simpliste qu’un navigateur web. Sans jeton d’authentification, les requêtes déclencheront un erreur de sécurité (pour ceux qui ne suivent pas : c’est parce que l’on a codé en déclenchant explicitement une exception dans le service).
Vous pourrez manipuler les données publiées en OData depuis votre application WP7 mais également depuis toute application tournant sur n’importe quelle plateforme supportant une authentification OAuth 2.0.
Elle est pas belle la vie ?
The Windows Azure Toolkit for Windows Phone is helping a lot to integrate all kind of Azure features in the device. Recently, Azure Toolkit for Windows Phone has been split in separate parts (storage, ACS, Push notification, …), so that each can be used separately in a project (via NuGet), which is a VERY good thing.
Though, it makes it easy to deal with ACS authentication and external identity providers which is what we need in our WP7 application. We want to get an authenticated user to use our app since the OData service will associate data to this user. The service will also ensure that each user will have access to his own data only.
Back to the Azure Toolkit : we are interested in the ACS part so that our WP7 application provides a log in page and an authenticated user. The NuGet package for this is here:
The How To is very explicit, though, it is very easy to handle an OAuth 2 authentication in a windows phone app.
But the procedure doesn’t explain how to set up your remote service so that it can handle the token you just received, and also doesn’t detail how to send it to the server. Actually the answer is a mix of the tutorial dedicated to the inital toolkit, and the new “How To” procedure.
Here is how to do it in a few steps…
Our WP7 application needs to access data from the OData service, but only an authenticated user will be allowed to. We also want to filter resulting information according to the user identity.
This package will install all your project needs to use ACS and store the token that will be later reinjected in the OData Http request.
The “HowTo” ([Your WP7 Project]/App_Readme/Phone.Identity.Controls.BasePage.Readme.htm) is very explicit and you just need to follow the steps to make it work. by the way, you need to have set up an ACS like explained here in Task 2.
The log in page will be provided from a web browser control, so you should make it available in the manifest file WMAppManifest.xml
Add the log in page as the welcome page in your application, as explained in the How To.
If your token is valid when you start the app, you will skip the log in page and navigate directly to your application home page. So you will have to handle the Back button from there so that you exit the application. You can do it by clearing the navigation history when you navigate to your home page:
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { base.OnNavigatedTo(e); while (this.NavigationService.BackStack.Any()) { NavigationService.RemoveBackEntry(); } }
At this point, you should be able to log into your app with any of your identity provider credentials, for example with a Live ID:
Once the log in procedure succeeds, your token is stored as a SimpleWebTokenStore in
The token will be placed in the header of the http request.You should register to the SendingRequest event of your WCF Data Service context and update the header with the authorization.
The access to the Resources is not allowed from a thread different from the IU thread, so if that may happen in your case, you should save the token in some place from a BeginInvoke so that you can reuse it safely in the SendingRequest event.
If your WCF Data Service has not been setup to handle OAuth 2, you should follow the step Task 3 – Securing an OData Service with OAuth2 and Windows Identity Foundation of the great tutorial mentioned earlier.
You will use an assembly developped by Microsoft DPE guys, that extends the WIF mechanism to handle OAuth.
At this point you should have:
You would probably check the identity on some actions made on your data.
You can use interceptors (QueryInterceptor or ChangeInterceptor) to do some identity validation and relative tasks, according to your business rules. In my case, I store the user identity name in each new record.
On queries, I return only records that are associated to the identity of the request initiator.
You can put a breakpoint on the ChangeInterceptor and QueryInterceptor to check if everything is going fine and if your identity is retrieved properly on the service side. If not, try to use IIS instead of Visual Studio Development Server (thanks to Benjamin Guinebertière for that tip !)
Each OData request made from your WP application is now including an identity token, allowed by the identity provider through ACS. You can use this identity for business rules purpose in your service, and this will be efficient wherever the request comes from. Though, you can protect your data from being accessed and updated from a simple web browser which will return a security exception.
You can still use these data from any application running on any platform that is able to send an OAuth http authorization header.