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!
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 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.
In this hands-on lab, you will learn how to:
The following is required to complete this hands-on lab:
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:
Note:
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.
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.
This hands-on lab includes the following exercises:
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.
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:
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:
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.
Figure 2 WP7 client
From here you will be able to explore the lists and work with them with the following operations:
All those operations correspond to a call to the OData service.
Figure 3 Begin Solution projects
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.
Figure 4 Creating a namespace
Figure 5 Open the ACS management portal
Figure 6 The ACS management portal home page
Figure 7 The list of configured identity provider and the link for adding a new one
Figure 8 Adding a new identity provider
Figure 9 Adding a new identity provider
Figure 10 Installing the Developer app
Figure 11 Allowing the app request for permission
Figure 12 Set up new app
Figure 13 Create the app
Figure 14 Web site core settings
Figure 15 App created
Figure 16 Add Facebook application
Figure 17 Adding a new relaying party
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
Figure 19 Default Rule Group for MyTodo
Figure 20 Generate rules for MyTodo
Figure 21 Generate rules for all the identity providers configured
Figure 22 Add new rule
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
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.
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>
<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>
</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.
(Code Snippet – ACS and Windows Phone 7 - Ex01 MyTodoDataService UsingOAuth)
C#
using System.Web; using Microsoft.Samples.DPE.OAuth;
(Code Snippet – ACS and Windows Phone 7 - Ex01 MyTodoDataService ThrowIfNotAuthenticated)
private static void ThrowIfNotAuthenticated() { var identity = HttpContext.Current.User.Identity; if (identity == null || !identity.IsAuthenticated) { OAuthHelper.SendUnauthorizedResponse(new OAuthError { Error = OAuthErrorCodes.UnauthorizedClient }, HttpContext.Current); } }
(Code Snippet – ACS and Windows Phone 7 - Ex01 MyTodoDataService OnStartProcessingRequest)
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.
using Microsoft.IdentityModel.Claims;
(Code Snippet – ACS and Windows Phone 7 - Ex01 MyTodoDataService UserName)
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"); } }
(Code Snippet – ACS and Windows Phone 7 - Ex01 MyTodoDataService OnChangeTaskList)
[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.
(Code Snippet – ACS and Windows Phone 7 - Ex01 MyTodoDataService OnChangeTask)
[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; } }
(Code Snippet – ACS and Windows Phone 7 - Ex01 MyTodoDataService QueryInterceptors)
[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.
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
<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.
<!--Application Resources--> <Application.Resources> <fed:RequestSecurityTokenResponseStore x:Key="rstrStore" /> ... </Application.Resources>
Figure 25 Adding a new Windows Phone view
<!--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>
<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}" ...
<!--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
using SL.Phone.Federation.Utilities;
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.
(Code Snippet – ACS and Windows Phone 7 - Ex01 ListView ListsView)
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.
(Code Snippet – ACS and Windows Phone 7 - Ex01 ListView PhoneApplicationPage)
private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e) { SignInControl.RequestSecurityTokenResponseCompleted += new EventHandler<SL.Phone.Federation.Controls.RequestSecurityTokenResponseCompletedEventArgs>(SignInControl_RequestSecurityTokenResponseCompleted); SignInControl.GetSecurityToken(); }
(Code Snippet – ACS and Windows Phone 7 - Ex01 ListView SignInControl)
void SignInControl_RequestSecurityTokenResponseCompleted(object sender, SL.Phone.Federation.Controls.RequestSecurityTokenResponseCompletedEventArgs e) { if (e.Error == null) { if (NavigationService.CanGoBack) { NavigationService.GoBack(); } } }
<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>
(Code Snippet – ACS and Windows Phone 7 - Ex01 ListView LogOutClick)
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)); } }
(Code Snippet – ACS and Windows Phone 7 - Ex01 DataServiceContext GetContextAddAuth)
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.
Figure 26 Selecting the Windows Phone 7 deployment service
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.
Figure 28 MyTodo Login
The sign-in control queries your ACS namespace to dynamically retrieve the list of identity providers you configured in task.
Figure 29 Windows Phone 7 embedded browser
Figure 30 Adding a list
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.
Figure 32 Back and Log Out buttons
Figure 33 Using a different login account
Figure 34 Logging out from the application