Asp mvc 4 Membership and WebSecurity

I need a suggestion about what to do. I'm currently using WebSecurity methods to do all the account related job. However it does not support E-mail uniqueness verification so I have a few options:

  1. Write (subclass) a new SimpleMembershipProvider overwriting the existing createuserAndAccount method to verify the email address. But i would also have to implement the login-logout features (just as websecurity does) and a few others.

  2. Add uniqueness constraints on the database and catch them on my code. However this would cause me to be DataBase dependent.

  3. This might be a little bit cheap, but i could copy/paste the WebSecurity source code (since its open) on a new class, and modify the createUserAndAccount method.

Any other option? I'm aiming for the option 3 at the moment, would be the fastest way. On a side note, on the future I will be requiring roles as well and I'm not sure if WebSecurity provides support for them.

Answers


If it were me, I'd probably go about it the following way:

First, assuming you're using SimpleMembership with Entity Framework or some database connection (ADO, LINQ to SQL, etc.) you're going to have two components: WebSecurity.* method calls, and the database connection to make profile changes. Personally, I'd add the CONSTRAINT to the database to ensure your data is pure, but you can also implement a membership service that handles this logic, too.

First, group these in to an interface that can be referenced in your controller (something like the following):

public interface IMembershipService
{
    Int32 CurrentUserId { get; }
    String CurrentUserName { get; }
    Boolean IsAuthenticated { get; }

    Boolean CreateUserAndAccount(String username, String password, String emailaddress = null);
    Boolean CreateUserAndAccount(String username, string password, out String confirmationToken, String emailaddress = null);
    Boolean Login(String username, String password, Boolean persistCookie = false);
    void Logout();
}

Then you can implement the service as a hybrid of SimpleMembership and your database connection. For the sake of keeping it generic, I use the IRepository<T> pattern, but this could be a direct DbContext, ObjectContext, etc. I'm also keeping it brief, so excuse the missing checksums and short implementation.

public class MembershipService : IMembershipService
{
    protected readonly SimpleMembershipProvider membershiProvider;
    protected readonly SimpleRoleProvider roleProvider;
    protected readonly IRepository<UserProfile> profileRepository;

    public MembershipService(IRepository<UserProfile> profileRepository)
    {
        this.membershipProvider = Membership.Provider as SimpleMembershipProvider;
        this.roleProvider = Role.Provider as SimpleRoleProvider;
        this.profileRepository = userRepository;
    }

    #region IMembershipService Implementation

    public Int32 CurrentUserId
    {
        get { return WebSecurity.CurrentUserId; }
    }
    public String CurrentUserName
    {
        get { return WebSecurity.CurrentUserName; }
    }
    public Boolean IsAuthenticated
    {
        get { return WebSecurity.IsAuthenticated; }
    }

    public Boolean CreateUserAndAccount(String username, String password, String emailaddress = null)
    {
        // validate the email address is unique
        if (!this.profileRepository.Any(x => x.EmailAddress == emailaddress))
        {
            WebSecurity.CreateUserAndAccount(username, password, new
            {
                EmailAddress = emailaddress
            }, createConfirmationToken);
            return true;
        }
        else
        {
            // handle the error how you see fit
            // (maybe even exception?)
            return false;
        }
    }
    public Boolean CreateUserAndAccount(String username, String password, out String confirmationToken, String emailaddress = null, out)
    {
        // validate the email address is unique
        if (this.profileRepository.First(x => x.EmailAddress == emailaddress) == null)
        {
            confirmationToken = WebSecurity.CreateUserAndAccount(username, password, new
            {
                EmailAddress = emailaddress
            }, createConfirmationToken);
            return true;
        }
        else
        {
            // handle the error how you see fit
            // (maybe even exception?)
            confirmationToken = String.Empty;
            return false;
        }
    }
    public Boolean Login(String username, String password, Boolean persistCookie = false)
    {
        return WebSecurity.Login(username, password, persistCookie);
    }
    public void Logout()
    {
        WebSecurity.Logout();
    }

    #endregion
}

Now you can reference this interface in your controller and have the logic in one place. if you're using a DI container, obviously register it, but here's an example implementation:

public class AccountController: Controller
{
    private readonly IMembershipService membershipService;

    public AccountController(IMembershipService membershipService)
    {
        this.membershipService = membershipService;
    }

    /* ... */

    [HttpPost, ValidateAntiForgeryToken]
    public ActionResult Register(LoginViewModel model, String returnUrl)
    {
        if (ModelState.IsValid)
        {
            if (this.membershipService.CreateUserandAccount(model.Username, model.Password, model.EmailAddress))
            {
                this.membershipService.Login(model.Username, model.Password);
                if (!String.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
                {
                    return Redirect(returnUrl);
                }
                return RedirectToRoute("Default");
            }
            else
            {
                ModelState.AddModelError("", "Unable to register.");
            }
        }
        return View(model);
    }

    /* ... */
}

If you're using EntityFramework, you can also use the IValidatableObject. To resist duplicating, here's another SO question/answer that checks for a unique entry:

Entity Framework IValidatableObject


Need Your Help

Get variable value from aspx and use it in code behind

javascript jquery asp.net visual-studio-2010

I am having a little issue. I have a drop down list which can have multiple values selected. I did that by simply adding a $('#id').attr('multiple','multiple');

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.