In my previous article, we had discussed about Data Binding, Validation, Exception Handling, Handling Multiple tables and many more. But the first step in a business application deals with Authentication. Surely nobody wants data to be meshed up by an anonymous user so in this article, we are going to implement Authentication to SOI (States of India) application. This post is restricted to FormsAuthentication
model only. Also, I am going to skip Authorization and due for next article.
Well, authentication in Silverlight 4 using RIA service in business application can be implemented in 2 ways:
- Using Default authentication provided by Silverlight Business Template (using ASP.NET Role and Membership)
- Using Custom Authentication (using your own database and model )
You can find a number of posts/articles available in blogs that deal with default authentication with ASP.NET membership provider. If you want to know more about it, then this MSDN article is worth a look .
So let's start with Authentication using your own custom table for application.
Scenario
This article is going to demonstrate the steps involved for integrating authentication to States of India (SOI App) using a custom table which will store basic information such as UserName
, Password
and Email
. Before proceeding with SOI app, let's have a look into the app with my previous articles.
So here the application aims to have the following functionality:
- The User has access to the Home page, where she/he can view the
State
and its information - But Adding a new state or Editing state requires user to be authenticated
Steps Involved For Implementing Authentication
Before going into the details of each step, the image below shows the overall picture/steps. Follow the Arrow mark for sequence, as there is a human tendency of reading from Left to Right :).
Adding a New Table to Database and Updating the Data Model
The first step is to add a table to application database. Here, I have added a new table called UserDetail
with the following fields:
Then, go to your Model at server side project and update the Model
. In case you need the details of setting up an Entity Data Model, then refer to my earlier post here.
Select the newly added UserDetail
table and proceed. Along with the newly added entity to data model, it will be as below. If you notice that, you can mark that the UserName
column in the table is mapped as Name
to UserDetail
entity. We will discuss about it later in this post.
The Basic Server Side Setup for Custom Authentication
Changes to WebConfig File
As we opted for Forms Authentication, the very first change we need is to add the authentication in webconfig file. So add the following section to the webconfig file at server side project.
<authentication mode="Forms">
</authentication>
The Concept
Before I proceed with the rest of the article, let's see some concepts and take a look at how it will works. When you use a business template in Silverlight, Visual Studio adds Authentication service derived from AuthenticationBase<T>
and User Derived form UserBase
. The user information and roles are stored with default ASP.NET membership provider table ASPNETDB.
But we are going to use UserDetail
table instead of default ASPNETDB
and our own Domainservice
to handle authentication. Here comes the IAuthentication
and Iuser
interfaces.
So our Custom DomainService
is going to be implement IAuthentication<T>
, where T
is the type of UserDetail
, in turn, the interface will make available the UserDetail
to both client and server side.
As the UserDetail
entity is going to be propagated to client side using IAuthentication
, it requires to implement IUser
interface. The update of Entity Data Model adds UserDetail
entity to model and generates the following piece of code along side State
and City
entity.
Our Database Table does not hold Name
property as it's required from IUser
interface point of view, so we need to map UserName
to Name
in UserDetail
entity. You can avoid this step if your table has a Name
column. For the time being, here I am going to skip Role
as implemented by IUser
.
Then let's add a class named UserDetail
to implement IUser
interface.
public partial class UserDetail : IUser
{
#region IUser Members
[DataMember]
public IEnumerable<string> Roles
{
get
{
return null;
}
set
{
throw new NotImplementedException();
}
}
#endregion
}
}
So partial classes for UserDetail
at EntityModel
and above UserDetail
sets the valid User
which is going to be used in the Custom Authentication domain service, which we are going to add in the next step.
Adding a DomainService for Authentication
Let's add a new DomainService
named SOIAuthDomainService
without selecting any entity. In the Domain service, implement IAuthentication
interface.
Here, in SOIAuthDomainService
along with the default methods, we will add some more methods for Validation and insertion of a user. And also implement logic for Login, Logout. The DefaultUser
is the user we are going to return in case the login failed. So overall, the class structure will be as below:
The Login Logic in the domain service is as follows:
public UserDetail Login(string userName, string password, bool isPersistent, string customData)
{
if (this.ValidateUser(userName, password))
{
FormsAuthentication.SetAuthCookie(userName, isPersistent);
return this.GetUser(userName);
}
return null;
}
while the ValidateUser
logic follows as below:
private bool ValidateUser(string username, string password)
{
return this.ObjectContext.UserDetails.Any(u => u.Name == username && u.Password == password);
}
As we are allowing User Registration, here let's add InsertUser
method. Later in this post, we will check with implementation:
public void InsertUser(UserDetail user)
{
if ((user.EntityState != EntityState.Detached))
{
this.ObjectContext.ObjectStateManager.ChangeObjectState(user, EntityState.Added);
}
else
{
this.ObjectContext.UserDetails.AddObject(user);
}
}
Client Side Setup
The next step is to add the Registration Service to the ApplicationLifeTime so that it can be accessed using WebContext.Current
. When you built a RIA project, it automatically creates a WebContext class at client side which holds information Authentication and Current User. Here check with the client side generated code:
So in the App.Xaml.cs, add the following:
WebContext webContext = new WebContext();
webContext.Authentication =
new System.ServiceModel.DomainServices.Client.ApplicationServices.FormsAuthentication();
this.ApplicationLifetimeObjects.Add(webContext);
Implementing Authentication Logic at UI
Let's add Login
And UserRegistration
screens to client side Silverlight project.
As my intention is to give a clear picture on authentication, I have not added any validation logic whatsoever. For more information on adding validation logic, refer to my earlier post here.
So coming to this post, let's check with the Login screen.
Login Screen
With the user input details and on Ok Button, click the user credential validated against database using the service. The commented code is self explanatory for each line of code and its intentions.
private void OKButton_Click(object sender, RoutedEventArgs e)
{
biCreateUser.IsBusy = true;
WebContext.Current.Authentication.LoggedIn +=
new EventHandler<AuthenticationEventArgs>(Authentication_LoggedIn);
LoginOperation lop= WebContext.Current.Authentication.Login
((new LoginParameters(txtUserName.Text, txtPassword.Text, true, null)));
lop.Completed += (Authsender, args) =>
{
if (!lop.HasError)
{
if (lop.LoginSuccess)
{
this.DialogResult = true;
}
else
{
lblAuthStatus.Content = "Login Failed";
}
}
else
{
MessageBox.Show(lop.Error.Message);
lop.MarkErrorAsHandled();
}
biCreateUser.IsBusy = false;
};
}
Once the Login operation completed successfully and the on login screen closed, we will display the UserName
on the main page header.
So I am going to add a method called UpdateStatus
in the Mainpage.Xaml.cs and will call it on the login window close.
private void ChildWindow_Closed(object sender, EventArgs e)
{
MainPage mpage = (MainPage)App.Current.RootVisual;
mpage.UpdateStatus();
}
Using Webcontext.Current
, we can check whether the client using the app is authenticated or not.
WebContext.Current.Authentication.User.Identity.IsAuthenticated
So the Update Status code follows as below:
public void UpdateStatus()
{
if (WebContext.Current.Authentication.User.Identity.IsAuthenticated)
{
lblUser.Visibility = System.Windows.Visibility.Visible;
lblUser.Content = WebContext.Current.Authentication.User.Identity.Name;
hbLogin.Content = "Logout";
}
else
{
lblUser.Visibility = System.Windows.Visibility.Collapsed;
hbLogin.Content = "Login";
}
}
User Registration
As shown in the picture, the user will have an option to Register in the Login screen and on demand, the UserRegistration
screen popup for adding a new user. The InsertUserDetail
method in the SOIAuthentication DomainService
class will be called on addition of a new user.
private void OKButton_Click(object sender, RoutedEventArgs e)
{
SOIAuthDomainContext domContext = new SOIAuthDomainContext();
UserDetail uDetail=new UserDetail();
uDetail.Name=txtUserName.Text;
uDetail.Password=txtPassword.Text;
uDetail.Email=txtEmail.Text;
biWait.IsBusy = true;
domContext.UserDetails.Add(uDetail);
SubmitOperation authSO= domContext.SubmitChanges();
authSO.Completed += (authsender, args) =>
{
if (!authSO.HasError)
{
MessageBox.Show("User Created");
this.DialogResult = true;
}
else
{
MessageBox.Show(authSO.Error.Message);
authSO.MarkErrorAsHandled();
}
biWait.IsBusy = false ;
};
}
The Final Punch
The above step lays the foundation for authentication and the Webcontext.Current
can be used throughout the project for additional constraint for each functionality. For example, in SOI app, the AddNewState
and EditState
is now onwards open for authenticated User only.
Conclusion
Microsoft suggests to follow ASP membership provided model for Authentication as it provides more reusability and more security. But authentication using custom table sometimes is unavoidable. I hope this article will help you in many ways either using multiple Domain Services, knowing application classes and of course authentication. Keep posting suggestions.
Source Code and Live Link