Aug 24, 2013

Saving Email from External Login in ASP.NET MVC 4.0 with SimpleMembership

ASP.NET MVC 4 web application allows users to log in from an external provider, such as Facebook, Twitter, Microsoft or Google and then integrate into your web application. Some OAuth/OpenId providers like Facebook, Google allow to access email. This article explains how to save the email in your database.

Before implementing, first lets consider following points:

1. User name is different from Email.

2. Email might be different for different providers.

3. Email is optional(some external provider like twitter doesn't allow to access email).

Getting Started:

1. Create a new ASP.NET MVC 4 > Internet Application Project

2. Open App_Start > AuthConfig.cs, uncomment the external services which you want to use and pass proper parameters. See here to configure and get parameters of external services. In this article, we will use Facebook,Twitter and Google providers.


		OAuthWebSecurity.RegisterTwitterClient(
                consumerKey: "xxxx",
                consumerSecret: "xxxxxxxx");

            OAuthWebSecurity.RegisterFacebookClient(
                appId: "yyyy",
                appSecret: "yyyyyyyy");

            OAuthWebSecurity.RegisterGoogleClient();

Model Changes:

3. In Account Model, add Email property in RegisterExternalLoginModel class:


 public class RegisterExternalLoginModel
    {
        [Required]
        [Display(Name = "User name")]
        public string UserName { get; set; }

        public string ExternalLoginData { get; set; }

        public string Email { get; set; }
    }

4. To add Email field in OAuthMembership table, add following class:


 [Table("webpages_OAuthMembership")]
    public class OAuthMembership
    {
        [Key, Column(Order = 0), StringLength(30)]
        public string Provider { get; set; }

        [Key, Column(Order = 1), StringLength(100)]
        public string ProviderUserId { get; set; }

        public int UserId { get; set; }

        [StringLength(100)] 
        public string Email { get; set; }
    }

5. add OAuthMembership in Userscontext


public class UsersContext : DbContext
    {
        public UsersContext()
            : base("DefaultConnection")
        {
        }

        public DbSet<UserProfile> UserProfiles { get; set; }
        public DbSet<OAuthMembership> OAuthMemberships { get; set; }
    }

It will create the required structure in database when you run the application and open register page first time.

oauth table diagrm

Controller Changes:

In AccountController, To set email from the external provider, replace the following lines in ExternalLoginCallback method :


  // User is new, ask for their desired membership name
                string loginData = OAuthWebSecurity.SerializeProviderUserId(result.Provider, result.ProviderUserId);
                ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(result.Provider).DisplayName;
                ViewBag.ReturnUrl = returnUrl;
                return View("ExternalLoginConfirmation", new RegisterExternalLoginModel { UserName = result.UserName, ExternalLoginData = loginData });

with the following lines:


 // User is new, ask for their desired membership name
                string loginData = OAuthWebSecurity.SerializeProviderUserId(result.Provider, result.ProviderUserId);
                ViewBag.ProviderDisplayName = OAuthWebSecurity.GetOAuthClientData(result.Provider).DisplayName;
                ViewBag.ReturnUrl = returnUrl;
                var model = new RegisterExternalLoginModel { UserName = result.UserName, ExternalLoginData = loginData };
                switch (result.Provider) {
                    case "facebook":
                    case "google":
                        {
                            model.Email = result.UserName;
                            model.UserName = "";
                            break;
                        }
                    case "twitter":
                        {
                            model.Email = "";
                            model.UserName = result.UserName;
                            break;
                        }
                    default:
                        break;
                
                }
                return View("ExternalLoginConfirmation", model);

Facebook and Google provide email as user name, so email is assigned for these providers.

In ExternalLoginConfirmation view, add following Email in form:

@Html.HiddenFor(m => m.Email)

It will post Email also when form is submitted. In ExternalLoginConfirmation method, To save email for new user,replace the following lines:


			// Insert name into the profile table
                        db.UserProfiles.Add(new UserProfile { UserName = model.UserName });
                        db.SaveChanges();

                        OAuthWebSecurity.CreateOrUpdateAccount(provider, providerUserId, model.UserName);
                        OAuthWebSecurity.Login(provider, providerUserId, createPersistentCookie: false);

                        return RedirectToLocal(returnUrl);

with these lines:


 // Insert name into the profile table
                        user = new UserProfile { UserName = model.UserName };
                        db.UserProfiles.Add(user);
                        db.SaveChanges();

                        OAuthWebSecurity.CreateOrUpdateAccount(provider, providerUserId, model.UserName);
                                                
                        if (!String.IsNullOrEmpty(model.Email)) {
                            var oauthItem = db.OAuthMemberships.FirstOrDefault(x => x.Provider == provider && x.ProviderUserId == providerUserId && x.UserId == user.UserId);
                            if (oauthItem != null) {
                                oauthItem.Email = model.Email;
                                db.SaveChanges(); 
                            }
                        }

                        OAuthWebSecurity.Login(provider, providerUserId, createPersistentCookie: false);

                        return RedirectToLocal(returnUrl);

This will save email in database for new user. Now if same user wants to add other external provider then in ExternalLoginCallback method, replace following line


 OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name);
 

with following lines:


 OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name);
                if (result.Provider == "facebook" || result.Provider == "google")
                {
                    using (UsersContext db = new UsersContext())
                    {
                        UserProfile user = db.UserProfiles.FirstOrDefault(u => u.UserName.ToLower() == User.Identity.Name);                       
                        if (user != null)
                        {                           
                                var oauthItem = db.OAuthMemberships.FirstOrDefault(x => x.Provider == result.Provider && x.ProviderUserId == result.ProviderUserId && x.UserId == user.UserId);
                                if (oauthItem != null)
                                {
                                    oauthItem.Email = result.UserName; 
                                    db.SaveChanges();
                                }                           
                        }
                    }
                } 
 

It will save email in OAuthMembership table when new provider is added.

oauth table diagrm

We have implemented to save email from OAuth/OpenId Providers. If you like this post, don’t forget to share. If you have any query, feel free to ask in comment box.