Get more information from Social providers used in the VS 2013 project templates

Get more information from Social providers used in the VS 2013 project templates

Rate This
  • Comments 19

When you create a New ASP.NET Project in VS 2013 and choose Individual Accounts, the template shows how you can login with Social providers such as Microsoft Account, Facebook, Google and Twitter. When you login with these Social Providers such as Facebook, you can request more information about the user such as the User’s picture, friends etc. and if the user allows your app to access this data then you can get this information and provide a rich experience in your site.

In the following post I am going to show you how you can request more data (or scopes) when a user logs in via Facebook provider. This post assumes that you have enabled Facebook login and are familiar with the basic walkthrough of Facebook Login.  You can visit http://www.asp.net/mvc/tutorials/mvc-5/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on to see a basic walkthrough on how to enable Facebook Login in the template.

[Update]: I have updated the following code to fix some issues that were reported.

Following are the steps to get more scopes from Facebook

Code Snippet
  1.  List<string> scope = newList<string>() { "email", "user_about_me", "user_hometown", "friends_about_me", "friends_photos" };
  2.  var x = newFacebookAuthenticationOptions();
  3.  x.Scope.Add("email");
  4.  x.Scope.Add("friends_about_me");
  5.  x.Scope.Add("friends_photos");
  6.  x.AppId = "636919159681109";
  7.  x.AppSecret = "f3c16511fe95e854cf5885c10f83f26f";
  8.  x.Provider = newFacebookAuthenticationProvider()
  9. {
  10.     OnAuthenticated = async context =>
  11.     {
  12.          //Get the access token from FB and store it in the database and
  13.         //use FacebookC# SDK to get more information about the user
  14.         context.Identity.AddClaim(
  15.         new System.Security.Claims.Claim("FacebookAccessToken",
  16.                                              context.AccessToken));
  17.     }
  18. };
  19.  x.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie;
  20.  app.UseFacebookAuthentication(x);

 

    • Line 10-17, we are hooking to the OnAuthenticated event for the Facebook OWIN authentication middleware. This method is called each time a user authenticates with Facebook.
      When the user is authenticated and has granted this app access to this data, all the data is stored in the FacebookContext in the Facebook authentication middleware.
    • Line 14, also stores the FacebookAccessToken which we get from Facebook and which we will use to get the Users’ friends information         
    • Note: In this example I am storing all the data as claims but you can also persist it in the database using ASP.NET Identity.

 

  • Let us add some code which uses this FacebookAccessToken and gets the list of friends and their pictures

 

    • You can add a link in Views\Shared\_LoginPartial.cshtml
 1: <li>
 2:       @Html.ActionLink("FacebookInfo", "FacebookInfo","Account")
 3: </li>
    • When a user logs in and clicks this link, we will execute code to get the list of Friends and their pictures

 

    • Get the claims from UserIdentity and store in the database. In this code we are reading the claims that we got from the OWIN middleware and store the claim (FacebookAccessToken) in the ASP.NET Identity membership database.
Code Snippet
  1. //
  2.         // GET: /Account/LinkLoginCallback
  3.         publicasyncTask<ActionResult> LinkLoginCallback()
  4.         {
  5.             var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(XsrfKey, User.Identity.GetUserId());
  6.             if (loginInfo == null)
  7.             {
  8.                 return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
  9.             }
  10.             var result = await UserManager.AddLoginAsync(User.Identity.GetUserId(), loginInfo.Login);
  11.             if (result.Succeeded)
  12.             {
  13.                 var currentUser = await UserManager.FindByIdAsync(User.Identity.GetUserId());
  14.                 //Add the Facebook Claim
  15.                 await StoreFacebookAuthToken(currentUser);
  16.                 return RedirectToAction("Manage");
  17.             }
  18.             return RedirectToAction("Manage", new { Message = ManageMessageId.Error });
  19.         }
      • Line 14-15 stores the FacebookAccessToken in the ASP.NET Identity database.
      • StoreFacebookAuthToken gets the claims from the UserIdentity and persists the AccessToken in the database as a User Claim. LinkLoginCallback action is called when the user is logged in and is associating another login provider.

ExternalLoginConfirmation

        action is called when you login with the Facebook provider for the first time. 

In Line 26, once the User is created we add a new line to add  the FacebookAccessToken as a claim for the user.

Code Snippet
  1. [HttpPost]
  2.         [AllowAnonymous]
  3.         [ValidateAntiForgeryToken]
  4.         publicasyncTask<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, string returnUrl)
  5.         {
  6.             if (User.Identity.IsAuthenticated)
  7.             {
  8.                 return RedirectToAction("Manage");
  9.             }
  10.  
  11.             if (ModelState.IsValid)
  12.             {
  13.                 // Get the information about the user from the external login provider
  14.                 var info = await AuthenticationManager.GetExternalLoginInfoAsync();
  15.                 if (info == null)
  16.                 {
  17.                     return View("ExternalLoginFailure");
  18.                 }
  19.                 var user = newApplicationUser() { UserName = model.Email };
  20.                 var result = await UserManager.CreateAsync(user);
  21.                 if (result.Succeeded)
  22.                 {
  23.                     result = await UserManager.AddLoginAsync(user.Id, info.Login);
  24.                     if (result.Succeeded)
  25.                     {
  26.                         await StoreFacebookAuthToken(user);
  27.                         await SignInAsync(user, isPersistent: false);
  28.                         return RedirectToLocal(returnUrl);
  29.                     }
  30.                 }
  31.                 AddErrors(result);
  32.             }
  33.  
  34.             ViewBag.ReturnUrl = returnUrl;
  35.             return View(model);
  36.         }
      • ExternalLoginCallback action is called when you associate the User with an external login provider for the first time. In line 17 we add a new line to add  the FacebookAccessToken as a claim for the user.
Code Snippet
  1. //
  2.         // GET: /Account/ExternalLoginCallback
  3.         [AllowAnonymous]
  4.         publicasyncTask<ActionResult> ExternalLoginCallback(string returnUrl)
  5.         {
  6.             var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
  7.             if (loginInfo == null)
  8.             {
  9.                 return RedirectToAction("Login");
  10.             }
  11.  
  12.             // Sign in the user with this external login provider if the user already has a login
  13.             var user = await UserManager.FindAsync(loginInfo.Login);
  14.             if (user != null)
  15.             {
  16.                 //Save the FacebookToken in the database if not already there
  17.                 await StoreFacebookAuthToken(user);
  18.                 await SignInAsync(user, isPersistent: false);
  19.                 return RedirectToLocal(returnUrl);
  20.             }
  21.             else
  22.             {
  23.                 // If the user does not have an account, then prompt the user to create an account
  24.                 ViewBag.ReturnUrl = returnUrl;
  25.                 ViewBag.LoginProvider = loginInfo.Login.LoginProvider;
  26.                 return View("ExternalLoginConfirmation", newExternalLoginConfirmationViewModel { Email = loginInfo.Email });
  27.             }
  28.         }
    • Store the FacebookAccessToken as a User Claim in the ASP.NET Identity database
Code Snippet
  1. privateasyncTask StoreFacebookAuthToken(ApplicationUser user)
  2.         {
  3.             var claimsIdentity = await AuthenticationManager.GetExternalIdentityAsync(DefaultAuthenticationTypes.ExternalCookie);
  4.             if (claimsIdentity != null)
  5.             {
  6.                 // Retrieve the existing claims for the user and add the FacebookAccessTokenClaim
  7.                 var currentClaims = await UserManager.GetClaimsAsync(user.Id);
  8.                 var facebookAccessToken = claimsIdentity.FindAll("FacebookAccessToken").First();
  9.                 if (currentClaims.Count() <=0 )
  10.                 {
  11.                     await UserManager.AddClaimAsync(user.Id, facebookAccessToken);
  12.                 }

 

    • Add the following in AccountViewModel.cs for binding view model binding
 1:     public class FacebookViewModel
 2:     {
 3:         [Required]
 4:         [Display(Name = "Friend's name")]
 5:         public string Name { get; set; }
 6:  
 7:         public string ImageURL { get; set; }
 8:     }

 

    • Add the following Code in AccountController to get the pictures of your Friends
      •    
Code Snippet
  1. //GET: Account/FacebookInfo
  2. [Authorize]
  3. publicasyncTask<ActionResult> FacebookInfo()
  4. {
  5.     var claimsforUser = await UserManager.GetClaimsAsync(User.Identity.GetUserId());
  6.     var access_token = claimsforUser.FirstOrDefault(x => x.Type == "FacebookAccessToken").Value;
  7.     var fb = newFacebookClient(access_token);
  8.     dynamic myInfo = fb.Get("/me/friends");
  9.     var friendsList = newList<FacebookViewModel>();
  10.     foreach (dynamic friend in myInfo.data)
  11.     {
  12.         friendsList.Add(newFacebookViewModel()
  13.            {
  14.                Name = friend.name,
  15.                ImageURL = @"https://graph.facebook.com/" + friend.id + "/picture?type=large"
  16.            });
  17.     }
  18.  
  19.     return View(friendsList);
  20. }
        

 

    • Add the following View to display the list of friends and the pictures
      • Create a View called FacebookInfo.cshtml under Views\Account folder and add the following markup
 1: @model IList<WebApplication96.Models.FacebookViewModel>
 2: @if (Model.Count > 0)
 3: {
 4:     <h3>List of friends</h3>
 5:     <div class="row">
 6:             @foreach (var friend in Model)
 7:             {
 8:               <div class="col-md-3">
 9:                 <a href="#" class="thumbnail">
 10:                  <img src=@friend.ImageURL alt=@friend.Name />
 11:                 </a>
 12:               </div>
 13:              }
 14:     </div>
 15: }
  • At this point you are all set to see your friends information
  • Run the project and log in using Facebook. You should be taken to the Facebook Site where when you successfully login and grant this app permissions to access this data, then you should be redirected back to the application.
  • When you click the FacebookInfo Link you should see a page which looks like the following

image

Conclusion

This was an easy way to extend the Social providers and get more information about the logged in user so you can provide a rich experience for the web site users. You can do this with the other Social Providers as well. If you have any questions, please visit the asp.net/forums or reach me via twitter (@rustd)

  • Interesting Finds: October 18, 2013

  • ClaimsIdentity on the controller never has the claims generated from within OnAuthenticated.

  • Same issue as NP.  Claims data seems to get lost somewhere along the way and is not available in the controller.

  • Hi, I have fixed some issues with the sample and I have updated the steps.

  • Great example, thanks! What about other providers? E.g. to get email (and maybe first/last name) from Facebook, Twitter, Microsoft, and Google?

  • How do we do this:

    {

       OnAuthenticated = async context =>

       {

           context.Identity.AddClaim(

           new System.Security.Claims.Claim("FacebookAccessToken",

                                                context.AccessToken));

       }

    };

    In VB..??

  • OK I have managed to convert all of this to VB except the last part with the new view (facebookinfo).

    Except I am new to MVC also so have no idea what is going on with that code, and therefore have no idea how to convert it to VB. Any clues anyone?

  • Never mind, done it, if anybody wants this code in VB let me know...

    Would be nice if Microsoft themselves would provide some support for this stuff in VB AND Web Forms.

    Wasted two days on getting this converted..

  • Question whats the app meaning?

    Example: app.UseFacebookAuthentication(x);

  • A question:

    At ExternalLoginCallback  beginning: var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();

    my result type is Microsoft.AspNet.Identity.Owin.ExternalLoginInfo which only contains a fixed DefaultUserName and the Login property. How do I get email address and other info (like full name) from loginInfo? There isn't any Email or other properties.

    Thanks

  • I think the e-mail address for all of the social logins should be returned. There's really no other way to merge identities. If someone logs into FB once, then logs in with Twitter, we should be able to know they first logged in with FB (if those accounts use the same e-mail). Same for GPlus and MS and Open.

    It's fine to have this extra coding for getting app-specific information like 'friends', but basic user information should automatically be requested and placed into the loginInfo response.

  • I totally agree with ChicagoDave about the email.

  • How to to this with GoogleAuthenticationOptions? That class does not have the x.Scope() property!

  • 2 ChicagoDave & woot : it's not that simple re. retrieving email. The default MVC5 code returns email for Google only! The code above helps you with Facebook email. Similar you can do with Microsoft. But as for Twitter - no way. I'd love if someone share the code how to do it.

  • Hello thx a lot; but i have the same issue as Péter Novák.

    loginInfo only contains DefaultUserName  and Login as properties.

    -> ExternalLoginCallback

    You have any ideas ? I'm pretty stuck with it right now.

Page 1 of 2 (19 items) 12