This article is compatible with the latest version of Silverlight.
Most developers working today in Silverlight probably have an ASP.NET background. I personally started with ASP.NET development way back in 2001 with the very first version of the platform. Over time, the ASP.NET platform evolved immensely and gathered some great features. One of these nice additions to the platform is the ASP.NET Membership API, introduced with ASP.NET 2.0. This API allows us to manage user authentication, role management and user profiling with ease. With ASP.NET 3.5, Microsoft added the so-called ASP.NET Application Services to make it possible to use this API from client-side technologies.
Why am I telling you all this, we’re here to learn something new about Silverlight?! In this article, I’ll show you how you can take advantage of the ASP.NET Membership API from within your Silverlight applications. We can use the authentication to allow or deny users access to a Silverlight application, block certain features if a user is not part of a particular role or use profile information.
The small application we’ll build inside this article, SilverlightSecureBank, is shown in the screenshot below. The code can be downloaded here.
As explained, we can leverage the authentication, role and profile mechanisms. Let’s start by looking at how we can authenticate from Silverlight.
Logging in with Silverlight
Let’s assume you’re the developer of an online banking application. The application was built a few years back with ASP.NET 2.0. Because of this, all user information is managed using the ASP.NET Membership API: the database contains usernames, passwords etc. managed by this API. Now the day has come that your bosses have decided they want to move part of the application to Silverlight. One of the requirements is of course that existing user accounts should keep working, so that the transition to this new application is smooth for the end users.
Luckily, you’re a developer who read this article and you know this requirement is easy to fulfill! The sample application already contains a SQL Server Express database with an existing regular user (john/password&1). Creating this database can be done using the ASP.NET Web Administration Tool (more info on this can be found here: http://msdn.microsoft.com/en-us/library/yy40ytx0.aspx).
In order to make it possible for a user to log on in the Silverlight applications using his existing credentials, we need to expose the ASP.NET Membership functionality as a service. However, we don’t need to create this service ourselves, since it already exists in the form of the before mentioned Application Services. The only thing that’s left for us is creating an endpoint for this service and enabling it via configuration.
Creating the endpoint is nothing more than creating an empty *.svc file (without code-behind, since the code is already available within ASP.NET). In this file, named AuthenticationService.svc here, the following code should be pasted:
<%@ ServiceHost Language="C#" Service="System.Web.ApplicationServices.AuthenticationService" %>
As can be seen, the service’s functionality is referenced here. This service needs to be configured just like a normal WCF service. To do so, inside the web.config file, the following code should be placed between the <configuration></configuration> tags.
<system.serviceModel>
<services>
<service name="System.Web.ApplicationServices.AuthenticationService"
behaviorConfiguration="AuthenticationServiceBehaviors">
<endpoint contract="System.Web.ApplicationServices.AuthenticationService"
binding="basicHttpBinding" />
</service>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<behaviors>
<serviceBehaviors>
<behavior name="AuthenticationServiceBehaviors">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
We still need to enable this application service though, since this is not done by default for every application. This can be done by adding the following configuration code, again between <configuration></configuration> tags.
<system.web.extensions>
<scripting>
<webServices>
<authenticationService enabled="true" requireSSL="false"/>
</webServices>
</scripting>
</system.web.extensions>
Also, check that the authentication mode is set to Forms:
<system.web>
<roleManager enabled="true" />
<authentication mode="Forms" />
<compilation debug="true" targetFramework="4.0" />
</system.web>
The service is now ready to be used by the Silverlight application. Like with regular services, we can add a service reference to it, resulting in Silverlight creating a proxy class. The service can then be used just like any other service. In the sample solution, a ChildWindow is shown to allow the user to log in, as shown in the screenshot below.
More importantly, let’s take a look at the code to log in the user. Upon clicking on the OK Button, the service is invoked asynchronously. We use the Login method here, which was exposed on the ASP.NET Application service.
private void OKButton_Click(object sender, RoutedEventArgs e)
{
Authentication.AuthenticationServiceClient proxy = new Authentication.AuthenticationServiceClient();
proxy.LoginCompleted += new EventHandler<Authentication.LoginCompletedEventArgs>(proxy_LoginCompleted);
proxy.LoginAsync(UserNameTextBox.Text, UserPasswordBox.Password, "", true);
}
When the service call is complete, we can check if the login was successful or not using the following simple lines of code:
void proxy_LoginCompleted(object sender, Authentication.LoginCompletedEventArgs e)
{
if (e.Error != null)
ErrorTextBlock.Text = e.Error.ToString();
else if(e.Result == false)
{
ErrorTextBlock.Text = "Error with login, try again";
}
else
{
this.DialogResult = true;
}
}
Apart from the Login method, several other operations of the ASP.NET Membership API are available, such as Logout, ValidateUser and IsLoggedIn. The latter comes in handy to allow us to check if the user has previously already logged in successfully. The ASP.NET Membership API uses authentication cookies on the client machine to perform this check and these are created in the exact same way when using these services from Silverlight. The code below performs this check upon initial loading of the application:
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Authentication.AuthenticationServiceClient proxy = new Authentication.AuthenticationServiceClient();
proxy.IsLoggedInCompleted += new EventHandler<Authentication.IsLoggedInCompletedEventArgs>(proxy_IsLoggedInCompleted);
proxy.IsLoggedInAsync();
}
void proxy_IsLoggedInCompleted(object sender, Authentication.IsLoggedInCompletedEventArgs e)
{
if (e.Result)
{
//Set up the UI for logged in access
UserLoginSuccess();
}
else
{
//user is already logged in
var loginWindow = new LoginWindow();
loginWindow.Closed += new EventHandler(loginWindow_Closed);
loginWindow.Show();
}
}
Because authentication cookies are used, once we’re logged in, we’ll remain so between requests, even if we close the browser.
We can now go a step further and check from Silverlight if the user is member of a specific role.
Checking the user’s role
Roles are also managed by the ASP.NET Membership API. Using roles, we have the ability to group users and more easily manage the permissions within an application. Silverlight can take advantage of the role management just like it did with the authentication.
Similarly with the authentication service, we need an *.svc endpoint (in the sample this endpoint is called RoleService.svc). The code for this *.svc file is shown below:
<%@ ServiceHost Language="C#" Service="System.Web.ApplicationServices.RoleService" %>
Also similar are the changes we need to do in the web.config. These are shown below:
<system.web.extensions>
<scripting>
<webServices>
...
<roleService enabled="true"/>
</webServices>
</scripting>
</system.web.extensions>
<system.serviceModel>
<services>
...
<service name="System.Web.ApplicationServices.RoleService" behaviorConfiguration="RoleServiceBehaviors">
<endpoint contract="System.Web.ApplicationServices.RoleService" binding="basicHttpBinding" />
</service>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<behaviors>
<serviceBehaviors>
...
<behavior name="RoleServiceBehaviors">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
With the role management now exposed on the service, we can use it from within a Silverlight application. In the database that comes with the download for this article, an “Administrators” role was created. One user (administrator/password&1) is added to this role. When a user from this role logs in, the application will add additional functionality to the screen in the form of a button called ChangeBalanceButton (giving each and every user access to this button would not be beneficial in a real world bank!).
On completion of a successful login attempt, the application will perform a second service call to check if the logged-in user is part of this role. In the below code, the service call is launched and when the callback is invoked, the Result property of the GetRolesForCurrentUserCompletedEventArgs parameter contains an array of strings, representing the roles the logged-in user is part of. If this array contains the string Administrators, the button’s visibility is set to true.
void loginWindow_Closed(object sender, EventArgs e)
{
...
RoleService.RoleServiceClient roleProxy = new RoleService.RoleServiceClient();
roleProxy.GetRolesForCurrentUserCompleted +=
new EventHandler<RoleService.GetRolesForCurrentUserCompletedEventArgs>(roleProxy_GetRolesForCurrentUserCompleted);
roleProxy.GetRolesForCurrentUserAsync();
...
}
void roleProxy_GetRolesForCurrentUserCompleted(object sender, RoleService.GetRolesForCurrentUserCompletedEventArgs e)
{
if (e.Result.Contains("Administrators"))
{
ChangeBalanceButton.Visibility = System.Windows.Visibility.Visible;
}
}
We’ll finish this article by looking at how we can leverage the profiles from ASP.NET within our Silverlight applications as well.
Changing the application based on the user’s profile
Profiles in the ASP.NET Membership API are used to store user specific information over several requests. For example, we could build a website where the user can change the theme (forums quite often offer this functionality). Of course, this is a per-user setting. Based on the logged-in user, we can retrieve this information. ASP.NET Membership takes care of storing this information and retrieving it.
As you could expect, Silverlight can take advantage of profile information as well. For example, we can change the application so that the user can select his preferred background color. This preference we will then store as profile information.
To get profiles working within our application, let’s first perform the necessary changes within the hosting site. Similarly, we need again an *.svc endpoint. In the sample, this is called ProfileService.svc.
<%@ ServiceHost Language="C#" Service="System.Web.ApplicationServices.ProfileService" %>
<configuration>
<system.web>
...
<profile>
<properties>
<add name="Color" type="string" defaultValue="LightGray" />
</properties>
</profile>
</system.web>
<system.web.extensions>
<scripting>
<webServices>
...
<profileService enabled="true" readAccessProperties="Color" writeAccessProperties="Color"/>
</webServices>
</scripting>
</system.web.extensions>
<system.serviceModel>
<services>
...
<service name="System.Web.ApplicationServices.ProfileService" behaviorConfiguration="ProfileServiceBehaviors">
<endpoint contract="System.Web.ApplicationServices.ProfileService" binding="basicHttpBinding" />
</service>
</services>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
<behaviors>
<serviceBehaviors>
...
<behavior name="ProfileServiceBehaviors">
<serviceMetadata httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
We can now use the profile information in our application by first adding a service reference to the service from within the Silverlight application. In the application, two buttons were added so that the user can select his preferred background color once logged in. If a color, stored as string, is found in the profile information, this is then used to set the background color. That means that there are actually two parts here: setting the information and retrieving it.
Let’s first look at the code to retrieve the profile information. The service sends back a Dictionary<string, object>, where the key is “Color” and the value the stored string.
private void CheckProfileInformation()
{
ProfileService.ProfileServiceClient profileProxy = new ProfileService.ProfileServiceClient();
profileProxy.GetAllPropertiesForCurrentUserCompleted +=
new EventHandler<ProfileService.GetAllPropertiesForCurrentUserCompletedEventArgs>
(profileProxy_GetAllPropertiesForCurrentUserCompleted);
profileProxy.GetAllPropertiesForCurrentUserAsync(true);
}
void profileProxy_GetAllPropertiesForCurrentUserCompleted(object sender, ProfileService.GetAllPropertiesForCurrentUserCompletedEventArgs e)
{
if (e.Error == null)
{
string color = e.Result["Color"].ToString();
switch (color)
{
case "LightGray": LayoutRoot.Background = new SolidColorBrush(Colors.LightGray);
break;
case "Red": LayoutRoot.Background = new SolidColorBrush(Colors.Red);
break;
case "Blue": LayoutRoot.Background = new SolidColorBrush(Colors.Blue);
break;
default: LayoutRoot.Background = new SolidColorBrush(Colors.LightGray);
break;
}
}
}
When the user clicks on a button, his or her new selection is stored. To do so, we have to create the Dictionary<string,object> ourselves and pass this along to the service. This is done in the code below:
private void ChangeColorForUser(string color)
{
ProfileService.ProfileServiceClient profileProxy = new ProfileService.ProfileServiceClient();
Dictionary<string, object> profileInfos = new Dictionary<string, object>();
profileInfos.Add("Color", color);
profileProxy.SetPropertiesForCurrentUserAsync(profileInfos, false, profileInfos);
profileProxy.SetPropertiesForCurrentUserCompleted +=
new EventHandler<ProfileService.SetPropertiesForCurrentUserCompletedEventArgs>(profileProxy_SetPropertiesForCurrentUserCompleted);
}
void profileProxy_SetPropertiesForCurrentUserCompleted(object sender, ProfileService.SetPropertiesForCurrentUserCompletedEventArgs e)
{
CheckProfileInformation();
}
Summary
Using the power of the ASP.NET Membership API, we can easily use authentication in our Silverlight applications. This integration makes it possible to easily migrate existing ASP.NET applications to Silverlight, since the authentication mechanism can be leveraged. This way, when migrating, users don’t even need to change their user accounts.
Bio
Gill Cleeren is Microsoft Regional Director (www.theregion.com), MVP ASP.NET, INETA speaker bureau member and Silverlight Insider. He lives in Belgium where he works as .NET architect at Ordina. Passionate about .NET, he’s always playing with the newest bits. In his role as Regional Director, Gill has given many sessions, webcasts and trainings on new as well as existing technologies, such as Silverlight, ASP.NET and WPF. He also leads Visug (www.visug.be), the largest .NET user group in Belgium. He’s the author of the upcoming book called Silverlight 4 Data and Services Cookbook. You can find his blog at www.snowball.be.