Sunday, December 30, 2012

Custom Authentication in Silverlight

“Muggle-born have no access to go inside the chamber of secret Harry, and only a heir of Slytherin can open the chamber. Since he is already here, the chamber of secret, has already been opened.” As you have guessed already, lines said by Albus Dumbledore to Harry Potter. Wonder why Harry Potter? Actually, our Silverlight is very much similar to that of Hogwarts. In case of authentication, I guess, to be precise. Silverlight provide the facility to authenticate the user using a particular query, making sure whether the querying user has the access to use the query or not. Just like you have to authenticate yourself, before the door of any house in Hogwarts, while requesting it to open. If you are not authenticated, your request will be rejected. In silverlight however, you have to authenticate yourself at the very beginning of the application, through a process we call as, Sign in.

Every application in silverlight has the sign in process. You must need to login before you can use the application. Business Template of silverlight do come with this process in build. But herein, we are going to create our own custom authentication classes from the scratch. So let’s jump in with both feet and create the application.

Before everything we need to create the database that we are going to use in the application. In SQL Server 2008 that I am using for the demo, execute the following query.

   1: Create database MyData
   2:  
   3: SET QUOTED_IDENTIFIER OFF;
   4: GO
   5: USE MyData;
   6: GO
   7: IF SCHEMA_ID(N'dbo') IS NULL EXECUTE(N'CREATE SCHEMA [dbo]');
   8: GO
   9:  
  10: -- --------------------------------------------------
  11: -- Dropping existing FOREIGN KEY constraints
  12: -- --------------------------------------------------
  13:  
  14:  
  15: -- --------------------------------------------------
  16: -- Dropping existing tables
  17: -- --------------------------------------------------
  18:  
  19: IF OBJECT_ID(N'[dbo].[UserDetail]', 'U') IS NOT NULL
  20:     DROP TABLE [dbo].[UserDetail];
  21: GO
  22:  
  23: -- --------------------------------------------------
  24: -- Creating all tables
  25: -- --------------------------------------------------
  26:  
  27: -- Creating table 'UserDetails'
  28: CREATE TABLE [dbo].[UserDetail] (
  29:     [UserID] int  NOT NULL,
  30:     [Name] nvarchar(50)  NOT NULL,
  31:     [Password] nvarchar(50)  NOT NULL,
  32:     [Email] nvarchar(50)  NULL  
  33: );
  34: GO
  35:  
  36: -- --------------------------------------------------
  37: -- Creating all PRIMARY KEY constraints
  38: -- --------------------------------------------------
  39:  
  40: -- Creating primary key on [UserID], [Name] in table 'UserDetails'
  41: ALTER TABLE [dbo].[UserDetail]
  42: ADD CONSTRAINT [PK_UserDetails]
  43:     PRIMARY KEY CLUSTERED ([UserID], [Name] ASC);
  44: GO

This will create the Database MyData and a table inside it, UserDetail.


image


Above image shows my UserDetail table in MyData database. Now let us create some users. For that, make some entries in the database.


image


Our database is set and we are ready to go. Next step is to create  blank silverlight application, AuthDemo.


image


When the application is created and opened, you must have greeted with the mainpage.xaml file. So, before we go any further into services etc., let’s first create a beautiful user interface for our application. Add the following lines of xaml into the grid of the main page.



   1: <Grid x:Name="LayoutRoot" Background="White">
   2:     <TextBox Height="23" 
   3:              HorizontalAlignment="Left" 
   4:              Margin="132,92,0,0" Name="TxtName" 
   5:              VerticalAlignment="Top" Width="120" />
   6:     <TextBox Height="23" 
   7:              HorizontalAlignment="Left" 
   8:              Margin="132,121,0,0" Name="TxtPass" 
   9:              VerticalAlignment="Top" Width="120" />
  10:     <Button Content="Login" Height="23" 
  11:             HorizontalAlignment="Left" Margin="177,160,0,0" 
  12:             Name="BtnLogin" Click="BtnLogin_OnClick" 
  13:             VerticalAlignment="Top" Width="75" />
  14:     <sdk:Label Height="28" 
  15:                HorizontalAlignment="Left"
  16:                Margin="84,42,0,0" Name="LoggedInPerson" 
  17:                VerticalAlignment="Top" Width="120" />
  18: </Grid>

Our layout is also ready. It’s time now to do things that a real man do, creating models and services. Entity data models are the ADO.Net entities that are linked to the database, and work as the mediator between the application and the database. I am not here going to show in detail how to create the entity data model in the application. Rather I am assuming that readers at least knows how to create the data model in Silverlight from the database. So create an entity data model basing it on the database that we just have created, MyData, and named it as AuthModel. It’s a good practice to keep model, services, classes and other inside separate folders in the project. So the AuthModel  should be placed into the Model folder.
Build the project once.


If the project build and complied successfully, the time has come to add services to it. First let us add Domain Service. Right click on the service folder(if you have created one, else right click on the whole project itself) in the web project, select add –>New Item, and choose Domain service. Name the service as UserDetailDomainService.


image


After clicking on add button, you will see a dialog box open asking to select the entities to be included in the service. For now the box would only have User Detail. Just check the enable editing box, click ok and you are done.


image


Now ready with the Service and Model. Next thing to do is to add authentication service. Just as you do to add the domain service, do exactly the same to add the authentication service, but this time now, choose Authentication Domain Service instead. Name the service as CustomAuthDomainService. Remove AuthenticationBase<User> and remove the User class then Inherit and implement LinqToEntitiesDomainService<AuthEntities> class and IAuthentication<UserDetail> interface.


Before proceeding further. Create another folder named “Classes” in the web project and add a class UserDetail to it. Add the keyword Partial to make the class a partial class of UserDetail class that already exists in the UserDetailMetadata that we’d created with service. Now implement the IUser interface in the class. On doing so, your class would have become:



   1: namespace AuthDemo.Web.Class
   2: {
   3: public partial class UserDetail :IUser
   4:     {
   5:         public string Name
   6:         {
   7:             get
   8:             {
   9:                 throw new NotImplementedException();
  10:             }
  11:             set
  12:             {
  13:                 throw new NotImplementedException();
  14:             }
  15:         }
  16:  
  17:         public IEnumerable<string> Roles
  18:         {
  19:             get
  20:             {
  21:                 throw new NotImplementedException();
  22:             }
  23:             set
  24:             {
  25:                 throw new NotImplementedException();
  26:             }
  27:         }
  28:     }
  29: }

The Name property is not required. Remove that property, also since the two UserDetail classes (one of Metadata and one above) are partial, that is both the classes are incomplete without each other. Wow! how romantic. So they must be in the same namespace. Since our Metadata’s class was in AuthDemo.Web.Model so we need to change the namespace of this class also from AuthDemo.Web.Classes to AuthDemo.Web.Model. Change the namespace, remove the Name property, then just compile.
Here is the final version of UserDetail Class



   1: namespace AuthDemo.Web.Model
   2: {
   3:     public partial class UserDetail :IUser
   4:     {
   5:         [DataMember]
   6:         public IEnumerable<string> Roles
   7:         {
   8:             get
   9:             {
  10:                 return null;
  11:             }
  12:             set
  13:             {
  14:                
  15:             }
  16:         }
  17:     }
  18: }

Moving back to our CustomAuthentication Service that’s now looks like:



   1: [EnableClientAccess]
   2: public class CustomAuthDomainService : LinqToEntitiesDomainService<AuthEntities>, IAuthentication<UserDetail>
   3: {
   4:     // To enable Forms/Windows Authentication for the Web Application, edit the appropriate section of web.config file.
   5:     public UserDetail GetUser()
   6:     {
   7:         throw new NotImplementedException();
   8:     }
   9:  
  10:     public UserDetail Login(string userName, string password, bool isPersistent, string customData)
  11:     {
  12:         throw new NotImplementedException();
  13:     }
  14:  
  15:     public UserDetail Logout()
  16:     {
  17:         throw new NotImplementedException();
  18:     }
  19:  
  20:     public void UpdateUser(UserDetail user)
  21:     {
  22:         throw new NotImplementedException();
  23:     }
  24: }

 

Now remove exception throw and add the following codes into each function as done here:

 


   1: [EnableClientAccess]
   2:     public class CustomAuthDomainService : LinqToEntitiesDomainService<AuthEntities>,                    IAuthentication<UserDetail>
   3:     {
   4:         private static UserDetail DefaultUser = new UserDetail()
   5:         {
   6:             Name = string.Empty,
   7:             Email = string.Empty,
   8:             Password = string.Empty,
   9:             UserID = 0
  10:         };
  11:  
  12:         // To enable Forms/Windows Authentication for the Web Application, edit the appropriate section of web.config file.
  13:         public UserDetail GetUserMy(string userName)
  14:         {
  15:             return this.ObjectContext.UserDetails.FirstOrDefault(x => x.Name == userName);
  16:         }
  17:  
  18:         public UserDetail Login(string userName, string password, bool isPersistent, string customData)
  19:         {
  20:             if (this.ValidateUser(userName, password))
  21:             {
  22:                 FormsAuthentication.SetAuthCookie(userName, isPersistent);
  23:                 return this.GetUserMy(userName);
  24:             }
  25:             return null;
  26:         }
  27:  
  28:         private bool ValidateUser(string username, string password)
  29:         {
  30:             return this.ObjectContext.UserDetails.Any(u => u.Name == username && u.Password == password);
  31:         }
  32:  
  33:         public void InsertUser(UserDetail user)
  34:         {
  35:             if ((user.EntityState != EntityState.Detached))
  36:             {
  37:                 this.ObjectContext.ObjectStateManager.ChangeObjectState(user, EntityState.Added);
  38:             }
  39:             else
  40:             {
  41:                 this.ObjectContext.UserDetails.AddObject(user);
  42:             }
  43:         }
  44:  
  45:         public UserDetail Logout()
  46:         {
  47:             FormsAuthentication.SignOut();
  48:             return CustomAuthDomainService.DefaultUser;
  49:         }
  50:  
  51:         public void UpdateUser(UserDetail user)
  52:         {
  53:             // Ensure the user data that will be modified represents the currently
  54:             // authenticated identity 
  55:             if ((this.ServiceContext.User == null) ||
  56:                 (this.ServiceContext.User.Identity == null) ||
  57:                 !string.Equals(this.ServiceContext.User.Identity.Name, user.Name, System.StringComparison.Ordinal))
  58:             {
  59:                 throw new UnauthorizedAccessException("You are only authorized to modify your own profile.");
  60:             }
  61:  
  62:             this.ObjectContext.UserDetails.AttachAsModified(user, this.ChangeSet.GetOriginal(user));  
  63:         }
  64:  
  65:         public UserDetail GetUser()
  66:         {
  67:             return null;
  68:         }
  69:     }

We are reaching very close…
In the Constructor of App.xaml.cs add the following line to set the WebContext as the global object through out the application.



   1: WebContext webContext;
   2: webContext = new WebContext();
   3: webContext.Authentication = 
   4: new System.ServiceModel.DomainServices.Client.ApplicationServices.FormsAuthentication(); 
   5: this.ApplicationLifetimeObjects.Add(webContext);

Just on the edge of finishing. Last thing to do is to write the code for login in the Login Button click event.



   1: private void BtnLogin_OnClick(object sender, RoutedEventArgs e)
   2: {          
   3:     var temp = 
   4:     WebContext.Current.Authentication.Login(TxtName.Text.Trim().ToLower(), TxtPass.Text);
   5:     temp.Completed += new EventHandler(temp_Completed);
   6: }
   7:  
   8: void temp_Completed(object sender, EventArgs e)
   9: {
  10:     var login = 
  11:     ((System.ServiceModel.DomainServices.Client.ApplicationServices.LoginOperation)(sender));
  12:     if (login.LoginSuccess)
  13:     {
  14:         LoggedInPerson.Content = "Welcome " + WebContext.Current.User.Name;
  15:     }
  16:     else
  17:     {
  18:         LoggedInPerson.Content = "Invalid credentials";
  19:     }
  20: }

Ok, all over. Build and run the project. Since we’d already entered the data just type in the credentials:
User Name : Neelma
Password   : Neelma@1


If everything goes fine, you’ll be greeted with a message saying, Welcome Neelma.


You can get the download link of the project here http://www.4shared.com/rar/f1KFQvOQ/AuthDemo.html?

Share this post

0 comments

:) :-) :)) =)) :( :-( :(( :d :-d @-) :p :o :>) (o) [-( :-? (p) :-s (m) 8-) :-t :-b b-( :-# =p~ :-$ (b) (f) x-) (k) (h) (c) cheer

 
© 2013 Neelesh Vishwakarma
Posts RSS Comments RSS
Back to top