0

I have an MVC5 app and have figured out how to authenticate with facebook initially using Identity 2.0. Other than adding email to the scope, it is basically default:

        var options = new FacebookAuthenticationOptions()
        {
            AppId = "##################",
            AppSecret = "######################"
        };
        options.Scope.Add("email");
        app.UseFacebookAuthentication(options);

In another section of my app, it is possible that the user would need to upgrade the facebook token to be able to publish to their wall as well (I was hoping to just add options.Scope.Add("publish_stream");). When I cut the above code out and place it inside the catch here (the code below is referenced here at facebooksdk.net):

    try {
    var client = new FacebookClient("my_access_token");
    dynamic result = client.Get("me/friends");
} catch (FacebookOAuthException) {
    // Our access token is invalid or expired
    // Here we need to do something to handle this.
}

It doesn't like the app. for starters and I can't figure out what to replace that with. What is the correct way to go about this? I realize there are examples on facebooksdk.net for whatever token request they do, but I was hoping to integrate it with Identity 2.0. Or does it matter? I'm not sure I'm understanding the Identity process correctly and not really familiar with facebook authentication either, so hopefully someone can understand what I'm trying to do and guide me? My guess would be that I simply need to pass my facebook token in the Logins SQL table to facebook where it would ask the user to reauthenticate with the elevated permissions? If the token doesn't initially exist in the Logins table, the process will add it there.

For the record, I realize I can request the permissions initially (when the user logs in to our site with facebook for the first time), but we're trying not to scare off the more savvy users initially. So, the idea would be when they click to publish their article, if they opt to publish to facebook, it will then request the upgrade of the token the first time and then hopefully never again (which the never part may be wishful thinking).

Thank you.

Community
  • 1
  • 1
Sum None
  • 2,164
  • 3
  • 27
  • 32

1 Answers1

0

Facebook can be confusing. The token you need depends on the action you are trying to achieve. For instance,

  • You use your app token to request a user token that has specific permissions.
  • You would then use that USER TOKEN to access the facebook api on behalf of the user.

In the code you posted, it appears you have the access token that you are sending to facebook to be the access token granted to your app ... not the given user's access token. You need to capture (and store) the user's access token given to you by facebook after having successfully been granted access for a given user. More on this here: https://developers.facebook.com/docs/facebook-login/access-tokens

Here is some sample code (used in startup_auth.cs) that uses the Identity framework. It captures the access token and persists it (as a claim) for a given user. The access token can be read later for access to the facebook api for a user at any given time.

// Facebook integration
var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
{
    AppId = ConfigurationManager.AppSettings["Facebook.appID"],
    AppSecret = ConfigurationManager.AppSettings["Facebook.appSecret"],
    Provider = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationProvider()
    {
        OnAuthenticated = (context) =>
        { 
            // STORE THE USER ACCESS TOKEN GIVEN TO US BY FACEBOOK FOR THIS USER
            context.Identity.AddClaim(new System.Security.Claims.Claim("urn:facebook:access_token", context.AccessToken, XmlSchemaString, "Facebook"));
            foreach (var x in context.User)
            {
                var claimType = string.Format("urn:facebook:{0}", x.Key);
                string claimValue = x.Value.ToString();
                if (!context.Identity.HasClaim(claimType, claimValue))
                                        context.Identity.AddClaim(new Claim(claimType, claimValue, XmlSchemaString, "Facebook"));

             }
             return Task.FromResult(0);
        }
    }
 };


 facebookOptions.Scope.Add("email");
 facebookOptions.Scope.Add("publish_actions");
 app.UseFacebookAuthentication(facebookOptions);

So now to retrieve the access token FOR THE USER, you would retrieve it (in the callback function/url from facebook) like this:

public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
    var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
    var accessToken = loginInfo.ExternalIdentity.Claims
                    .Where(c => c.Type.EndsWith("facebook:access_token"))
                    .FirstOrDefault();
    ....
}
Domin8urMind
  • 1,314
  • 8
  • 10
  • Ok, thanks for clearing that up a bit. But, my default startup_auth looks quite a bit different than yours. Yet, notice your first code chunk has that `app` reference on the very last line....In mine it references `IAppBuilder app`, but what is it? To simplify, seems like maybe I want to add an `if` statement to those facebookoptions in the startup_auth...like if (returnurl contains some reference), then add the publish_actions scope? Then in my controller where the try/catch statement is, call the last code chunk you referenced for the catch statement? Thanks, I'll try to digest this more... – Sum None Jul 08 '15 at 10:38
  • After a lot more reading, you're correct in that all the tokens are confusing and I was thinking about it mostly wrong. But, I finally had my eureka moment and your answer is mostly what I was looking for.... Thanks again. – Sum None Jul 09 '15 at 13:20