Wednesday, 15 April 2015

SQL Membership Provider

> C#/.Net Samples
> Back to .Net Authentication

Tool
http://blogs.msdn.com/b/webdev/archive/2013/08/19/asp-net-web-configuration-tool-missing-in-visual-studio-2013.aspx

Web.Config

<system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
 
    <machineKey validationKey="CA102D4198A2539EC41724F0AA26A685FBB2C2065539C2E4481EB7665174B8E223BAD896B7198DF9A759BBB714959FBA141FC41C2B45A3C714F43131C4A1400A" decryptionKey="39EDCC930B8019E126C1B744B3BC7325FF8D5390E6693E3307AF6BD0E74ED04C" validation="SHA1" decryption="AES" />
 
    <httpCookies httpOnlyCookies="true" />
      
    <authentication mode="Forms">
      <forms name="TestApp.ASPXFORMSAUTH" loginUrl="~/Login" enableCrossAppRedirects="true" cookieless="AutoDetect" domain="TestApp.com" slidingExpiration="true" protection="All" path="/" timeout="30" />
    </authentication>
 
    <membership>
      <providers>
        <clear />
        <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="TestAppConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="true" minRequiredPasswordLength="5" minRequiredNonalphanumericCharacters="0" applicationName="TestApp" />
      </providers>
    </membership>
 
    <roleManager enabled="true">
      <providers>
        <clear />
        <add connectionStringName="TestAppConnection" applicationName="TestApp" name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" />
      </providers>
    </roleManager>

  </system.web>

User Class used by the Helper

[Serializable]
    public class User
    {
         
        //membership provider specific items
        public string Role { get; set; }
        public Guid ContactId { get; set; }  //Membership GUID, gets stored in the profile table as ContactId
        public bool IsApproved { get; set; }
        public bool IsAccountLockedOut { get; set; } 
         public DateTime LastLoginDate { get; set; }
        public DateTime LastActivityDate { get; set; }
        public string UserId { get; set; } 
        //comes from profile - custom table
        public string Title { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string PhoneNumber { get; set; }
        public string Email { get; set; }

 
        #region Computed Properties
        public string FullName
        {
            get
            {
                var fullName = String.Empty;
                if (!String.IsNullOrWhiteSpace(FirstName) || !String.IsNullOrWhiteSpace(LastName))
                {
                    fullName = String.Join(" ", FirstName, LastName);
                }
                return fullName;
            }
        }
        #endregion
    }

Mapper Helper Method

This mapper maps the provider information and our custom information together into User class

private User MapMembershipUserToUser(MembershipUser membershipUser)
        {
            User user = null;
 
            if (membershipUser != null && membershipUser.ProviderUserKey != null)
            {
                var membershipGuid = Guid.Empty;
                Guid.TryParse(membershipUser.ProviderUserKey.ToString(), out membershipGuid);
                if (membershipGuid != null && !membershipGuid.Equals(Guid.Empty))
                {
                    string[] userRole = GetUserRole(membershipUser.UserName);
                    if (userRole != null && userRole.Any())
                    {
                        //get user info from custom table
                        user = UserDataAccess.GetUser(membershipGuid);
                        
                        
                        if (user != null)
                        {
                            //attach membership items to the user
                            user.Role = String.Join(",", userRole);
                            user.IsAccountLockedOut = membershipUser.IsLockedOut;
                            user.IsApproved = membershipUser.IsApproved;
 
                            user.UserId = membershipUser.UserName;
                            user.ContactId = membershipGuid; 
                            user.Email = membershipUser.Email;
                            user.LastLoginDate = membershipUser.LastLoginDate;
                            user.LastActivityDate = membershipUser.LastActivityDate;
                        }
                    }
                }
            }
 
            return user;
        }

Helper Class

DataAccess is used to get the user profile information like name etc. This information is not going in provider tables.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Security;
using TestApp.DataAccess;
using TestApp.DataModel;

#region Constants
 
        private const string AspNetSqlMembershipProvider = "AspNetSqlMembershipProvider";
        private const string AspNetSqlRoleProvider = "AspNetSqlRoleProvider";
 
        #endregion
 
        #region Declarations
 
        private readonly MembershipProvider _provider;
 
        #endregion
 
        #region Constructor
 
        public MembershipProviderHelper()
        {
            _provider = Membership.Providers[AspNetSqlMembershipProvider];
        }
 
        #endregion

public User ValidateUserIdPassword(string userName, string password)
        {
            if (String.IsNullOrWhiteSpace(userName))
                throw new ApplicationException("Unable to validate user as userName is missing");
            if (String.IsNullOrWhiteSpace(password))
                throw new ApplicationException("Unable to validate user as password is missing");
 
            User user = null;
            var isValidated = _provider.ValidateUser(userName, password);
            if (isValidated)
            {
                user = GetUserByUserName(userName);
            }
            else
            {
                //not approved and locked are treated as not validated. Check and get the user by username
                user = GetUserByUserName(userName);
                if (user != null)
                {
                    // make the user null if account is not locked and is approved other wise send the user info back. If sending the user info back, display appropriate message per the locked and approved flags
                    if (!user.IsAccountLockedOut && user.IsApproved)
                        user = null;
                }
            }
            return user;
        }

public bool IsUserNameUnique(string userName)
        {
            if (String.IsNullOrWhiteSpace(userName))
                throw new ApplicationException("Unable to get user as userName is missing");
            var user = GetMembershipUserByUserName(userName);
            var isUnique = user == null || String.IsNullOrWhiteSpace(user.UserName) || !user.UserName.ToLowerInvariant().Equals(userName);
            return isUnique;
        }
 
public bool IsUserNameUnique(string userName, Guid userId)
        {
            if (String.IsNullOrWhiteSpace(userName))
            {
                throw new ApplicationException("Unable to get user as userName is missing");
            }
                
            MembershipUser user = GetMembershipUserByUserName(userName);
            bool isUnique = false;
 
            if (user == null)
            {
                isUnique = true;
            }
 
            if (user != null && user.ProviderUserKey.ToString() == userId.ToString())
            {
                isUnique = true;
            }
            
            return isUnique;
        } 
 
 public bool IsEmailUnique(string email)
       {
           if (String.IsNullOrWhiteSpace(email))
               throw new ApplicationException("Unable to get user as email is missing");
           var userName = GetUserNameByEmail(email);
           var isUnique = String.IsNullOrWhiteSpace(userName);
           return isUnique;
       }
 
 public bool IsEmailUniqueForUser(string userName, string email)
        {
            if (String.IsNullOrWhiteSpace(userName))
                throw new ApplicationException("Unable to check for unique email as userName is missing");
 
            if (String.IsNullOrWhiteSpace(email))
                throw new ApplicationException("Unable to check for unique email as email is missing");
 
            var isEmailunique = false;
            var currentUserId = GetUserNameByEmail(email);
            if (String.IsNullOrWhiteSpace(currentUserId) || currentUserId.Equals(userName))
                isEmailunique = true;
            return isEmailunique;
        }
 
public bool IsUserVerified(string userName)
        {
            if (String.IsNullOrWhiteSpace(userName))
                throw new ApplicationException("Unable to verify user as userName is missing");
 
            var isApproved = false;
 
            var user = _provider.GetUser(userName, false);
            if (user != null)
            {
                isApproved = user.IsApproved;
            }
            return isApproved;
        } 
 
public bool IsUserLocked(string userName)
        {
            if (String.IsNullOrWhiteSpace(userName))
                throw new ApplicationException("Unable to check if user is locked as userName is missing");
 
            var isLocked = false;
            var user = _provider.GetUser(userName, false);
            if (user != null)
            {
                isLocked = user.IsLockedOut;
            }
            return isLocked;
        } 

public MembershipUser GetMembershipUserByUserName(string userName)
        {
            if (String.IsNullOrWhiteSpace(userName))
                throw new ApplicationException("Unable to get user as userName is missing");
 
            var providerUser = _provider.GetUser(userName, false);
            return providerUser;
        }
 
public MembershipUser GetMembershipUserByContactId(Guid contactId)
        {
            var providerUser = _provider.GetUser(contactId, false);
 
            return providerUser;
        }
 
public void UpdateMembershipUser(MembershipUser membershipUser)
        {
            _provider.UpdateUser(membershipUser);
        }
 
public User GetUserByUserName(string userName)
        {
            if (String.IsNullOrWhiteSpace(userName))
                throw new ApplicationException("Unable to get user as userName is missing");
 
            var providerUser = _provider.GetUser(userName, false);
 
            var user = MapMembershipUserToUser(providerUser);
            return user;
        }
 
//this pulls the user from membership and profile - basic information
        public User GetUserByGuid(Guid userAuthId)
        {
            if (userAuthId.Equals(Guid.Empty) || userAuthId == null)
                throw new ApplicationException("Unable to get user as userGuid is missing");
            var providerUser = _provider.GetUser(userAuthId, false);
            var user = MapMembershipUserToUser(providerUser);
            return user;
        }
 
//this pulls the user from membership and profile - basic information
        public User GetUserByEmail(string email)
        {
            if (String.IsNullOrWhiteSpace(email))
                throw new ApplicationException("Unable to get user as email is missing");
            User user = null;
            var userName = _provider.GetUserNameByEmail(email);
            if (!String.IsNullOrWhiteSpace(userName))
            {
                user = GetUserByUserName(userName);
            }
            return user;
        }
 
//returns user name only
        public string GetUserNameByEmail(string email)
        {
            if (String.IsNullOrWhiteSpace(email))
                throw new ApplicationException("Unable to get userName as email is missing");
 
            var userName = _provider.GetUserNameByEmail(email);
            return userName;
        }
 
public bool DeleteUser(string userName, bool deleteAllRelatedData)
        {
            if (String.IsNullOrWhiteSpace(userName))
                throw new ApplicationException("Unable to delete user as userName is missing");
 
            var result = false;
            result = _provider.DeleteUser(userName, deleteAllRelatedData);
            return result;
        }
 
public List<MembershipUser> GetAllUsers()
        {
            List<MembershipUser> users = null;
            var userList = Membership.GetAllUsers().Cast<MembershipUser>();
            if (userList != null && userList.Count() > 0)
                users = userList.ToList();
            return users;
        }
 
public bool VerifyUser(string userName)
       {
           var isApproved = false;
 
           if (String.IsNullOrWhiteSpace(userName))
               throw new ApplicationException("Unable to verify user as userName is missing");
 
           var user = _provider.GetUser(userName, false);
           if (user != null)
           {
               user.IsApproved = true;
               _provider.UpdateUser(user);
               //get the user again and check
               var approvedUser = _provider.GetUser(userName, false);
               if (approvedUser != null && approvedUser.IsApproved)
               {
                   isApproved = true;
               }
           }
           return isApproved;
       }
 
public MembershipCreateStatus CreateUser(string userName, string password, string email, bool isApproved)
        {
            if (String.IsNullOrWhiteSpace(userName))
                throw new ApplicationException("Unable to create user as userName is missing");
            if (String.IsNullOrWhiteSpace(password))
                throw new ApplicationException("Unable to create user as password is missing");
            if (String.IsNullOrWhiteSpace(email))
                throw new ApplicationException("Unable to create user as email is missing");
 
            MembershipCreateStatus status;
            _provider.CreateUser(userName, password, email, null, null, isApproved, null, out status);
            return status;
        }
 
public MembershipCreateStatus CreateUserAndAssignRole(string userName, string password, string email, bool isApproved, string role)
        {
            if (String.IsNullOrWhiteSpace(userName))
                throw new ApplicationException("Unable to create user as userName is missing");
            if (String.IsNullOrWhiteSpace(password))
                throw new ApplicationException("Unable to create user as password is missing");
            if (String.IsNullOrWhiteSpace(email))
                throw new ApplicationException("Unable to create user as email is missing");
            if (String.IsNullOrWhiteSpace(role))
                throw new ApplicationException("Unable to create user as role is missing");
 
            MembershipCreateStatus status;
            _provider.CreateUser(userName, password, email, null, null, isApproved, null, out status);
            if (status == MembershipCreateStatus.Success)
                Roles.AddUserToRole(userName, role);
            return status;
 
        }
 
public void UpdateEmail(string userName, string email)
        {
            if (String.IsNullOrWhiteSpace(userName))
                throw new ApplicationException("Unable to create user as userName is missing");
            if (String.IsNullOrWhiteSpace(email))
                throw new ApplicationException("Unable to create user as email is missing");
 
            MembershipUser user = _provider.GetUser(userName, false);
            if (user != null)
            {
                user.Email = email;
                _provider.UpdateUser(user);
            }
        }
 
public bool ChangePassword(string userName, string newPassword)
        {
 
            if (String.IsNullOrWhiteSpace(userName))
                throw new ApplicationException("Unable to change password as userName is missing");
            if (String.IsNullOrWhiteSpace(newPassword))
                throw new ApplicationException("Unable to change password as password is missing");
 
            var result = false;
 
            // The underlying ChangePassword() will throw an exception rather
            // than return false in certain failure scenarios.
            try
            {
                var currentUser = _provider.GetUser(userName, true);
                if (currentUser != null)
                    result = _provider.ChangePassword(userName, currentUser.ResetPassword(), newPassword);
            }
            catch (Exception ex)
            {
                throw new ApplicationException("Error changing password", ex);
            }
            return result;
        }
 
public bool UpdateUserName(Guid userAuthId, string newUserId)
        {
            var updateUser = false;
            var user = _provider.GetUser(userAuthId, false);
            if (user != null)
            {
                //see if newUserName already in use
                var existingUser = _provider.GetUser(newUserId, false);
                //if user does not exist then update User with newUsername
                if (existingUser == null)
                {
                    //TODO: update user, DB case is 49854
                    updateUser = true;
                }
 
            }
 
            return updateUser;
        }
 
public string[] GetAllRoles()
        {
            var userRoles = Roles.GetAllRoles();
            return userRoles;
        }
 
public string[] GetUserRole(string userName)
        {
            string[] roles = null;
            if (!String.IsNullOrWhiteSpace(userName))
            {
                var roleProvider = Roles.Providers[AspNetSqlRoleProvider];
                if (roleProvider != null)
                {
                    roles = roleProvider.GetRolesForUser(userName);
                    //convert to lower case
                    if (roles != null && roles.Any())
                    {
                        roles = roles.Select(s => s.ToLowerInvariant()).ToArray();
                    }
                }
            }
            return roles;
        }
 
public void AddUserToRoles(string userName, string[] roleNames)
        {
            if (String.IsNullOrWhiteSpace(userName))
                throw new ApplicationException("Unable to add role as userName is missing");
            if (roleNames == null || !roleNames.Any())
                throw new ApplicationException("Unable to add role as no roles provided");
 
            Roles.AddUserToRoles(userName, roleNames);
        }
 
public void DeleteUserRoles(string userName)
        {
            if (String.IsNullOrWhiteSpace(userName))
                throw new ApplicationException("Unable to delete roles as userName is missing");
 
            var userRoles = Roles.GetRolesForUser(userName);
            if (userRoles != null && userRoles.Length > 0)
                Roles.RemoveUserFromRoles(userName, userRoles);
        }
 
public string ErrorCodeToString(MembershipCreateStatus createStatus)
        {
            // See http://msdn.microsoft.com/en-us/library/system.web.security.membershipcreatestatus.aspx for
            // a full list of status codes.
            switch (createStatus)
            {
                case MembershipCreateStatus.Success:
                    return "The user was successfully created.";
                case MembershipCreateStatus.DuplicateUserName:
                    return "User name already exists. Please enter a different user name.";
                case MembershipCreateStatus.DuplicateEmail:
                    return "A user name for that email address already exists. Please enter a different email address.";
                case MembershipCreateStatus.InvalidPassword:
                    return "The password provided is invalid. Please enter a valid password value.";
                case MembershipCreateStatus.InvalidEmail:
                    return "The email address provided is invalid. Please check the value and try again.";
                case MembershipCreateStatus.InvalidAnswer:
                    return "The password retrieval answer provided is invalid. Please check the value and try again.";
                case MembershipCreateStatus.InvalidQuestion:
                    return "The password retrieval question provided is invalid. Please check the value and try again.";
                case MembershipCreateStatus.InvalidUserName:
                    return "The user name provided is invalid. Please check the value and try again.";
                case MembershipCreateStatus.ProviderError:
                    return "The authentication provider returned an error. Please verify your entry and try again.";
                case MembershipCreateStatus.UserRejected:
                    return "The user creation request has been canceled. Please verify your entry and try again.";
                default:
                    return "An unknown error occurred. Please verify your entry and try again.";
            }
        } 



Logging in the User, checking User Roles and LogOff

Logged in users basic information has been placed in the authentication ticket


public static User GetUserFromFormsAuthencitaionTicket()
        {
            User user = null;
            
            if (HttpContext.Current != null)
            {
                var myCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
 
                if (myCookie != null)
                {
                    var decrypted = FormsAuthentication.Decrypt(myCookie.Value);
 
                    if (decrypted != null && !string.IsNullOrEmpty(decrypted.UserData))
                    {
                       user = JsonConvert.DeserializeObject<User>(decrypted.UserData); 
                    }
                }
            }
 
            return user;
        }


public static bool IsLoggedInUserInRole(string role)
        {
            var isRole = false;
            if (String.IsNullOrWhiteSpace(role)) return isRole;
 
            var user = GetUserFromFormsAuthencitaionTicket();
            if (user == null || String.IsNullOrWhiteSpace(user.Role)) return isRole;
 
            var userRoles = user.Role.Split(',');
            isRole = userRoles.Any(myRole => myRole.ToLowerInvariant().Equals(role.ToLowerInvariant()));
            return isRole;
        }


public static bool IsLoggedInUserInRole(string[] roles)
       {
           var isRole = false;
           if (roles == null || !roles.Any()) return isRole;
 
           var user = GetUserFromFormsAuthencitaionTicket();
           if (user == null || String.IsNullOrWhiteSpace(user.Role)) return isRole;
 
           var userRoles = user.Role.Split(',');
           isRole = userRoles.Any(myRole => roles.Any(role => myRole.ToLowerInvariant().Equals(role.ToLowerInvariant())));
           return isRole;
       }

public static User AuthenticateUser(string userName, string password, bool rememberMe)
        {
            //if can't login the user, then the caller needs to display appropriate messages  
             User user = null;
            var loginStatus = WebEvent.WebEventWrongUsernamePassword;
 
            //Membership provider authentication
            var provider = new MembershipProviderHelper();
            user = provider.ValidateUserIdPassword(userName, password);
            
            //if we have user then check and do the login
            if (user != null && !user.IsAccountLockedOut && user.IsApproved)
            {
                //do login and put user in the FormsAuthenticationTicket
                LoginUser(user, rememberMe);
            }
            return user;
        }



private static void LoginUser(User user, bool isRememberMe)
        {
            //Forms Authentication
            //var expiryDateTime = isRememberMe ? DateTime.Now.AddYears(1) : DateTime.Now.AddMinutes(20);
            //FormsAuthentication.Timeout.TotalMinutes is coming from web.config
            var expiryDateTime = isRememberMe ? DateTime.Now.AddYears(1) : DateTime.Now.AddMinutes(FormsAuthentication.Timeout.TotalMinutes);
 
            //store the serialized user data into the FormsAuthenticationTicket
            string userJson = JsonConvert.SerializeObject(user);
 
            var ticket = new FormsAuthenticationTicket(
                    1, // Ticket version
                    user.UserId, // Username associated with ticket
                    DateTime.Now, // Date/time issued
                    expiryDateTime, // Date/time to expire  DateTime.Now.AddYears(1)
                    isRememberMe, // "true" for a persistent user cookie
                    userJson, // User-data
                    FormsAuthentication.FormsCookiePath); // Path cookie valid for
 
            // Encrypt the cookie using the machine key for secure transport
            var hash = FormsAuthentication.Encrypt(ticket);
            var cookie = new HttpCookie(
                FormsAuthentication.FormsCookieName, // Name of auth cookie
                hash); // Hashed ticket
 
            // Set the cookie's expiration time to the tickets expiration time
            if (ticket.IsPersistent)
            {
                cookie.Expires = ticket.Expiration;
            }
 
            // Add the cookie to the list for outgoing response
            HttpContext.Current.Response.Cookies.Add(cookie);
        }


public static void LogOff()
        {
            FormsAuthentication.SignOut();
            if (HttpContext.Current.Session != null)
            {
                HttpContext.Current.Session.Clear();
                HttpContext.Current.Session.Abandon();
            }
            
            // clear authentication cookie
            var cookie1 = new HttpCookie(FormsAuthentication.FormsCookieName, "");
            cookie1.Expires = DateTime.Now.AddYears(-1);
            HttpContext.Current.Response.Cookies.Add(cookie1);
            
            // clear session cookie (do it anyway)
            var cookie2 = new HttpCookie("ASP.NET_SessionId", "");
            cookie2.Expires = DateTime.Now.AddYears(-1);
            HttpContext.Current.Response.Cookies.Add(cookie2);
 
            //clear any custom cookies that may have been written
            var customCookie = new HttpCookie("CustomCookie");
            customCookie.Expires = DateTime.Now.AddYears(-1);
            HttpContext.Current.Response.Cookies.Add(customCookie);
        }









Back to .Net Authentication