Custom Membership, Role and Profile: Silverlight RIA Service
By Peter Bromberg
How to enable your Silverlight 3 Application for RIA Services and use the built-in support for your custom Membership, Role and Profile providers.
f you haven't had a look at Silverlight RIA Services yet, I'd recommend it. This
infrastructure simplifies two-way data access via services with Silverlight,
and includes support for Membership, Roles and Profiles. It really represents
a big step in the right direction for simplifying bidirectional data access and
business logic, especially with Silverlight.
In this article I will illustrate how to "hook up" your Silverlight Application
for authentication via Membership, authorization with Roles, and the ability
to save user-entered Profile data. I will use a modified version of the Altairis
classes that are available on codeplex.com. This means we will only have four
tables added to our database - Users, Roles, UsersInRole, and Profiles. All the
data is stored in visible columns which makes it easy to search the profiles
table, for example, for all those users who want to receive your email newsletter.
There are no stored procedures involved.
You will first need to install the RIA Services preview. I recommend that you also download the accompanying PDF, which provides a lot of
detail, and plenty of good code samples. We start by creating a new Silverlight
Project. Note the new checkbox at the bottom of the New Application dialog:

Checking this Link Option is what enables your project for the magic of RIA Services,
along with support for ASP.NET Membership, Roles and Profiles. In my UnBlog post here, I provifde a number of additional resource links that "didn't make it"
into the MIX '09 presentations. Check them out, please.
In the web.config, we need to enable the custom providers with the requisite elements:
connectionStrings>
<clear/>
<add name="LocalSqlServer" providerName="System.Data.SqlClient" connectionString="server=(local);Integrated Security=SSPI;database=TEST"/>
</connectionStrings>
authentication mode="Forms"/>
<membership defaultProvider="MyMembershipProvider">
<providers>
<clear />
<add name="MyMembershipProvider" type="PAB.Web.Providers.SimpleSqlMembershipProvider,PAB.Web.Providers" connectionStringName="LocalSqlServer" RequiresQuestionAndAnswer="true" EnablePasswordRetrieval="true" />
</providers>
</membership>
<roleManager enabled="true" defaultProvider="MyRoleProvider">
<providers>
<clear />
<add name="MyRoleProvider" type="PAB.Web.Providers.SimpleSqlRoleProvider,PAB.Web.Providers" connectionStringName="LocalSqlServer" cacheRolesInCookie="true" />
</providers>
</roleManager>
<profile enabled="true" automaticSaveEnabled="false" defaultProvider="MyProfileProvider">
<providers>
<clear />
<add name="MyProfileProvider" type="PAB.Web.Providers.SimpleSqlProfileProvider,PAB.Web.Providers" connectionStringName="LocalSqlServer" tableName="Profiles" keyColumnName="UserName" lastUpdateColumnName="LastUpdate" />
</providers>
<properties>
<add name="UserName" type="String" customProviderData="UserName;varchar;50" />
<add name="LastUpdate" type="DateTime" customProviderData="LastUpdate;datetime" />
<add name="FirstName" type="String" customProviderData="FirstName;varchar;50" />
<add name="LastName" type="String" customProviderData="LastName;varchar;50" />
<add name="Email" type="String" customProviderData="Email;varchar;300" />
<add name="Newsletter" type="Bool" defaultValue="True" customProviderData="Newsletter;bit;1" />
</properties>
</profile>
You can see above that with the custom Profile provider I use, we can actually define
the fields that match up with our Profile Table. Unlike the opaque way data is
stored in the default provider that ships with ASP.NET, all our "stuff"
is visible in columns that are easily SQL-searchable.
Now we need to create the server-side Service class that RIA will magically pick
up and create a Silverlight proxy for:
using System.Web.Profile;
using System.Web.Ria.ApplicationServices;
using System.Web.Security;
using PAB.Web.Providers;
namespace SLRIAAuth.Web
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web.Ria;
using System.Web.Ria.Data;
using System.Web.DomainServices;
[EnableClientAccess]
public class AuthenticationService : AuthenticationBase<User>
{
}
[EnableClientAccess]
public class User : UserBase
{
public string UserName { get; set; }
public DateTime LastUpdate { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public string Email { get; set; }
public bool Newsletter { get; set; }
}
[EnableClientAccess]
public class MembershipService : DomainService
{
[ServiceOperation]
public bool Authenticate(string userName, string password)
{
return Membership.ValidateUser(userName, password);
}
[ServiceOperation]
public void AddUser(string userName, string password, string email)
{
MembershipCreateStatus createStatus;
Membership.CreateUser(userName, password, email, null, null, true, out createStatus);
if (createStatus != MembershipCreateStatus.Success)
{
throw new DomainServiceException(createStatus.ToString());
}
}
}
}
We need an AuthenticationService, a User class, and a MembershipService. All these
need the [EnableClientAccess] attribute, and methods within them need the [ServiceOperation]
attribute.
One other thing, we need to change the Silverlight control on the ASPX page to host
the User DataContext that enables our Silverlight controls to be "user aware":
<% @ Register Assembly="System.Web.Ria" Namespace="System.Web.Ria" TagPrefix="ria" %>
<ria:SilverlightApplication ID="Silverlight1" runat="server" Source="~/ClientBin/SLRIAAuth.xap" MinimumVersion="3.0.40307.0" Width="100%" Height="100%" />
I don't do any data binding in this sample, but the above lines are your key to two-way
databinding with your Silverlight controls when RIA Services is used.
That takes care of the Server side. Whenever you do anything to your AuthenticationService
class and save it or build the app, a generated proxy class called (In this case)
SLRIAAuth.Web.g.cs is regenerated in your Silverlight application. You'll need
to enable "Show all Files" at the top of the Solution Explorer window
to see this. I generally bring it into the project to get Intellisense. This
will also clear up some VS / Resharper errors related to the fact that the generated
class file was not "included the project".
In the Silverlight Application, you should see something new in the App.xaml file:
<Application.Services>
<appsvc:WebUserService x:Name="UserService" />
</Application.Services>
This is what identifies our service proxy so we can code against it. Now here is
what I do in MainPage.xaml.cs:
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Ria.ApplicationServices;
using System.Windows.Shapes;
namespace SLRIAAuth
{
public partial class MainPage : UserControl
{
UserService _service;
public MainPage()
{
InitializeComponent();
Login();
}
void Login()
{
_service = App.Current.Services[
"UserService"]
as UserService;
UserContext ctx =
new UserContext();
ctx.Service.LoginCompleted +=
new EventHandler<LoginCompletedEventArgs>(Service_LoginCompleted);
LoginParameters parms =
new LoginParameters(
"admin",
"admin",
true);
ctx.Service.Login(parms);
}
void Service_LoginCompleted(
object sender, LoginCompletedEventArgs e)
{
string userName = _service.User.Name;
bool authenticated = _service.User.IsAuthenticated;
bool isAdmin = _service.User.IsInRole(
"admin");
MessageBox.Show(
"User: " +userName +
" Is Authenticated: " +authenticated.ToString( ) +
" Is Admin: " + isAdmin.ToString( ));
SLRIAAuth.Web.User user = (_service.User
as SLRIAAuth.Web.User);
user.Email =
"email@user.com";
user.Newsletter =
true;
user.LastUpdate = DateTime.Now;
user.Firstname=
"Joe";
user.Lastname =
"Blow";
_service.SaveUserCompleted +=
new EventHandler<SaveUserCompletedEventArgs>(service_SaveUserCompleted);
_service.SaveUser();
}
void
service_SaveUserCompleted(object sender, SaveUserCompletedEventArgs e)
{
if (e.Error!=null)
MessageBox.Show(e.Error.ToString());
else
MessageBox.Show("User logged in; profile properties created and saved");
}
}
}
We have a _service variable that is used to hold a reference to our proxy Service,
then use the UserContext to handle a normal call to the Login method. The sample
database SQL script installs a single user, "admin" with password "admin".
In the LoginCompleted event handler, I pop up a MessageBox showing that the user
was authenticated, and their role. Finally, I add the Profile properties for
the user and call _service.SaveUser(); The SaveUserCompleted handler shows the
results. And, if you go look in your database Profiles table, you'll see that
indeed, this user's profile data, which was not in the Profile table after you
ran my table script, has been saved. In a real app, you would of course have
nice Login and User Profile forms and so on; this sample is just a "proof
of concept".
You can download the complete Visual Studio 2008 Silverlight 3 solution here. Be sure to create a database "TEST", and run the provided SQL Script
to populate it. You can do a lot more with Silverlight RIA Services, this is
just the beginning.
It is easy for developers to be intimidated by the complexity of some of these new
offerings. Silverlight RIA services is relatively easy to learn, and it brings
a great deal of new power to you as a Silverlight developer. The March MIX 09
preview does everything it is supposed to do, and it works. The newest May '09
preview fixes bugs, and is even more stable. The addition of RIA Services to
Silverlight applications moves Silverlight 3 into a whole new realm for developers
of LOB Silverlight applications. In particular, have a look at the "Shared"
classes option which allows you to automatically replicate server-side entities
into the Silverlight client. If you take the time to study this, as I have, you
should definitely get "religion" about what Silverlight 3 and RIA Services
is going to offer!
Popularity (10727 Views)
 |
| Biography - Peter Bromberg |
Peter Bromberg is a C# MVP, MCP, and .NET expert who has worked in banking, financial and telephony for over 20 years. Pete focuses exclusively on the .NET Platform, and currently develops SOA and other .NET applications for a Fortune 500 clientele. Peter enjoys producing digital photo collage with Maya,playing jazz flute, the beach, and fine wines. You can view Peter's UnBlog and IttyUrl sites.
|  |
|
|
Article Discussion: Custom Membership, Role and Profile: Silverlight RIA Service
Thanks so much for posting this article
Rachida Dukes replied
to Peter Bromberg at Saturday, October 23, 2010 5:34 AM
That was very quick thanks so much.
Rachida
Password encryption
Sean Capes replied
to Rachida Dukes at Saturday, October 23, 2010 5:34 AM
Hi Peter,
Thanks for the article, very useful. I've extended the provider to use my EntityDataModel but one thing I'm stuck on is how to encrypt/hash the password in Silverlight app and then decrypt when received on the Server. I'm guessing I should create some kind of shared class but I'm not sure about duplicating the machine key that is used for encoding.
Any ideas?
Thanks,
Sean.
ASP.Net Authorization
Paul Seth replied
to Sean Capes at Saturday, October 23, 2010 5:34 AM
Hi Peter,
Thanks for the great articles. However I've hit a snag with this one. I'm using a combination of RIA and the default ASP.Net membership provider. As in your first article on this subject (http://www.eggheadcafe.com/tutorials/aspnet/7cc2760f-50f2-492d-9d62-48ad5c7f70b4/aspnet-membership-and-ro.aspx) I am using web.config to limit access to only SLRIAAuthTestPage.aspx and ClientBin/SLRIAAth.xap. Everthying else is set with <deny users="*">.
The problem is that the Silverlight is now denied access back to the RIA web service. It is not clear to me the service filename to allow access to. In the original article above you used WebServices/AuthenticationService.svc
However, in the RIA model I do not have a .svc file to grant unauthroized access to.
Thanks greatly for your help.
Paul
Please help.
Bob Newbale replied
to Peter Bromberg at Saturday, October 23, 2010 5:34 AM
Hi Piter!
I have a problem and would like to know if it can be sold using RIA
Services.
The project which I'm currently working does not allow
user multiple entries to the system. As a solution we are using session
concept. For example when user login it’s open session which will be closed on
logout. Before actual login application should check if this user has any open
session and asking him to close it if it’s open.
I would be really appreciated for any help.
Thank you.
Try this MD5 implementation
Peter Bromberg replied
to Sean Capes at Saturday, October 23, 2010 5:34 AM
The URI prefix is not recognized.
david findlay replied
to Peter Bromberg at Saturday, October 23, 2010 5:34 AM
Hi Peter,
Thanks for this, however when I run the solution - after creating the database and files - and get an error
"The URI prefix is not recognized" thrown by
ctx.Service.Login(parms);
on the main page.
Any thoughts or pointers?
Cheers
Sounds like you don't have the web application marked as the startup project.
Peter Bromberg replied
to david findlay at Saturday, October 23, 2010 5:34 AM
For this to work, the Silverlight app needs to be launched from an ASPX page.
Chris replied
to Peter Bromberg at Saturday, October 23, 2010 5:34 AM
One thing I can't figure out with RIA is on the *Server*, in a "Business Application" project Authentication service (inherits AuthenticationBase<User>), given a username, how
can I get the UserBase object for it? There is a method this.GetUser() which works on the currently logged in user, but why not an overload to pass in any username?
I see a method this.GetAuthenticatedUser(IPrincipal principal), but this only works if you have an already authenticated IPrincipal to pass in. I also see nothing in
"Membership" class to at least get the profile's custom properties so I could construct a User object myself.
Sorry, but I was not familiar with ASP.NET membership services before now and I may be missing something obvious.
What I really need is a way to get a list of all the users, with their custom profile properties, and a way to update them. Isn't there something in RIA for this?

I don't think the RIA Services implementation implements the entire Membership provider and all it's methods. Plus, since I wrote this article RIA Services has gone through several more implmentations.
Chris replied
to Peter Bromberg at Saturday, October 23, 2010 5:34 AM
I'm sorry Peter for dragging you back here after so much time passed.
It's ok this isn't implemented in RIA now, but could you tell me how this sort of thing could be implemented in a Silverlight application? From everything I've read, it's easy in ASP.Net, but I can't even figure out how to get at the list of users from the server side. How do I access the membership provider so I can get at all this data on the server?
To implement this in a Silverlight app is not difficult. You would simply take your MembershipProvider, and "wrap" a WCF service around the methods that you want to have included. You would do this with a "Silveright-enabled WCF service".
Your Silverlight app would only require a service reference to the new WCF service in order to expose all the methods to your Silverlight app on the client.