Click here to Skip to main content
15,885,914 members
Articles / Web Development / HTML

Really Simple Way to Extract Facebook Authentication Token and Upload Pictures to Facebook on Behalf of Another User

Rate me:
Please Sign up or sign in to vote.
4.67/5 (2 votes)
18 Dec 2014CPOL6 min read 33.6K   472   12   1
Custom authentication provider which extracts Facebook user authentication token and a website sample code impersonating a Facebook user.

Introduction

If you ever wondered how social publishing companies like Hootsuite or KontentKloud manage to save user's Facebook login data and later post messages to Facebook on behalf of that user, this article has the answers. You can also download the complete project discussed here.

Background

Latest updates to Microsoft Visual Studio made it very easy to create a website which enables users to log in with external login provider, like Facebook. All you need to do is to get app keys from Facebook developer site. Unfortunately, the default Facebook Authentication Provider supplied by Microsoft does not allow saving user's authentication token for later reuse.

In this article, I show how to create a very simple authentication provider that does exactly that, allowing you to save Facebook authentication token to a database, and later log in and upload pictures to Facebook impersonating another user.

Using the Code

Here are the 5 steps to build the example web site:

  1. Create default MVC website
  2. Add custom authentication provider which extracts user token
  3. Add token field to user profile where the token will be saved
  4. Customize Account controller to actually save token into the user profile during registration
  5. Add code that uses persisted token when uploading picture to Facebook

It might sound complicated but in fact it takes just several lines of code to accomplish all this.

Step 1

Open Visual Studio 2013 and create default MVC website using Individual User Accounts for authentication.

Default MVC website

Call the project PersistentFacebookAuth.

Step 2

Add new class to the project, call it MyFacebookAuthenticationProvider.cs. Add this code to the class:

C#
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.Owin.Security.Facebook;

namespace PersistentFacebookAuth
{
    public class MyFacebookAuthenticationProvider : FacebookAuthenticationProvider
    {
        public const string MyTokenClaimName = "my:FacebookToken";

        public override Task Authenticated(FacebookAuthenticatedContext context)
        {
            context.Identity.AddClaim(new Claim(MyTokenClaimName, context.AccessToken));
            return base.Authenticated(context);
        }
    }
}

This code extends the default FacebookAuthenticationProvider with one line of code, which takes AccessToken from authentication context and saves it in the Claims collection. Later, in the Account controller, we will find the token in the Claims collection using the claim name "my:FacebookToken". This is an arbitrary name, you can come up with your own, it just must be unique in the collection - for convenience, we store it in the MyTokenClaimName constant.

We are now ready to configure our website for Facebook login utilizing new authentication provider.

In the App_Start folder, find Startup.Auth.cs file and open it. Find this place in the code:

C#
//app.UseFacebookAuthentication(
//   appId: "",
//   appSecret: "");

If you uncomment those lines, the default provider will be used. We don't need that. Instead, we will add code that directs the app to use our custom provider:

C#
var fbOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions
{
    SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie,
    AppId = "--your id here --",
    AppSecret = "-- your key here --",
    Provider = new MyFacebookAuthenticationProvider()
};

fbOptions.Scope.Add("email");
fbOptions.Scope.Add("read_stream");
fbOptions.Scope.Add("publish_stream");

app.UseFacebookAuthentication(fbOptions);

Don’t forget to copy AppId and AppSecret keys from Facebook developer site. (Simple description of the procedure can be found here).

You can also change the Scope collection - it defines the permissions your app will request from the user when authenticating.

Step 3

Now, we need a place to store the authentication token. Any place that can hold long strings will do. In this example, we add another column to the AspNetUsers database table holding user profile. You really don't need to know much about that database. Just add one line of code and the default backend in the project will do the rest.

In the Models folder, find file IdentityModels.cs and open it. Add the FacebookToken field declaration to the ApplicationUser class:

C#
public string FacebookToken { get; set; }

The ApplicationUser class should now look like this:

C#
public class ApplicationUser : IdentityUser
{
    public string FacebookToken { get; set; }

    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        // Note the authenticationType must match the one defined 
        // in CookieAuthenticationOptions.AuthenticationType
        var userIdentity = await manager.CreateIdentityAsync
                (this, DefaultAuthenticationTypes.ApplicationCookie);
        // Add custom user claims here
        return userIdentity;
    }
}

On the first run of your website, the database will be generated and the FacebookToken column will be added to the AspNetUsers table.

Step 4

Now, we need to modify the Account controller to actually save token into the user profile during the registration process. This happens after the user authenticates with Facebook and confirms the registration.

In the Controllers folder, find and open the AccountController.cs file. Find the ExternalLoginConfirmation function:

C#
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ExternalLoginConfirmation(ExternalLoginConfirmationViewModel model, 
    string returnUrl)

Inside the function, find the line where new ApplicationUser is constructed:

C#
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };

Here, we will add our authentication token which was already extracted by our custom authentication provider. Add a new line of code above the ApplicationUser constructor:

C#
var tokenClaim = info.ExternalIdentity.Claims.FirstOrDefault
    (c => c.Type == MyFacebookAuthenticationProvider.MyTokenClaimName);

Here, we search through the Claims collection to find our custom claim holding the authentication token.

Now, modify the ApplicationUser constructor to add the FacebookToken value:

C#
var user = new ApplicationUser 
    { UserName = model.Email, Email = model.Email, FacebookToken = tokenClaim.Value };

The resulting code fragment, saving token with the user profile, should look like this:

C#
var tokenClaim = info.ExternalIdentity.Claims.FirstOrDefault
(c => c.Type == MyFacebookAuthenticationProvider.MyTokenClaimName);
var user = new ApplicationUser 
{ UserName = model.Email, Email = model.Email, FacebookToken = tokenClaim.Value };
var result = await UserManager.CreateAsync(user);

This completes the part which extracts and saves authentication token of a Facebook user.

In the next step, we will change default Home page view and controller to demonstrate how to use the token when uploading pictures to the user's Facebook account without requiring user to log in.

Step 5 - Upload Example

The goal of this step is to modify Home page to display a File Upload button with the list of registered users, allowing you to upload a picture to the Facebook profile of selected user.

To save you some time, the code utilizes Facebook library which you can get from NuGet. Right-click on the project name, select "Manage NuGet Packages..." option, and enter "facebook" into the search box:

package

Find the Facebook package and click Install. You are now ready to complete our project.

In the Controllers folder, find and open the HomeController.cs file.
First thing we want to do here is to display the list of currently registered users who have Facebook login information. Modify Index function like this:

C#
public ActionResult Index()
{
    var users = HttpContext.GetOwinContext().GetUserManager
    <ApplicationUserManager>().Users.Where(u => u.FacebookToken != null).ToList();
    if (users != null && users.Count > 0) ViewBag.Users = users;
           
    return View();
}

Here, we put the list of users into the ViewBag. Now let's modify the Home Index View. In the Views/Home folder, find Index.cshtml file and open it.

Add this HTML code to any place you like:

HTML
@if (ViewBag.Users != null)
{
    <div class="col-md-8">
        @using (Html.BeginForm("Upload", "Home", 
        FormMethod.Post, new { enctype = "multipart/form-data" }))
        {
            <div class="form-horizontal">
                <div class="form-group">
                    <label class="col-md-6 control-label">Select picture to upload:</label>
                    <div class="col-md-6">
                        <input type="file" name="file" />
                    </div>
                </div>
                <div class="form-group">
                    <label class="col-md-7 control-label">Choose Facebook account to upload
                    </label>
                </div>
                @foreach (PersistentFacebookAuth.Models.ApplicationUser user in ViewBag.Users)
                {
                    <div class="form-group">
                        <label class="col-md-6 control-label">@user.Email</label>
                        <div class="col-md-6">
                            <button class="btn btn-success" id="userID" 
                            name="userID" value="@user.Id">Upload</button>
                        </div>
                    </div>
                }
            </div>
        }
    </div>
}

What is essential here is the HTML Form, input type="file", and foreach statement listing registered users' emails with corresponding submit button. When you click a button, the Upload action of the home controller will be called with the corresponding userID value.

Now let's create that Upload action function which will do all the uploading work.

Open again the HomeController.cs file. Add this code there:

C#
[HttpPost]
public async Task<ActionResult> Upload(string userID)
{
    if (Request.Files.Count > 0)
    {
        var selectedUser = await HttpContext.GetOwinContext().GetUserManager
        <ApplicationUserManager>().FindByIdAsync(userID);
        var facebookUserID = selectedUser.Logins.Where
        (u => u.LoginProvider == "Facebook").Select(u => u.ProviderKey).FirstOrDefault();
        var facebookUserToken = selectedUser.FacebookToken;

        var imgFile = Request.Files[0];

        if (imgFile != null && imgFile.ContentLength > 0)
        {
            var pictureBytes = new byte[imgFile.ContentLength];
            imgFile.InputStream.Read(pictureBytes, 0, imgFile.ContentLength);

            await uploadToFacebook(facebookUserID, 
            facebookUserToken, pictureBytes, Path.GetFileName(imgFile.FileName));

            return Redirect("<a abp="9652" 
            href="https://www.facebook.com/">https://www.facebook.com/" + facebookUserID);
        }
    }

    return RedirectToAction("Index");
}

First, we take supplied userID and retrieve selectedUser data from the profile database table. For that user, we populate facebookUserID and facebookUserToken variables - we will use these values when uploading picture to Facebook.

After that, we retrieve picture file name and image bytes array from the Request, and send all the data to the uploadToFacebook function.

Below is the code for the uploadToFacebook function, which actually uploads the picture to Facebook impersonating the selected user.

C#
private async Task<bool> uploadToFacebook(string facebookUserID, 
string token, byte[] pictureBytes, string fileName)
{
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

    var mediaObj = new FacebookMediaObject
    {
        FileName = fileName,
        ContentType = "image/" + Path.GetExtension(fileName).Substring(1),
    };
    mediaObj.SetValue(pictureBytes);

    var args = new Dictionary<string, object>();
    args.Add("source", mediaObj);

    var client = new FacebookClient(token);
    await client.PostTaskAsync(string.Format("/{0}/photos", facebookUserID), args);

    return true;
}

In the first line here, we tell .NET library to use TLS security protocol - Facebook stopped responding to default SSL3 requests.

Then, we pack image bytes into the FacebookMediaObject, and add that object to the arguments collection.

Now, we get to the place where we use authentication token in the FacebookClient constructor. This is actually the point of the whole exercise - to impersonate a Facebook user using the token saved during registration procedure.

Finally, we submit the upload request to Facebook and redirect requestor to the user's image library.

That's it. You now have a working website that allows you to upload pictures to another user's Facebook album.

Download the source code to see all the code and try it.

Remember, this is an example, not a production quality website. Besides, you should never allow strangers to post to other users Facebook profile (see license and other documents on the Facebook site).

Points of Interest

Similar code can be used to connect to Twitter or LinkedIn. If you'd like to see how that can be done leave a comment here and I'll post another article with more details.

You can see how this code works in real production system here: https://my.kontentkloud.com/account/register/010basic

I am planning another article describing how to connect your website to Microsoft Office 365 business cloud. Leave me a comment if you have a specific question on that topic.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionPlease check the attached source code... Pin
Stoffe8118-Dec-14 21:52
Stoffe8118-Dec-14 21:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.