Friday, August 31, 2012

Implement Facebook Login with OpenID Selector in MVC3

In My last post, I explained how we can implement Facebook login with OpenId selector tool. In this post I will explain you how we can use that tool in an ASP.NET MVC3 application.



1- Open Visual Studio 2010 go to File > New > Project > Web > ASP.NET MVC 3 Application:

then Choose Internet Application be sure to have Razor as your View engine and Click OK:
2- Download DotNetOpenAuth dll and Updated OpenID Selector files that we will use.
      A- Add the DotNetOpenAuth.dll to references in your site.
      B- Copy all contents of openid-selector\css folder to the 'Content' folder .
      C- Copy images, images.large, images.small folders to the site 'Content' folder.
      D- Copy openid-jquery.js, openid-en.js files from  openid-selector\js folder to 'Scripts' folder

Your Solution Explorer  will look like this (See Highlighted Portion)  :



 3- Go to Views > Shared > _Layout.cshtml and replace the <head> with this new head with new styles and scripts:
<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title</title>
    <link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
    <link href="@Url.Content("~/Content/openid-shadow.css")" rel="stylesheet" type="text/css" />
    <link href="@Url.Content("~/Content/openid.css")" rel="stylesheet" type="text/css" />
    <script src="@Url.Content("~/Scripts/openid-jquery.js")" type="text/javascript"></script>
    <script src="@Url.Content("~/Scripts/openid-en.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
 <script type="text/javascript" src="https://connect.facebook.net/en_US/all.js"> </script>
   
    <script type="text/javascript">
        $(document).ready(function () {
            openid.init('openid_identifier');
        });
    </script>
</head>

4- Go to Models > AccountModels.cs  , navigate to public class LogOnModel
   and Add OpenID attribute that we will use it to hold the returned OpenID from OpenID-Selector
  your class will look like this:
    public class LogOnModel
    {
        [Display(Name = "OpenID")]
        public string OpenID { get; set; }

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

        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }

        [Display(Name = "Remember me?")]
        public bool RememberMe { get; set; }
    }

5- Go to Models > AccountModels.cs and Add following class to it:
    public class UserDetailsModel
    {
        public string OpenID { get; set; }
        public string ProviderUrl { get; set; }
        public string FriendlyIdentifier { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }        
        public string Dob { get; set; }
        public string Gender { get; set; }
        public string Email { get; set; }
    }
  
6- go to Views > Account > LogOn.cshtml replace all the markup with this one, This will
   integrate updated OpenID-Selector to LogOn View: 
 
 
@model FBOpenIDMVC3.Models.LogOnModel

@{
    ViewBag.Title = "Log On";    
}
<h2>
    Log On</h2>
<p>
    Please enter your username and password. @Html.ActionLink("Register", "Register")
    if you don't have an account.
</p>
<script type="text/javascript">
    $(document).ready(function () {
        // Init the SDK upon load
        window.fbAsyncInit = function () {
            FB.init({
                appId: '188220747944294', // App ID
                channelUrl: '//' + window.location.hostname + '/channel', // Path to your Channel File
                scope: 'id,name,first_name,last_name,gender,email',
                status: true, // check login status
                cookie: true, // enable cookies to allow the server to access the session
                xfbml: true  // parse XFBML
            });
        };
    });
    /*This Method will be invoked on lick on Facebook button*/
    function FBLogin() {
        FB.login(FBCallBack);
    }
    function FBCallBack(response) {
        if (response.authResponse) {
            // user has auth'd your app and is logged into Facebook
            FB.api('/me?fields=id,name,first_name,last_name,gender,email,birthday', function (userDetail) {
                if (userDetail.name) {
                    var url = '@Url.Action("ShowUserDetails", "Account", new { OpenID = "_id_", FriendlyIdentifier = "_id_", FirstName = "_first_", LastName = "_last_", Dob = "_birthday_", Gender = "_gender_", Email = "_email_" })';
                    url = url.replace('_id_', userDetail.id);
                    url = url.replace('_id_', userDetail.id);
                    url = url.replace('_first_', userDetail.first_name);
                    url = url.replace('_last_', userDetail.last_name);
                    url = url.replace('_birthday_', userDetail.birthday);
                    url = url.replace('_gender_', userDetail.gender);
                    url = url.replace('_email_', userDetail.email);
                    window.location.href = url;
                }
            });
        }
    }
    </script>  
<form action="Authenticate?ReturnUrl=@HttpUtility.UrlEncode(Request.QueryString["ReturnUrl"])" method="post" id="openid_form">
<input type="hidden" name="action" value="verify" />
<div>    
    <fieldset>
        <legend>Login using OpenID</legend>
        <div class="openid_choice">
            <p>
                Please click your account provider:</p>
            <div id="openid_btns">
            </div>
        </div>
        <div id="openid_input_area">
            @Html.TextBox("openid_identifier")
            <input type="submit" value="Log On" />
        </div>
        <noscript>
            <p>
                OpenID is service that allows you to log-on to many different websites using a single
                indentity. Find out <a href="http://openid.net/what/">more about OpenID</a> and
                <a href="http://openid.net/get/">how to get an OpenID enabled account</a>.</p>
        </noscript>
        <div>
            @if (Model != null)
            {
                if (String.IsNullOrEmpty(Model.UserName))
                {
                <div class="editor-label">
                    @Html.LabelFor(model => model.OpenID)
                </div>
                <div class="editor-field">
                    @Html.DisplayFor(model => model.OpenID)
                </div>
                <p class="button">
                    @Html.ActionLink("New User ,Register", "Register", new { OpenID = Model.OpenID })
                </p>
                }
                else
                {
                    //user exist 
                <p class="buttonGreen">
                    <a href="@Url.Action("Index", "Home")">Welcome , @Model.UserName, Continue..." </a>
                </p>

                }
            }
        </div>
    </fieldset>
</div>
</form>

@Html.ValidationSummary(true, "Login was unsuccessful. Please correct the errors and try again.")
@using (Html.BeginForm())
{
    <div>
        <fieldset>
            <legend>Or Login Normally</legend>
            <div class="editor-label">
                @Html.LabelFor(m => m.UserName)
            </div>
            <div class="editor-field">
                @Html.TextBoxFor(m => m.UserName)
                @Html.ValidationMessageFor(m => m.UserName)
            </div>
            <div class="editor-label">
                @Html.LabelFor(m => m.Password)
            </div>
            <div class="editor-field">
                @Html.PasswordFor(m => m.Password)
                @Html.ValidationMessageFor(m => m.Password)
            </div>
            <div class="editor-label">
                @Html.CheckBoxFor(m => m.RememberMe)
                @Html.LabelFor(m => m.RememberMe)
            </div>
            <p >
                <input type="submit" value="Log On" />
            </p>
        </fieldset>
    </div>
}

7- Update the value of img_path in openid-jquery.js to '../Content/images/'
8- Now let us run the project , then click the [Log On] link , you will get like this page:
9- Now to implement Open ID using DotnetOpenAuth, Go to Controllers > 
AccountController.cs and Add these using:
 
using DotNetOpenAuth.Messaging;
using DotNetOpenAuth.OpenId;
using DotNetOpenAuth.OpenId.RelyingParty;
using DotNetOpenAuth.OpenId.Extensions.AttributeExchange;
Then Add the following code to AccountController.cs:
        private static OpenIdRelyingParty openid = new OpenIdRelyingParty();

        [ValidateInput(false)]
        public ActionResult Authenticate(string returnUrl)
        {
            var response = openid.GetResponse();
            if (response == null)
            {
                //Let us submit the request to OpenID provider
                Identifier id;
                if (Identifier.TryParse(Request.Form["openid_identifier"], out id))
                {
                    try
                    {
                        var request = openid.CreateRequest(Request.Form["openid_identifier"]);
                        var fetch = new FetchRequest();
                        fetch.Attributes.Add(new AttributeRequest(WellKnownAttributes.Contact.Email, true));
                        fetch.Attributes.Add(new AttributeRequest(WellKnownAttributes.BirthDate.WholeBirthDate, true));
                        fetch.Attributes.Add(new AttributeRequest(WellKnownAttributes.Person.Gender, true));
                        fetch.Attributes.Add(new AttributeRequest(WellKnownAttributes.Name.First, true));
                        fetch.Attributes.Add(new AttributeRequest(WellKnownAttributes.Name.Last, true));
                        request.AddExtension(fetch);
                        return request.RedirectingResponse.AsActionResult();
                    }
                    catch (ProtocolException ex)
                    {
                        ViewBag.Message = ex.Message;
                        return View("LogOn");
                    }
                }

                ViewBag.Message = "Invalid identifier";
                return View("LogOn");
            }

            //Let us check the response
            switch (response.Status)
            {

                case AuthenticationStatus.Authenticated:
                    var fetch = response.GetExtension<FetchResponse>();
                    var sFirstName = "";
                    var sEmail = "";
                    var sLastName = "";
                    var sGender = "";
                    var sDob = "";
                    if (fetch != null)
                    {
                        foreach (var vAtrrib in fetch.Attributes)
                        {
                            switch (vAtrrib.TypeUri)
                            {
                                case WellKnownAttributes.Name.First:
                                    var firstNames = fetch.Attributes[WellKnownAttributes.Name.First].Values;
                                    sFirstName = firstNames.Count > 0 ? firstNames[0] : null;
                                    break;
                                case WellKnownAttributes.Contact.Email:
                                    var emailAddresses = fetch.Attributes[WellKnownAttributes.Contact.Email].Values;
                                    sEmail = emailAddresses.Count > 0 ? emailAddresses[0] : null;
                                    break;
                                case WellKnownAttributes.Name.Last:
                                    var lastNames = fetch.Attributes[WellKnownAttributes.Name.Last].Values;
                                    sLastName = lastNames.Count > 0 ? lastNames[0] : null;
                                    break;
                                case WellKnownAttributes.Person.Gender:
                                    var genders = fetch.Attributes[WellKnownAttributes.Person.Gender].Values;
                                    sGender = genders.Count > 0 ? genders[0] : null;
                                    break;
                                case WellKnownAttributes.BirthDate.WholeBirthDate:
                                    var doBs = fetch.Attributes[WellKnownAttributes.BirthDate.WholeBirthDate].Values;
                                    sDob = doBs.Count > 0 ? doBs[0] : null;
                                    break;
                            }
                        }
                    }
                    var sFriendlyLogin = response.FriendlyIdentifierForDisplay;
                    var lm = new UserDetailsModel
                    {
                        OpenID = response.ClaimedIdentifier,
                        FriendlyIdentifier = sFriendlyLogin,
                        FirstName = sFirstName,
                        LastName = sLastName,
                        Dob = sDob,
                        Gender = sGender,
                        Email = sEmail
                    };
                    FormsService.SignIn(sEmail, false);
                    return View("ShowUserDetails", lm);

                case AuthenticationStatus.Canceled:
                    ViewBag.Message = "Canceled at provider";
                    return View("LogOn");
                case AuthenticationStatus.Failed:
                    ViewBag.Message = response.Exception.Message;
                    return View("LogOn");
            }

            return new EmptyResult();
        }

        public ActionResult ShowUserDetails(UserDetailsModel objUserDetails)
        {
            return View(objUserDetails);
        }
        [HttpPost]
        public ActionResult ShowUserDetails(string OpenID, string FriendlyIdentifier, string FirstName, string LastName, string Dob, string Gender, string Email)
        {
            var objUserDetails = new UserDetailsModel()
            {
                OpenID = OpenID,
                FriendlyIdentifier = FriendlyIdentifier,
                FirstName = FirstName,
                LastName = LastName,
                Dob = Dob,
                Gender = Gender,
                Email = Email,
            };
            return View(objUserDetails);
        }
10 - Right Click on Views--> Accounts and Click Add-->View to add new view like this:
11- Add following code in this View:
    public class UserDetailsModel
    {
        public string OpenID { get; set; }
        public string ProviderUrl { get; set; }
        public string FriendlyIdentifier { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Dob { get; set; }
        public string Gender { get; set; }
        public string Email { get; set; }
    }
12- Now run the project click [Log On] link and click a provider like Google it may ask you to sign in or ask you to allow access to your information you will get a page like this :
Congratulations  ,  now you had integrated OpenID login with facebook login to your project.

Download Code from here



1 comment:

  1. Great Job Ravi! I never knew that Facebook login can also be implemented by open Id selector in MVC3. The usefulness and significance of this article is overwhelming. Thanks again and good luck! Web Development - Zeemo

    ReplyDelete