Accessing the ASP.NET Authentication, Profile and Role Service in Silverlight

In ASP.NET 2.0, we introduced a very powerful set of application services in ASP.NET (Membership, Roles and profile).  In 3.5 we created a client library for accessing them from Ajax and .NET Clients and exposed them via WCF web services.    For more information on the base level ASP.NET appservices that this walk through is based on, please see Stefan Schackow's excellent book Professional ASP.NET 2.0 Security, Membership, and Role Management.

In this tutorial I will walk you through how to access the WCF application services from a directly from the Silverlight client.  This works super well if you have a site that is already using the ASP.NET application services and you just need to access them from a Silverlight client.   (Special thanks to Helen for a good chunk of this implantation)

Here is what I plan to show:

1. Login\Logout
2. Save personalization settings
3. Enable custom UI based on a user's role (for example, manager or employee)
4. A custom log-in control to make the UI a bit cleaner

image

 

You can download the completed sample solution

Part I: Login\Logout
In VS, do File\New select the Silverlight solution.  Let's call it "ApplicationServicesDemo".

image

 

We will need both the client side Silverlight project and the ASP.NET serverside project.

image

 

Let's configure our system with the test users.  To do this we will use the ASP.NET Configuration Manager.  In VS, under the Website menu, select "ASP.NET Configuration". Use this application to add a couple of users.  I created two employees:

ID:manager
password:manager!
and
ID:employee
password:employee!

image

 

To expose the ASP.NET Authentication system, let's add a new WCF service.  Because we are just going to point this at the default one that ships with ASP.NET, we don't need any code behind, so the easiest thing to do is to add a new Text File.  In the ASP.NET website, Add New Item, select Text File  and call it "AuthenticationService.svc"

image

 

Add this one line as the contents of the file.  This wires it up to the implementation that ships as part of ASP.NET.

<%@ ServiceHost Language="C#" 
Service
="System.Web.
ApplicationServices.AuthenticationService"
%>

Now in Web.config, we need to add the WCF magic to turn the service on.

  <system.serviceModel>
    <services>
      <!-- this enables the 
WCF AuthenticationService endpoint
--> <service name=
"System.Web.ApplicationServices
.AuthenticationService
" behaviorConfiguration=
"AuthenticationService
TypeBehaviors
"> <endpoint contract=
"System.Web.ApplicationServices.
AuthenticationService
" binding="basicHttpBinding"
bindingConfiguration
="userHttp" bindingNamespace=
"http://asp.net/ApplicationServices/v200"/> </service> </services> <bindings> <basicHttpBinding> <binding name="userHttp"> <!-- this is for demo only.
Https/Transport security is recommended
--> <security mode="None"/> </binding> </basicHttpBinding> </bindings> <behaviors> <serviceBehaviors> <behavior name=
"AuthenticationServiceTypeBehaviors"> <serviceMetadata httpGetEnabled="true"/> </behavior> </serviceBehaviors> </behaviors> <!-- this is needed since this service is
only supported with HTTP protocol
--> <serviceHostingEnvironment
aspNetCompatibilityEnabled
="true"/> </system.serviceModel>

Now, still in Web.config, we need to enable forms authentication.  Under the <system.web> change the authentication mode from "Windows" to "Forms".

<authentication mode="Forms" />

One last change to web.config, we need to enable authentication to be exposed via the web service.This is done by adding a System.Web.Extensions section.

  <system.web.extensions>
    <scripting>
      <webServices>
        <authenticationService enabled=
"true" requireSSL="false"/> </webServices> </scripting> </system.web.extensions>

 

Now, to consume this authentication service in Silverlight, let's open the page.xaml file and add some initial UI. Just buttons to log "employee" and "manager"  in and a textblock to show some status.

    <Grid x:Name="LayoutRoot" 
Background
="White"> <StackPanel> <Button x:Name="employeeLogIn" Width="100" Height="50" Content="Log In Employee" Click="employeeLogIn_Click"></Button> <Button x:Name="managerLogIn" Width="100" Height="50" Content="Log In Manager" Click="managerLogIn_Click"></Button> <TextBlock x:Name="statusText"></TextBlock> </StackPanel> </Grid>



Now, let's add a reference to the service we just created

Right click on the Silverlight project and select Add Service Reference

image

 

Click Discover and set the namespace to "AuthenticationService"

image

 

If you get an error at this point, it is likely something wrong with your AuthenticationService.svc or the web config, go back and double check those.

Now, let's write a little code to call that service to log us in.  First add the right using statement

using ApplicationServicesDemo.AuthenticationServices;

Then, in employeeLogIn_Click method write the code to call the service to log the employee in.  For now, we will hard code the name in password, but by the end we will be prompting the user to get this data.

First we create a the web services client class, then we call the login method asynchronously.  Remember all network calls in Silverlight are async, otherwise we'd lock up the whole browser.  Finally we sign up for the callback.

private void employeeLogIn_Click
(object sender, RoutedEventArgs e) { AuthenticationServiceClient client =
new
AuthenticationServiceClient(); client.LoginAsync("employee", "employee!", "
"
, true, "employee"); client.LoginCompleted +=
new EventHandler
<LoginCompletedEventArgs>(client_LoginCompleted); }

In the callback, for now, let's just set our status.

void client_LoginCompleted
(object sender, LoginCompletedEventArgs e) { if (e.Error != null) statusText.Text =
e.Error.ToString(); else statusText.Text = e.UserState +
" logged In result:" + e.Result;

}

Run it!  You should see a good status.  Try changing the password and ID, and see the status change to false.  It is working.

image

 

Now do the same thing for manager and you are set!

private void managerLogIn_Click
(object sender, RoutedEventArgs e) { AuthenticationServiceClient client =
new
AuthenticationServiceClient(); client.LoginCompleted +=
new
EventHandler
<LoginCompletedEventArgs>
(client_LoginCompleted); client.LoginAsync("manager",
"manager!", "", true, "manager"); }
Next Page - Part 2: Save Personalization Settings

Part 2: Save Personalization Settings

In this part I will show how to leverage the ASP.NET profile system to store and retrieve data tied to a particular user.  The beautiful thing about this is there is no explicit database configuration required.

First, let's go back to the ASP.NET server project and add the ProfileService.svc.  The easiest way to do this might be to copy the AuthenticationService.svc change the file name and then change the contents as show below.

<%@ ServiceHost Language="C#" Service=
"System.Web.ApplicationServices.
ProfileService"
%>

Now, we need to enable the service via WCF configuration in web.config.  This looks just like the config for the prfofile service.  In the system.serviceModel\services section add a new node.

      <!-- this enables the WCF 
ProfileService endpoint
--> <service name=
"System.Web.ApplicationServices.
ProfileService
" behaviorConfiguration=
"ProfileServiceTypeBehaviors"> <endpoint contract=
"System.Web.ApplicationServices.
ProfileService
" binding="basicHttpBinding"
bindingConfiguration
="userHttp" bindingNamespace=
"http://asp.net/
ApplicationServices/v200
"/> </service>

and in the system.serviceModel\behaviors\serviceBehaviors add a new node

        <behavior name=
"ProfileServiceTypeBehaviors"> <serviceMetadata
httpGetEnabled
="true"/> </behavior>



Now we need to configure the profile system.  Again, in web.config add a section listing all the profile properties.  In the System.web section, add the following node.

    <profile>
      <properties>
        <add name="Color" type=
"string" defaultValue="Red" /> </properties> </profile>

 

Now we need to enable profile service to be accessed from the webservice. To do this, add the following node to the system.web.extensions\scripting\webservices section.

        <profileService enabled=
"true" readAccessProperties=
"Color" writeAccessProperties=
"Color"/>

Now, let's go to the Silverlight client and add a reference to this service.  This is the same as we did authentication service. Add Service Reference, Discover and select the profileService.

image

 

Now, we need to add a little UI to the page.xaml in order to give us a way to view and edit the personalized setting.

            <StackPanel Orientation=
"Horizontal"
HorizontalAlignment=
"Center"> <
TextBlock>Favorite Color:
</TextBlock> <TextBox x:Name=
"colorNameBox"

Width
="100" Height=
"25"></
TextBox> <Button x:Name=
"submitButton"

Width
="50" Height="25" Content="submit" Click=
"submitButton_Click"
>
</
Button> </StackPanel>

Now, let's extend loginComplete to retrieve all the profile properties for the user that just logged in.   Again, we need to do that asynchronously so we don't block the browser.

void client_LoginCompleted
(object sender,
LoginCompletedEventArgs
e) { if (e.Error != null) statusText.Text =
e.Error.ToString(); else { statusText.Text = e.UserState +
" logged In result:" + e.Result; ProfileServiceClient client =
new
ProfileServiceClient(); client.GetAllProperties
ForCurrentUserAsync(false); client.GetAllProperties
ForCurrentUserCompleted +=
new
EventHandler
<GetAllPropertiesFor
CurrentUserCompletedEventArgs
>
(client_GetAllProperties
ForCurrentUserCompleted); } }

When we get the property values back, we just set the LayoutRoot to have that background.

void client_GetAllProperties
ForCurrentUserCompleted
(object sender,
GetAllPropertiesForCurrent
UserCompletedEventArgs
e) { if (e.Error == null) { colorNameBox.Text =
e.Result["Color"]; ChangeBackgroundColor
(e.Result["Color"]); } }
private void ChangeBackgroundColor
(string colorName) { SolidColorBrush brush =
new
SolidColorBrush(); switch (colorName.ToLower()) { case "black": brush.Color = Colors.Black; break; case "blue": brush.Color = Colors.Blue; break; case "brown": brush.Color = Colors.Brown; break; case "green": brush.Color = Colors.Green; break; case "orange": brush.Color = Colors.Orange; break; case "purple": brush.Color = Colors.Purple; break; case "yellow": brush.Color = Colors.Yellow; break; case "red": brush.Color = Colors.Red; break; case "white": default: brush.Color = Colors.White; break; } LayoutRoot.Background = brush; }

Finally, when the submit button is pressed we need to set the value on the server.. for completeness, I show waiting until the result comes back from the server before setting the background locally.

private void submitButton_Click
(object sender, RoutedEventArgs e) { ProfileServiceClient client =
new
ProfileServiceClient(); Dictionary<string, object> properites =
new
Dictionary<string,object>(); properites.Add("Color",colorNameBox.Text); client.SetPropertiesForCurrentUserAsync
(properites, false, properites); client.SetProperties
ForCurrentUserCompleted +=
new EventHandler
<SetPropertiesForCurrent
UserCompletedEventArgs
>
(client_SetPropertiesFor
CurrentUserCompleted); } void client_SetPropertiesFor
CurrentUserCompleted
(object sender,
SetPropertiesForCurrentUser
CompletedEventArgs
e) { Dictionary<string, object>
properites = e.UserState as
Dictionary<string, object>; ChangeBackgroundColor
((string)properites["Color"]); }

The end result looks good!  Notice the employee and the manager can each have different values for color.

image

 

image

 

 

Next Page - Part 3. Enable Custom UI Based on a User's Role

© 2008 SYS-CON Media