The New Hands-on Lab on ACS + WP7 + OAuth2.0 OData Service
After the new content storm of yesterday, here I’ll start to look at some individual pieces. The first one I’d like to feature is the new ACS+WP7 phone, based on the work of Caleb from the ACS team (hear the details from the man himself here). It’s available both online in out msdn course and offline in Identity Developer Training Kit and Windows Azure platform Training Kit.
I could summarize the content, but why doing the job twice? We’ve already written a very complete set of instructions for it, hence (for the joy of your RSS readers) below I am pasting the entire lab manual. Enjoy!
Overview
Smartphones are the ultimate personal devices. Users expect to be able at any moment to extract the phone from a pocket and instantly gain access to his online data and services, just like he would from his PC. In order to accomplish that level of access from the phone, two basic prerequisites need to be satisfied:
- The user must be able to use his existing identities from mobile applications
- Services and data must be exposed in ways that are suitable to be securely consumed by mobile clients
The Windows Azure platform can help. By taking advantage of the Windows Azure AppFabric Access Control Service (ACS) , your application can outsource authentication management and broker access to commonly used social identity providers (such as Facebook, Windows Live ID, Google and Yahoo) and business providers (such as Active Directory and any other directory product with federation capabilities).
By exposing services via the OData protocol, a REST-based open standard, you are guaranteed to be accessible from the widest range of platforms and clients. ACS and Windows Identity Foundation, a .NET component which simplifies authentication and authorization development tasks, can work in synergy to use the OAuth 2 protocol for securing OData calls from mobile applications.
In this hands-on lab you will extend a simple Windows Phone 7 application and associated Windows Azure-hosted service by integrating ACS authentication in the user experience and leverage it for securing service calls.
Objectives
In this hands-on lab, you will learn how to:
- Use ACS for handling authentication in a Windows Phone 7 application.
- Consume an OData service from a Windows Phone 7 application using OAuth 2.
- Use Windows Identity Foundation to handle OAuth2-protected calls to a WCF OData service.
- Authorize calls to a WCF OData service according to types and values of incoming claims
Prerequisites
The following is required to complete this hands-on lab:
- IIS 7 (with ASP.NET, WCF HTTP Activation)
- Microsoft .NET Framework 4.0
- Microsoft Visual Studio 2010 Professional or higher
- Windows Azure Tools for Microsoft Visual Studio 1.4
- Windows Phone 7 SDK 10.0
- Windows Identity Foundation Runtime
- Windows Identity Foundation SDK 4.0
Setup
For convenience, much of the code used in this hands-on lab is available as Visual Studio code snippets. To check the prerequisites of the lab and install the code snippets:
- Open a Windows Explorer window and browse to the lab’s SourceSetup folder.
- Double-click the Dependencies.dep file in this folder to launch the Dependency Checker tool and install any missing prerequisites and the Visual Studio code snippets.
- If the User Account Control dialog is shown, confirm the action to proceed.
This process may require elevation. The .dep extension is associated with the Dependency Checker tool during its installation. For additional information about the setup procedure and how to install the Dependency Checker tool, refer to the Setup.docx document in the Assets folder of the training kit.
Using the Code Snippets
Throughout the lab document, you will be instructed to insert code blocks. For your convenience, most of that code is provided as Visual Studio Code Snippets, which you can use from within Visual Studio 2010 to avoid having to add it manually.
If you are not familiar with the Visual Studio Code Snippets, and want to learn how to use them, you can refer to the Setup.docx document in the Assets folder of the training kit, which contains a section describing how to use them.
Exercises
This hands-on lab includes the following exercises:
- Authenticating Users via ACS and invoking via OAuth2 an OData Service in a Windows Phone 7 app
Estimated time to complete this lab: 30 minutes.
When you first start Visual Studio, you must select one of the predefined settings collections. Every predefined collection is designed to match a particular development style and determines window layouts, editor behavior, IntelliSense code snippets, and dialog box options. The procedures in this lab describe the actions necessary to accomplish a given task in Visual Studio when using the General Development Settings collection. If you choose a different settings collection for your development environment, there may be differences in these procedures that you need to take into account.
Exercise 1: Authenticating Users via ACS and invoking via OAuth2 an OData Service in a Windows Phone 7 app
Let’s say that you developed a Windows Phone 7 application, composed by one Silverlight client and one OData service, which allows the creation and editing of to-do lists.
The to-do lists are saved in the cloud, in Window Azure’s storage, via the OData service (hosted in Windows Azure as well). The application has no concept of individual users, hence all the lists are accessible by anyone who runs the client. Wouldn’t it be nice to enable each user to define his own private lists?
In order to add the private lists feature, the application needs to gain the two new capabilities below:
- the ability to recognize each user, hence to add authentication capabilities in the WP7 app
- the ability to restrict access to the saved lists according to the identity of the current user, hence to add authentication and authorization capabilities to the OData service
Adding authentication capabilities to your WP7 application can be very complicated if you have to handle everything from scratch: luckily, you don’t have to. The Windows Azure AppFabric Access Control Service (ACS) can take care of brokering authentication for you, enabling the user to authenticate with well-known providers. If you want to know more about ACS, you can take a look at the dedicated hands-on labs in the training kit. For the purpose of this lab, the main way in which you will interact with ACS is via a Silverlight control, which you can include in your WP7 application and that will take care of all runtime interactions with the service. All that is left to do for you is to configure the service, via its handy management portal, and specify your preferences such as which identity providers you want to enable and similar.
Adding authentication and authorization capabilities to the OData service, again, could be a lot of work if approached from the wrong angle. The current way of securing REST services is via the OAuth 2 protocol. The protocol per se is pretty simple and handling it directly would be feasible, however as you have learned going through this training kit taking a dependency on the specific details of the protocol leads to high-maintenance code. Once again, luckily you don’t have to. Windows Identity Foundation (WIF) does not support OAuth 2 out of the box, however its extensibility model can be leveraged to add new protocols. In this hands-on lab you will use some WIF extensions which enable you to secure WCF REST services via OAuth 2. This will allow you to keep your service code completely free from authentication- and authorization-specific code; it will also allow you to leverage the rich WIF programming model for handling claims-based authorization just like you would when using the out-of-the-box protocols.
This first exercise will be all about adding those two capabilities to the described application. More in detail, you will follow the sequence below:
- Configure ACS to work with your application and services
- Modify the WP7 application to authenticate users via ACS
- Modify the OData service to accept OAuth2-secured calls and handle authorization
Figure 1
A high level description of the architecture and flow of the solution of Exercise 1.
Task 1 – Exploring the Initial Solution
In this task, you will explore the initial solution to familiarize with its structure.
- Open Visual Studio 2010 as an administrator. Go to File | Open | Project menu and select the Begin.sln located in SourceEx1-SecureODataWP7Begin folder of the lab.
- Press F5 to run application: the WP7 emulator and a browser representing the OData service will appear. The first screen in the WP7 application presents you with the to-do lists already created. For the purpose of this lab, the lists are stored in a local SQL database.
Figure 2
WP7 clientFrom here you will be able to explore the lists and work with them with the following operations:
- Creating a task list
- Adding a task
- Marking a task as completed
All those operations correspond to a call to the OData service.
- Go back to Visual Studio and stop debugging.
- Let’s explore the Visual Studio solution. Open the Solution Explorer and browse the code. You will note that the solution contains three projects: MyTodo.Cloud, MyTodo.Phone and MyTodo.Services.
Figure 3
Begin Solution projects- MyTodo.Phone: This is a Silverlight for Windows Phone/Windows Phone Application project , the main WP7 application. It contains the usual Silverlight views, resources and data model. It also contains the necessary classes to communicate with the OData service.
- MyTodo.Services: This is the OData WCF service responsible for handling to-do lists operations.
- MyTodo.Cloud: The OData service is hosted in Windows Azure: this project provides a description of the Service project in term of Windows Azure role.
Feel free to explore the code if you want to: we kept the solution as simple as possible in order to make it easier to add the extra functionalities in the following tasks.
Task 2 – Configuring AppFabric Access Control Service with multiple identity providers
In this task, you will configure your own Access Control Service namespace to use multiple identity providers and recognize the MyTodo OData service as a registered application. You will do everything in this task through the Windows Azure AppFabric management portal.
Note. ACS makes use of claims-based identity. In claims-based identity the successful outcome of one authentication operation is expressed by issuing a security token. A security token contains a list of attributes (called claims) which describe the authenticated user. In a nutshell, ACS operates by brokering token requests to multiple identity providers (IPs); once ACS receives a token from one IP, it applies some processing logic to it and issues a transformed token to the application it protects. The advantage for the application is that it is now decoupled from the details of how every IP operates, and just needs to deal with one single broker. Everything is based on open standards, hence there is no lock-in; the moment in which the application developer wants to use a different service, switching is a simple config line away.
Giving a concise yet exhaustive description of ACS and claims-based identity is difficult, but operating the service is actually very simple. The indications in this task are mainly aimed at helping you to move along through the hands-on lab scenario. If you want to gain a deeper understanding of how ACS works, please refer to the other ACS hands-on labs in the training kit.
- Navigate to https://windows.azure.com/and log in with your Live ID credentials.
- In the navigation pane, select Service Bus, Access Control & Caching and click on Access Control.
- Click New Namespace to create a new namespace.
- The portal displays a dialog. Type in a name for your Namespace, select a region, choose a Subscription and click the Create Namespace button. Make sure to validate the availability of the name first. Service names must be globally unique as they are in the cloud and accessible by whomever you decide to grant access.
Figure 4
Creating a namespace - Take note of the service namespace you used.
- The service namespace will take some time to be provisioned. When the new namespace status becomes active, click on the Access Control Service button.
Figure 5
Open the ACS management portal - This launches (in another browser window or tab) the Access Control Service Management Portal, shown in the figure below.
Figure 6
The ACS management portal home page - Let’s start by adding the identity providers from which we want to authenticate users from. On the navigation pane, select Identity Providers. Windows Live ID is already configured: click Add to add a new identity provider to the list.
Figure 7
The list of configured identity provider and the link for adding a new oneFigure 8
Adding a new identity provider - Under the “Add preconfigured identity provider” section, select Google and click Next.
- On the “Login Page Settings” form leave the default settings and click Save.
Figure 9
Adding a new identity provider - Repeat steps 6 to 8 for adding Yahoo as another identity provider (select Yahoo in step 7).
- Now, you will add Facebook as another identity provider. To do this, browse to http://www.facebook.com and login with any existing account or obtain a new Facebook account.
- Install the Facebook Developer application by clicking Go to app at http://www.facebook.com/apps/application.php?id=2345053339.
Figure 10
Installing the Developer app - Click Allow to enable the Developer app access your basic information.
Figure 11
Allowing the app request for permission - In the Facebook Developer application, click Set Up New Application to create a new application.
Figure 12
Set up new app - In the Application Name field, enter MyTodo. Then agree to the terms, and click Create Application.
Figure 13
Create the app - Complete the security check by entering the captcha code in the text box and click Submit.
- Click the Web Site tab in the left panel.
- In the Site URL field, enter the URL to your ACS Service Namespace (e.g. https://[yourservicenamespace].accesscontrol.windows.net/) as shown below. This is the fully-qualified domain name shown in the URL while you are accessing the ACS management portal.
Figure 14
Web site core settings - Click Save Changes at the bottom of the screen.
- In the resulting page, copy the following information. This will be used to add the Facebook application in the ACS portal:
- Application ID
- Application Secret
Figure 15
App created - On the Access Control Service management portal main page, click Identity providers.
- Click Add to add another identity provider.
- Select Facebook application, and click Next.
- In the Application ID field, enter the API key copied from your Facebook application.
- In the Application secret field, enter the application secret copied from your Facebook application.
- Clear the Application permissions field since we do not need any special permission.
- Leave the default values in the Login Page Settings section and click Save.
Figure 16
Add Facebook application - The next step is telling ACS about the MyTodo service, the resource we are authenticating users for. In identity jargon, the application that requires authentication is known as relying party. On the navigation pane, select Relying parties and click the Add link on top of the “Relying Party Applications” table.
Figure 17
Adding a new relaying party - Fill the form with the following values and leave the fields not in the list with its default value:
- Name: MyTodo
- Realm: uri:mytodo
- Token format: SWT
- Token signing key: click Generate and take note of the generated key
The settings above are suitable for a REST service, especially for what concerns the token format. The Simple Web Token, or SWT, is a compact token format which works well with REST style calls in which tokens are sent in HTTP headers or other parts of the HTTP request.
The token signing key used here is a very important factor in our authentication scenario, as the MyTodo service will use it for verifying the authenticity of incoming tokens. More about this in task #3.Figure 18
Add relying party - Click Save.
- The last thing you need to do in the ACS configuration is defining which user information supplied by the identity providers should be made available to the myTodo service. In ACS, that’s done by defining claims transformation rules. On the navigation pane, select Rule Groups.
- In the “Rule Groups” table select Default Rule Group for MyTodo.
Figure 19
Default Rule Group for MyTodo - In this scenario, we have no special processing logic for the tokens: we just need to pass-through the user information form the identity provider to the MyTodo service. ACS provides a handy shortcut for automatically generating pass-through rules. On the top of the “Rules” table, click Generate.
Figure 20
Generate rules for MyTodo - Select all the identity providers to generate rules for and click Generate.
Figure 21
Generate rules for all the identity providers configured - Click the Add link on top of the Rules table to configure a new rule.
Figure 22
Add new rule - Fill the form with the following values:
- If…
- Claim issuer / Identity Provider: Windows Live ID
- (And) Input claim type / Select type: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier
- (And) Input claim value / Enter value: Any
- Then…
- Output claim type / Select type: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
- Output claim value: Pass through input claim value
- Rule information
- Description: Copy nameidentifier to name
not every identity provider supply the same list of claims. This additional rule was added because Windows Live ID does not provide a claim for name, which can be a problem given the fact that name is what (usually) provides the value for the principal’s name at the relying party. With this rule, you are using the nameidentifier claim value (which Windows Live ID does provide) to create a new name claim in the output token.
Figure 23
Pass through nameidentifier claim value to name claim - If…
- Click Save.
Figure 24
Rule created
You are done with configuring ACS. Now it’s time to start modifying the projects.
Task 3 – Securing an OData Service with OAuth2 and Windows Identity Foundation
In this task, you will add an OData Service that will provide authentication to the solution. To do that, you will include an OData authentication project, provided by this Hands-On-Lab and then you will update the configuration .
For simplicity, the MyTodo solution provided in this lab does not use HTTPS. In a real world scenario, it is highly recommended to use HTTPS for the communication with the secure service.
- Add the DPE.OAuth2 project from the assets folder to the solution. To do this, in the Solution Explorer, right click the MyTodo solution, select Add and click Existing Project. Browse to [Lab folder]SourceAssetsDPE.OAuth and open the DPE.OAuth.csproj file.
- Add a reference to the DPE.OAuth project in the MyTodo.Services project. In the solution explorer, right click the MyTodo.Services project and select Add Reference. In the Add Reference dialog select the Projects tab and double click the DPE.OAuth project.
- Open the Web.config file.
- Add a configuration section for configuring the OAuth module. Add the following code as the first element under the configuration section.
XML
<configuration> <configSections> <section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" /> </configSections> <connectionStrings>
- Specify the WIF modules that are required for authenticating the service requests: ProtectedResourceModule, the WIF extension that handles OAuth2, and the OOB WIF modules WSFederationAuthenticationModule and SessionAuthenticationModule. To do this, replace the existing empty modules element inside system.webServer with the following highlighted code.
XML
<system.webServer> <validation validateIntegratedModeConfiguration="false" /> <modules runAllManagedModulesForAllRequests="true"> <add name="ProtectedResourceModule" type="Microsoft.Samples.DPE.OAuth.ProtectedResource.ProtectedResourceModule" /> <add name="WSFederationAuthenticationModule" type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler" /> <add name="SessionAuthenticationModule" type="Microsoft.IdentityModel.Web.SessionAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" preCondition="managedHandler" /> </modules> <handlers /> </system.webServer>
- Add the WIF configuration element, specialized for the OAuth2 module. Insert the following code before the closing </configuration> element.
XML
</system.serviceModel> <microsoft.identityModel> <service name="OAuth"> <audienceUris> <add value= "uri:mytodo" /> </audienceUris> <securityTokenHandlers> <add type="Microsoft.Samples.DPE.OAuth.Tokens.SimpleWebTokenHandler, Microsoft.Samples.DPE.OAuth" /> </securityTokenHandlers> <issuerTokenResolver type="Microsoft.Samples.DPE.OAuth.ProtectedResource.ConfigurationBasedIssuerTokenResolver, Microsoft.Samples.DPE.OAuth"> <serviceKeys> <add serviceName="uri:mytodo" serviceKey="[Insert Symmetric Token Signing Key]" /> </serviceKeys> </issuerTokenResolver> <issuerNameRegistry type="Microsoft.Samples.DPE.OAuth.ProtectedResource.SimpleWebTokenTrustedIssuersRegistry, Microsoft.Samples.DPE.OAuth"> <trustedIssuers> <add issuerIdentifier="https://[Service Namespace].accesscontrol.windows.net/" name="MyTodo" /> </trustedIssuers> </issuerNameRegistry> </service> </microsoft.identityModel> </configuration>
You don’t need to understand the fine details of that configuration element in order to use the OAuth2 module; however if you are a WIF expert and you are curious about the meaning of the various elements there, here there’s a quick description.
The SecurityTokenHandlers element is used for registering a new SecurityTokenHandler class, SimpleWebTokenHandler, which enhances WIF with the capability of processing SWT tokens.The issuerTokenResolver element contains a custom TokenResolver class, which tells WIF how to check the signature of incoming SWT tokens; this allows you to specify directly in config the symmetric key you defined in ACS for signing tokens for the MyTodo application.
The issuerNameRegistry section contains another custom class, which leverages the WIF extensibility model: this time the class enables WIF to list a SWT issuer, instead of the out of the box issuers which are expected to sign tokens with X.509 certificates.
Let me stress that you are not required to understand anything of the above explanation in order to take advantage of OAuth2 in your service. You could have skipped this note altogether and your ability of taking advantage of the feature would not be diminished in the least.
- In the WIF configuration code you have just added, replace the placeholders with the values you recorded when configuring ACS: Enter the service key for MyTodo application (task 2, step 30) and the service namespace (task 2, step 4).
- That’s pretty much it! All that’s left is making sure that there are no extra paths through which unauthenticated users can access the service. Open the MyTodoDataService.cs file from the MyTodo.Services project.
- Add the following using statements.
(Code Snippet – ACS and Windows Phone 7 – Ex01 MyTodoDataService UsingOAuth)
C#
using System.Web; using Microsoft.Samples.DPE.OAuth;
- Create a function to return an unauthorized response if the current user is not authenticated. To do this, insert the following code in the MyTodoDataService class.
(Code Snippet – ACS and Windows Phone 7 – Ex01 MyTodoDataService ThrowIfNotAuthenticated)
C#
private static void ThrowIfNotAuthenticated() { var identity = HttpContext.Current.User.Identity; if (identity == null || !identity.IsAuthenticated) { OAuthHelper.SendUnauthorizedResponse(new OAuthError { Error = OAuthErrorCodes.UnauthorizedClient }, HttpContext.Current); } }
- Override the method OnStartProcessingRequest and call ThrowIfNotAuthenticated to validate the current user before processing any request to the service.
(Code Snippet – ACS and Windows Phone 7 – Ex01 MyTodoDataService OnStartProcessingRequest)
C#
protected override void OnStartProcessingRequest(ProcessRequestArgs args) { ThrowIfNotAuthenticated(); base.OnStartProcessingRequest(args); }
Task 4 – Adding Authorization for Private Lists
At this point, the MyTodo OData service has been secured: any existing clients invoking the service without adding authentication information will not gain access. However, we still don’t have implemented the private list feature: right now you need to be authenticated in order to call the service, but once you do it, there is no restriction to which lists you can see. In this task, you will add authorization logic that will properly restrict access in order to implement the private lists feature.
- Add a reference to the Microsoft.IdentityModel library in the MyTodo.Services project. In the solution explorer, right click the MyTodo.Services project and select Add Reference. In the Add Reference dialog select the .NET tab, select the Microsoft.IdentityModel component and click OK.
- Open the MyTodoDataService.cs file from the MyTodo.Services project.
- Add the following using statement.
C#
using Microsoft.IdentityModel.Claims;
- Create a UserName property to get the name claim value in the MyTodoDataService class.
(Code Snippet – ACS and Windows Phone 7 – Ex01 MyTodoDataService UserName)
C#
private string UserName { get { var identity = HttpContext.Current.User.Identity as IClaimsIdentity; if (identity != null) { var claimName = identity.Claims.Single(c => c.ClaimType == ClaimTypes.Name); if (claimName != null) { return claimName.Value; } } throw new DataServiceException(401, "Unauthorized"); } }
- Find the OnChangeTaskList function and insert code to set the UserName property to the current user for add operations. Now that every newly created list has an owner associated to it, the delete and update operations can be restricted to work only if the current user is indeed the owner of the targeted list. Replace the OnChangeTaskList function body with the following code.
(Code Snippet – ACS and Windows Phone 7 – Ex01 MyTodoDataService OnChangeTaskList)
C#
[ChangeInterceptor("TaskLists")] public void OnChangeTaskList(TaskList taskList, UpdateOperations operation) { var userName = this.UserName; if (operation == UpdateOperations.Add) { taskList.UserName = userName; if (taskList.Id == Guid.Empty) { taskList.Id = Guid.NewGuid(); } } else if (operation == UpdateOperations.Delete || operation == UpdateOperations.Change) { var originalList = this.GetOriginalTaskList(taskList.Id); if (originalList == null || originalList.UserName != userName || taskList.UserName != userName) { throw new DataServiceException(401, "Unauthorized"); } } }
Note: This function contains authorization code. Normally it is good practice to try externalizing that code as much as possible, and WIF’s extensibility model encourages that via classes such as ClaimsAuthorizationManager. However, in OData the relevant details of a request are often embedded in the structure of the request URI itself, which would be laborious to parse from ClaimsAuthorizationManager; for that reason, here we kept things simple and placed all logic here.
- Execute the same updates to the OnChangeTask function; replace the OnChangeTask function body with the following code.
(Code Snippet – ACS and Windows Phone 7 – Ex01 MyTodoDataService OnChangeTask)
C#
[ChangeInterceptor("Tasks")] public void OnChangeTask(Task task, UpdateOperations operation) { var userName = this.UserName; if (operation == UpdateOperations.Add) { if (task.Id == Guid.Empty) { task.Id = Guid.NewGuid(); } task.UserName = userName; task.StartDate = DateTime.UtcNow; task.DueDate = DateTime.MaxValue; task.TimestampUpdate = DateTime.UtcNow; } else if (operation == UpdateOperations.Delete || operation == UpdateOperations.Change) { var originalTask = this.GetOriginalTask(task.Id); var originalList = this.GetOriginalTaskList(originalTask.ListId); if (originalTask == null || originalList == null || originalList.UserName != userName || originalTask.UserName != userName || task.UserName != userName || task.ListId != originalTask.ListId) { throw new DataServiceException(401, "Unauthorized"); } if (operation == UpdateOperations.Change) task.TimestampUpdate = DateTime.UtcNow; } }
- Add query interceptors to filter the private lists and the items that are not owned by the current user. Then, include the public interceptor.
(Code Snippet – ACS and Windows Phone 7 – Ex01 MyTodoDataService QueryInterceptors)
C#
[QueryInterceptor("Tasks")] public Expression<Func<Task, bool>> QueryTasks() { return c => c.TaskList.IsPublic == 1 || c.TaskList.UserName.Equals(HttpContext.Current.User.Identity.Name, StringComparison.OrdinalIgnoreCase); } [QueryInterceptor("TaskLists")] public Expression<Func<TaskList, bool>> QueryTaskLists() { return c => c.IsPublic == 1 || c.UserName.Equals(HttpContext.Current.User.Identity.Name, StringComparison.OrdinalIgnoreCase); }
That’s it for this task. You augmented the MyTodo service with authentication and authorization logic, now it’s up to the clients to comply with the new access policy.
- Open ListView.xaml from the Views folder in the MyTodo.Phone project and include a textblock to display when a list is private or public. To do that, replace the existing stack panel content with the following markup:
XAML
... <ListBox.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal" Margin="0,0,0,30"> <Image x:Name="ItemImage" Source="/Microsoft.Samples.MyTodo.Phone;component/Resources/Images/ArrowImg.png" Height="43" Width="43" VerticalAlignment="Top" Margin="10,0,20,0"/> <StackPanel> <TextBlock x:Name="ItemText" Text="{Binding Name}" Margin="-2,-13,0,0" Style="{StaticResource PhoneTextExtraLargeStyle}" TextWrapping="Wrap" Width="395" /> <StackPanel Orientation="Horizontal" Margin="0,-6,0,3"> <Image Source="/Microsoft.Samples.MyTodo.Phone;component/Resources/Images/PublicImg.png" Height="30" Width="30" Visibility="{Binding IsPublic, Converter={StaticResource VisibilityConverter}, ConverterParameter=1}" /> <TextBlock Text="Public" Style="{StaticResource PhoneTextSubtleStyle}" Margin="0" Visibility="{Binding IsPublic, Converter={StaticResource VisibilityConverter}, ConverterParameter=1}" /> <Image Source="/Microsoft.Samples.MyTodo.Phone;component/Resources/Images/PublicImg.png" Height="30" Width="30" Opacity="0.35" Visibility="{Binding IsPublic, Converter={StaticResource VisibilityConverter},ConverterParameter=0}" /> <TextBlock Text="Private" Style="{StaticResource PhoneTextSubtleStyle}" Margin="0" Visibility="{Binding IsPublic, Converter={StaticResource VisibilityConverter},ConverterParameter=0}" /> <TextBlock Text=" | Tasks: " Style="{StaticResource PhoneTextSubtleStyle}" Margin="0" /> <TextBlock Text="{Binding Tasks.Count}" Style="{StaticResource PhoneTextSubtleStyle}" Margin="0" /> </StackPanel> </StackPanel> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> ...
Task 5 – Adding Authentication to a WP7 Application
In this task, you will finally add authentication to the WP7 application. In order to do that you will include a project containing a control which wraps much of the communication with ACS and session management, then you will write the necessary code to integrate the control in your app and configure it to use your ACS namespace
- Add the SL.Phone.Federation project from the assets folder to the solution. To do this, in the Solution Explorer, right click the MyTodo solution, select Add and click Existing Project. Browse to [Lab folder]SourceAssetsSL.Phone.Federation and open the SL.Phone.Federation.csproj file.
- Rebuild the solution.
- Add a reference to the SL.Phone.Federation project in the MyTodo.Phone project. In the solution explorer, right click the MyTodo.Phone project and select Add Reference. In the Add Reference dialog select the Projects tab and double click the SL.Phone.Federation project.
- Open the App.xaml file and define a namespace prefix for the SL.Phone.Federation.Utilities.
XAML
<Application x:Class="Microsoft.Samples.MyTodo.Phone.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:fed="clr-namespace:SL.Phone.Federation.Utilities;assembly=SL.Phone.Federation">
Verify you added the fed namespace definition inside the Application opening tag.
- Add the following resource in App.xaml (set Realm, ServiceNamespace and RequestSecurityTokenReponseStore properties).
XAML
<!--Application Resources--> <Application.Resources> <fed:RequestSecurityTokenResponseStore x:Key="rstrStore" /> ... </Application.Resources>
- You’ll need a new page which will host the authentication experience. Add a new page in the Views folder and name it LoginView.xaml. In the Solution Explorer, right click the Views folder, select Add and click New Item. In the Add New Item dialog, select Windows Phone Portrait Page, set the name to LoginView.xaml and click Add.
Figure 25
Adding a new Windows Phone view
- Update the Text property from the ApplicationTitle TextBlock to get its value from the ApplicationNameString resource and set the Text property from the PageTitle TextBlock to “log in”.
XAML
<!--TitlePanel contains the name of the application and page title--> <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> <TextBlock x:Name="ApplicationTitle" Text="{StaticResource ApplicationNameString}" Style="{StaticResource PhoneTextNormalStyle}"/> <TextBlock x:Name="PageTitle" Text="log in" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/> </StackPanel>
- Define a namespace prefix for SL.Phone.Federation.Controls.
XAML
<phone:PhoneApplicationPage x:Class="Microsoft.Samples.MyTodo.Phone.Views.LoginView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:fed="clr-namespace:SL.Phone.Federation.Controls;assembly=SL.Phone.Federation" FontFamily="{StaticResource PhoneFontFamilyNormal}" ...
- Now that you have the login page in place, you can instantiate the control that will take care of the authentication mechanics. Inside the “ContentPanel” Grid control, include the following code to configure an AccessControlServiceSignIn control.
XAML
<!--ContentPanel - place additional content here--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <fed:AccessControlServiceSignIn x:Name="SignInControl" Realm="uri:mytodo" ServiceNamespace="[Service Namespace]" RequestSecurityTokenResponseStore="{StaticResource rstrStore}" /> </Grid>
The AccessControlServiceSignIn wraps most of the logic required to handle the sign-in operation; at this point you need to integrate the control with various elements of the application so that the authentication experience appears seamless
- Replace the [Service Namespace] placeholder in the AccessControlServiceSignIn control with your account service namespace to be used.
- Open the ListsView.xaml.cs file and add the following using statement.
C#
using SL.Phone.Federation.Utilities;
- The SignInControl persists tokens on the phone’s isolated storage, so that one authenticated session can be maintained across multiple runs of the application (until the token is no longer valid or the user explicitly logs out. Define and initialize the _rstrStore class variable with the rstrStore resource.
C#
public partial class ListsView : PhoneApplicationPage { RequestSecurityTokenResponseStore _rstrStore = App.Current.Resources["rstrStore"] as RequestSecurityTokenResponseStore;
Note. Saving a token on the phone’s storage is not very secure. The isolated storage may prevent other applications from stealing the persisted token, but it does not prevent somebody with physical access to the device to eventually get to it. Any form of encryption would not solve the issue if the decryption key resides on the phone, no matter how well it is hidden. You could require the user to enter a PIN at every application run, and use the PIN to decrypt the token, however the approach presents obvious usability and user acceptance challenges.
While we wait for a better solution to emerge, it may be of some consolation considering that saving token is much better than saving direct credentials such as a username/password pair. A token is typically scoped to be used just with a specific service, and it has an expiration time: this somewhat limits what an attacker can do with a token, whereas no such restrictions would be present should somebody acquire username and password.
- At load time, the ListView page should load the ViewModel data if there is a valid session (i.e. there is a valid token in storage) or redirect to the login page. Find the Loaded event in the ListsView constructor and change it according to the snipped below.
(Code Snippet – ACS and Windows Phone 7 – Ex01 ListView ListsView)
C#
public ListsView() { InitializeComponent(); this.PageTransitionReset.Begin(); this.Loaded += (sender, args) => { if (!_rstrStore.ContainsValidRequestSecurityTokenResponse()) { NavigationService.Navigate(new Uri("/Views/LoginView.xaml", UriKind.Relative)); } else { this.PageTransitionIn.Begin(); this.ViewModel.Reload(); } }; ...
If you use the provided code snippet in Visual Studio, you will have to move the transition and reload code inside the else block.
- Rebuild the solution.
- Here there’s the main trigger that will show the authentication UI. Open LoginView.xaml page. Move the cursor to the page root element and switch to the Events view in the Properties window (press F4 if you do not see the properties window). Double click on the Loaded property and add the following code to handle the RequestSecurityTokenResponseCompleted event.
(Code Snippet – ACS and Windows Phone 7 – Ex01 ListView PhoneApplicationPage)
C#
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e) { SignInControl.RequestSecurityTokenResponseCompleted += new EventHandler<SL.Phone.Federation.Controls.RequestSecurityTokenResponseCompletedEventArgs>(SignInControl_RequestSecurityTokenResponseCompleted); SignInControl.GetSecurityToken(); }
- Once the sign-in control completed an authentication request, it will raise the RequestSecurityTokenResponseCompleted event that you wired up in the former step. The handler below ensures that in case of successful login the application will navigate back to the page that triggered the authentication operation. Add the SignInControl_RequestSecurityTokenResponseCompleted method using the snippet below.
(Code Snippet – ACS and Windows Phone 7 – Ex01 ListView SignInControl)
C#
void SignInControl_RequestSecurityTokenResponseCompleted(object sender, SL.Phone.Federation.Controls.RequestSecurityTokenResponseCompletedEventArgs e) { if (e.Error == null) { if (NavigationService.CanGoBack) { NavigationService.GoBack(); } } }
- You need to provide a UI element for triggering the logout operation. Open the ListsView.xaml page and insert the following code snippet at the bottom before the closing PhoneApplicationPage element to create an application bar and a log out button to it.
XAML
<phone:PhoneApplicationPage.ApplicationBar> <shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> <shell:ApplicationBarIconButton IconUri="/Resources/Images/LogoutImg.png" Text="log out" Click="LogOut_Click"/> </shell:ApplicationBar> </phone:PhoneApplicationPage.ApplicationBar>
- Here there is the logout logic. Open to the ListsView.xaml.cs file and create the LogOut_Click event. The code clears the stored token and navigates to the LoginView.xaml page.
(Code Snippet – ACS and Windows Phone 7 – Ex01 ListView LogOutClick)
C#
private void LogOut_Click(object sender, EventArgs e) { if (MessageBox.Show("Are you sure you want to log out?", "Log out", MessageBoxButton.OKCancel) == MessageBoxResult.OK) { _rstrStore.RequestSecurityTokenResponse = null; NavigationService.Navigate(new Uri("/Views/LoginView.xaml", UriKind.Relative)); } }
- The moment in which the application actually uses the token is when it invokes the service. Open the TodoService.cs file from the Services folder.
- You need to ensure that every time the service is invoked, the token is injected in the call according to the OAuth2 specification. A good way of adding the necessary logic so that it is triggered for every call is to add it to the GetContext method. Find the GetContext function and update the SendingRequest event to add an “Authorization” request header with the issued token.
(Code Snippet – ACS and Windows Phone 7 – Ex01 DataServiceContext GetContextAddAuth)
C#
private DataServiceContext GetContext() { var dataService = new DataServiceContext(serviceUri); dataService.MergeOption = MergeOption.OverwriteChanges; dataService.SendingRequest += (sender, args) => { var store = App.Current.Resources["rstrStore"] as SL.Phone.Federation.Utilities.RequestSecurityTokenResponseStore; args.RequestHeaders["Authorization"] = "OAuth " + store.SecurityToken; }; return dataService; }
Exercise 1: Verification
It is time to give your newly secured Windows Phone 7 application a spin.
In order to verify that you have correctly performed all steps in exercise one, you will first create a private list and one task. Then, you will log in with a different user to verify that the created list is not visible.
To perform this verification you need to have at least two accounts in either Windows Live ID, Google, Yahoo! or Facebook.
- Set the target deployment device to Windows Phone 7 Emulator.
Figure 26
Selecting the Windows Phone 7 deployment service
- Set the MyTodo.Cloud project as the StartUp project. To do this, in the Solution Explorer, right click the MyTodo.Cloud project and select Set as StartUp Project.
- Start debugging the cloud service by pressing F5. The compute emulator should start and a browser with the OData service endpoint should open.
Figure 27
OData service endpoint
Since the solution contains a Windows Phone 7 project and the target deployment is the Windows Phone 7 Emulator, Visual Studio will also start up the emulator when you run the cloud project but the Phone project will not start debugging: you have to start the debug session manually as explained below.
- In the solution explorer, right click the MyTodo.Phone project, select Debug and click Start New Instance. This will open the MyTodo application on the login page.
Figure 28
MyTodo Login
The sign-in control queries your ACS namespace to dynamically retrieve the list of identity providers you configured in task.
- Log in to the application with your account information. After that you will enter to MyTodo:
Figure 29
Windows Phone 7 embedded browser
- Create a new list in the application. To do that, write the list name in the textbox at the bottom and click the + button to save it.
Figure 30
Adding a list
- Click the new list name link to open it, and then create several task items using the textbox at the bottom. Click ‘+’ to save the task item:
Figure 31
Adding task items to the list
Only the owner of a public list can remove or add items from it. If you try to edit a list you do not own, you will get a Service Error exception.
- Click the back button to return to the application lists, and click the log out icon at the bottom of the application.
Figure 32
Back and Log Out buttons
- Log in again using a different account than the one used in step 4. You will notice you cannot see the tasks lists created by other users:
Figure 33
Using a different login account
- Repeat steps 6 to 9 and login with the first account to see you will see only the private lists that where created by the current users and the already existing public lists.
- Log out from the application.
Figure 34
Logging out from the application