Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Announcement] Facebook 3.0.1 and lower no longer work #38

Closed
Tratcher opened this issue Mar 27, 2017 · 78 comments
Closed

[Announcement] Facebook 3.0.1 and lower no longer work #38

Tratcher opened this issue Mar 27, 2017 · 78 comments
Milestone

Comments

@Tratcher
Copy link
Member

Tratcher commented Mar 27, 2017

Facebook as deprecated their old OAuth endpoints that were used by Katana 3.0.1 and lower.
Here's a Fiddler trace of a failing auth flow:

#	Result	Protocol	Host	URL	Body	Caching	Content-Type	Process	Comments	Custom	
1191	302	HTTPS	localhost:44318	/Account/ExternalLogin	0	private		chrome:16236			
1202	302	HTTPS	www.facebook.com	/dialog/oauth?response_type=code&client_id=569522623154478&redirect_uri=https%3A%2F%2Flocalhost%3A44318%2Fsignin-facebook&scope=&state=gQ2fRAt8BI46eC52Z_YdSFMCYbBleCGJO5Jl1BO4yQQFl0dVjx-Z0EqMS6QGNfIHD6n7fApnqdodg6ea4E7Ky9rsnExnoW22a7mV7uYAnj089d3yKm6TN4F2YoFgeVPZPakdddB_D-b8988omDTjeQPHrfSVNFqqATAsvab15PHkSaCuk5OqWZRJUnkKtfanM2uA9E8PH4_JrNrLc4DZyd0tRfGr0C3aHUkPJMUiEq0	0	private, no-cache, no-store, must-revalidate; Expires: Sat, 01 Jan 2000 00:00:00 GMT	text/html	chrome:16236			
1203	302	HTTPS	localhost:44318	/signin-facebook?code=AQDQ5k6PZ623JZqcDBHkeK6-uryTkyuKZWGD5Hk_rcLV2sYArbQKJE-d-WIvHGikif_5VtclZojZUInsOmV_KEYUYD9jL3Gn0qKKzdk6574_Ya8IoAVPpvm9hsHZ50bKAxItn01fTW54hhGRxUFpX4yCLqXrLtVgImctDxM73XGTEq7poHN7nglEcU0TjFKImpc4Pu-FkkrQXoDGrD4Xeig4NCJHzvJcw8Oc8iJIBCJDSESi6Y2U5Y2Gsy4WntIkRnhCPqh3p--h_2LFqbRcjpx9KYrvPqhW9sr5eShwY1JJ8fVlKzBQmmQbgf0IvTcbckLnxJOPCsIyIFCs5xNf9AH4&state=gQ2fRAt8BI46eC52Z_YdSFMCYbBleCGJO5Jl1BO4yQQFl0dVjx-Z0EqMS6QGNfIHD6n7fApnqdodg6ea4E7Ky9rsnExnoW22a7mV7uYAnj089d3yKm6TN4F2YoFgeVPZPakdddB_D-b8988omDTjeQPHrfSVNFqqATAsvab15PHkSaCuk5OqWZRJUnkKtfanM2uA9E8PH4_JrNrLc4DZyd0tRfGr0C3aHUkPJMUiEq0	0			chrome:16236			
1205	200	HTTPS	graph.facebook.com	/oauth/access_token?grant_type=authorization_code&code=AQDQ5k6PZ623JZqcDBHkeK6-uryTkyuKZWGD5Hk_rcLV2sYArbQKJE-d-WIvHGikif_5VtclZojZUInsOmV_KEYUYD9jL3Gn0qKKzdk6574_Ya8IoAVPpvm9hsHZ50bKAxItn01fTW54hhGRxUFpX4yCLqXrLtVgImctDxM73XGTEq7poHN7nglEcU0TjFKImpc4Pu-FkkrQXoDGrD4Xeig4NCJHzvJcw8Oc8iJIBCJDSESi6Y2U5Y2Gsy4WntIkRnhCPqh3p--h_2LFqbRcjpx9KYrvPqhW9sr5eShwY1JJ8fVlKzBQmmQbgf0IvTcbckLnxJOPCsIyIFCs5xNf9AH4&redirect_uri=https%3A%2F%2Flocalhost%3A44318%2Fsignin-facebook&client_id=xxxxx&client_secret=xxxxxxxx	251	private, no-cache, no-store, must-revalidate; Expires: Sat, 01 Jan 2000 00:00:00 GMT	application/json; charset=UTF-8	iisexpress:1144			
1206	302	HTTPS	localhost:44318	/Account/ExternalLoginCallback?error=access_denied	442	private	text/html; charset=utf-8	chrome:16236			
1207	200	HTTPS	localhost:44318	/Account/Login	2,134	private	text/html; charset=utf-8	chrome:16236			

Note the /Account/ExternalLoginCallback?error=access_denied request.

If you enable logging here's the message (caused by a change in Facebook's response format):
https://github.com/aspnet/AspNetKatana/wiki/Debugging#logging

Microsoft.Owin.Security.Facebook.FacebookAuthenticationMiddleware Error: 0 : Authentication failed
System.ArgumentNullException: Value cannot be null.
Parameter name: stringToEscape
   at System.Uri.EscapeDataString(String stringToEscape)
   at Microsoft.Owin.Security.Facebook.FacebookAuthenticationHandler.<AuthenticateCoreAsync>d__0.MoveNext()
    ProcessId=1144
    DateTime=2017-03-27T20:53:38.6034000Z

This has been fixed in Katana 3.1.0-RC1 which is now available on nuget.org.

@Tratcher Tratcher added this to the Discussions milestone Mar 27, 2017
@refactorthis
Copy link

Thanks for this, noticed this wasn't working this morning. Looks like a few other sites have been hit by this also.

@arnoldsi-tr
Copy link

This stopped working also for my website

@embee8
Copy link

embee8 commented Mar 28, 2017

This saved me even more pain. I noticed that Facebook Login on my site stopped to work two days ago. After hours of investigating an error that came out of nowhere, installing 3.1.0 RC1 resolved the issue. The middleware now handles the JSON response properly.

@Andrei-
Copy link

Andrei- commented Mar 28, 2017

Been debugging this myself today. The problem is in FacebookAuthenticationHandler line 90:
...
[request to https://graph.facebook.com/oauth/access_token here]
88: tokenResponse.EnsureSuccessStatusCode();
89: string text = await tokenResponse.Content.ReadAsStringAsync();
90: IFormCollection form = WebHelpers.ParseForm(text);

The code make a request to https://graph.facebook.com/oauth/access_token and then tries to parse the response and ParseForm method expects a parameter with query string like format - key1=value&key2=value ,
but now after facebook API changes the response comes as a json -
{ "key1": "value", "key2":"value"},
so the access_token cannot be extracted any more and the whole world crashes silently.

It looks like an easy fix, any chance someone will update the repo and push a new version to nugget?
Would do it myself but....

@wizzar
Copy link

wizzar commented Mar 28, 2017

@andrei, you can get the updated version at https://www.nuget.org/packages/Microsoft.Owin.Security.Facebook/3.1.0-rc1, or go to Tools > nuget package manager > manage packets for solution, click on "updates" and check "include prerelease".

Cheers

@Andrei-
Copy link

Andrei- commented Mar 28, 2017

@wizzar, awesome, thanks.

@paul-turner-github
Copy link

This doesn't seem to be fixed. I still get the same error with Facebook.

@YovavGad
Copy link

YovavGad commented Mar 29, 2017

Still not working, I updated to the latest version 3.1.0-rc1 and made sure all DLLs got updated,

I'm using it like this:


var facebookAuthOptions = new FacebookAuthenticationOptions();
facebookAuthOptions.AppId = "xxx";
facebookAuthOptions.AppSecret = "yyy";
facebookAuthOptions.SendAppSecretProof = true;
facebookAuthOptions.CallbackPath = new PathString("/signin-facebook");

facebookAuthOptions.Scope.Add("public_profile");
facebookAuthOptions.Scope.Add("email");
facebookAuthOptions.Scope.Add("user_birthday");

// added for Microsoft.Owin.Security.Facebook/3.1.0-rc1
facebookAuthOptions.Fields.Add("email");
facebookAuthOptions.Fields.Add("birthday");
facebookAuthOptions.Fields.Add("gender");
facebookAuthOptions.Fields.Add("locale");
facebookAuthOptions.Fields.Add("location");
facebookAuthOptions.Fields.Add("timezone");

facebookAuthOptions.Provider = new FacebookAuthenticationProvider()
{
    OnAuthenticated = (context) =>
    {
        context.Identity.AddClaim(new Claim("FacebookAccessToken", context.AccessToken));

        var expiryDuration = context.ExpiresIn ?? new TimeSpan();
        context.Identity.AddClaim(new Claim("urn:facebook:expires_in", DateTime.UtcNow.Add(expiryDuration).ToString(CultureInfo.InvariantCulture)));

        // Add all other available claims
        foreach (var claim in context.User)
        {
            var claimType = string.Format("urn:facebook:{0}", claim.Key);
            var claimValue = claim.Value.ToString();
            if (!context.Identity.HasClaim(claimType, claimValue))
                context.Identity.AddClaim(new Claim(claimType, claimValue, "XmlSchemaString", "Facebook"));
        }

        return Task.FromResult(0);
    }
};
app.UseFacebookAuthentication(facebookAuthOptions);

Any ideas?

UPDATE: I spent hours on this today, not resolved yet, I deleted my facebook web app (it was API 2.4) and created a new one (2.8) that is not approved yet, but I think test app should be working, so far I'm getting a 302 error=access_denied

UPDATE 2: new facebook app is approved, still not working. not even without using the FacebookAuthenticationProvider

The net traffic looks like this:

https://localhost:44300/external-providers
(302)
https://www.facebook.com/v2.8/dialog/oauth?response_type=code&client_id=1897713540512505&redirect_uri=https%3A%2F%2Flocalhost%3A44300%2Fsignin-facebook&scope=&state=OXOiCtOJPEK5WCQkEJt-mH3lxEnTCXG3YcGFtW7j_dyuPd_9LevphavHobpB6EH3Wfb8BpbhqST13SacFMB3OnddsgnslzcAX7KnMpzpnEcfFI3KErmByenV9xn1IEt7PhY4TjXi5KHFNUp1PVu_ikUBnvf0hJpY2IK0fczvQLVmGKplJV-g6qsEMYXfKHNYoqjDiu9cA-iGIPXfRxPxZg
(302)
https://localhost:44300/signin-facebook?code=AQDrGJ9sqk0ENVw_7RW3zmauzV1Gf6rWXBotgAKf6uBbdryamL5RMHiOgIrBiPYdy8CA9HC9hq2sahu2bt-VwznOJ8PNFTxRmLCe0PRrwxSsffscHSdNzjz16jjavuupse087mB_2KhZeKKWeS2Rlwabcfa-n4FGIcyDizpOKTWJl_J6PFAjWv3XfPy1zkbLz0cAsTdQ8t8pOARPhP5nKwUIGN5GuS4fzuGhgPCerlPyHknK0BWcFrRC0fa_n3X2d0-zANu6nzhRspssDTWmIAr9PZCLOlpSMlJeb1NrebECNbw4YqGzScCWxBRVL8YAfQ4&state=OXOiCtOJPEK5WCQkEJt-mH3lxEnTCXG3YcGFtW7j_dyuPd_9LevphavHobpB6EH3Wfb8BpbhqST13SacFMB3OnddsgnslzcAX7KnMpzpnEcfFI3KErmByenV9xn1IEt7PhY4TjXi5KHFNUp1PVu_ikUBnvf0hJpY2IK0fczvQLVmGKplJV-g6qsEMYXfKHNYoqjDiu9cA-iGIPXfRxPxZg
(302)
https://localhost:44300/signup-connect?error=access_denied
(302)
https://localhost:44300/signout
(200)

@embee8
Copy link

embee8 commented Mar 29, 2017

@YovavGad, it seems that 3.1.0 RC1 has fixed it for most people in this thread, so I assume it comes down to your personal configuration. Remember this is a place to track issues, not to troubleshoot custom setups. If you think the latest release doesn't work as intended, it might be useful for the project owners to provide additional information, i.e. what do you mean with "still not working"? Some debug logs would be helpful to further investigate the issue.

Tip: Try it without specifying the Provider. After upgrading to 3.1.0 RC1, I literally went back to the most basic setup where I only specify the App ID, Secret, and UserInformationEndpoint and it worked.

In your case:
facebookAuthOptions.UserInformationEndpoint = "https://graph.facebook.com/v2.8/me?fields=email,first_name,last_name[etc]";

@Tratcher
Copy link
Member Author

@embee8 Fields can be specified on Options now, just like Scope.

@embee8
Copy link

embee8 commented Mar 29, 2017

Fantastic news, @Tratcher! What I have struggled with in the past is to find documentation of all the different options. The boilerplate code in the standard MVC template only outlined the app secret and id. Is there an official and complete documentation out there?

@Tratcher
Copy link
Member Author

@embee8 nothing extensive, start with the Options doc comments.

@Tratcher
Copy link
Member Author

Or MSDN: https://msdn.microsoft.com/en-us/library/microsoft.owin.security.facebook.facebookauthenticationoptions(v=vs.113).aspx

@arinray73
Copy link

Hi,

I use webpages and use the following Microsoft.Web.WebPages.OAuth and call function OAuthWebSecurity.VerifyAuthentication to verify facebook logins.

In Nuget it is still 3.2.3 dated 9th February 2015.

what can be the solution please?

@Tratcher
Copy link
Member Author

@NTaylorMullen @Eilon ?

@Eilon
Copy link
Member

Eilon commented Mar 30, 2017

@arinray73 that package ultimately uses the 3rd-party DotNetOpenAuth library to do all the actual OAuth protocol implementation. Can you share more info about what isn't working for you?

@erzki
Copy link

erzki commented Mar 30, 2017

I can confirm that the 3.1.0-rc1 works now if you remove the custom FacebookAuthenticationProvider.

My config looks like this now. I have added email in scope.

var facebookOptions = new FacebookAuthenticationOptions
{
  AppId = ConfigurationManager.AppSettings["FacebookAppId"],
  AppSecret = ConfigurationManager.AppSettings["FacebookAppSecret"],
};
facebookOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookOptions);

@dnovhorodov
Copy link

dnovhorodov commented Mar 30, 2017

Greetings,

Just updated Microsoft.Owin.Security.Facebook package to 3.1.0-rc1.
I have very simple setup based on Microsoft example for external authentication:

var fbAuthOptions = new FacebookAuthenticationOptions();
fbAuthOptions.Scope.Add("email");
fbAuthOptions.AppId = "111111111111111";
fbAuthOptions.AppSecret = "secret_here";
 // Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/Account/Login")
});

app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
app.UseFacebookAuthentication(fbAuthOptions);

All goes fine according to Fiddler:

GET https://www.facebook.com/v2.8/dialog/oauth?response_type=code&client_id=111111111111111&redirect_uri=http%3A%2F%2Flocalhost%3A59735%2Fsignin-facebook&scope=email&state=v5WWRCHT93gXIGgkEYOtg57yD7BI7dhu6okwao41f8kZzgfcE5Zyq3W0QW2QmfMb-RXFuaovmNkL0wuP1RiLefnIOWkOaf4dkaHTo0C69V9aNn59KYcEd4z4fbyRct4g1jfb0OLYvHuaO6sDAcxejUTZiTIuC4gy4za-H6LsHdrETDsskb8iu2PQXZbR-4bzzn9j2H1rshS6AxN8xKuqFvKEABZ1OeJS2O4qKrxGyY8 HTTP/1.1

HTTP/1.1 302 Found
Location: http://localhost:59735/signin-facebook?code=longlongcode#_=_

GET http://localhost:59735/signin-facebook?code=longlongcodeHTTP/1.1
Host: localhost:59735

HTTP/1.1 302 Found
Location: /Account/ExternalLoginCallback
Set-Cookie: .AspNet.Correlation.Facebook=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT
Set-Cookie: .AspNet.ExternalCookie=somecodehere; path=/; HttpOnly

CONNECT graph.facebook.com:443 HTTP/1.1
Host: graph.facebook.com
Connection: Keep-Alive
HTTP/1.1 200 Connection Established

GET https://graph.facebook.com/v2.8/oauth/access_token?grant_type=authorization_code&code=somecodehere&redirect_uri=http%3A%2F%2Flocalhost%3A59735%2Fsignin-facebook&client_id=1111111111111111&client_secret=some_secret_here HTTP/1.1
HTTP/1.1 200 OK
{"access_token":"access_token_here","token_type":"bearer","expires_in":5109049}

GET https://graph.facebook.com/v2.8/me?access_token=some_token_here&%26appsecret_proof=proof_here HTTP/1.1
Host: graph.facebook.com
HTTP/1.1 200 OK

GET http://localhost:59735/Account/ExternalLoginCallback HTTP/1.1

So workflow should be fine. But when I do:

var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();

private IAuthenticationManager AuthenticationManager
        {
            get
            {
                return HttpContext.GetOwinContext().Authentication;
            }
        }

in my ExternalLoginCallback controller I get null. Why is that?
Please share how you get login information from external provider.

@embee8
Copy link

embee8 commented Mar 30, 2017

@Tratcher, I just noticed and wanted to let you know that the Fields attribute is read-only at the moment (3.1.0 RC1). People will need to continue to set the required UserInformationEndpoint instead.

@Tratcher
Copy link
Member Author

@embee8 what's the problem? Fields is a list you add to, just like scope. Neither let you assign the whole collection.

@embee8
Copy link

embee8 commented Mar 30, 2017

Gotcha @Tratcher, my bad. I tried to assign the whole thing at once. Thanks!

@Tratcher
Copy link
Member Author

@dnovhorodov I assume your /Account/ExternalLoginCallback request included the cookie?
Do you get any output if you enable logging? https://github.com/aspnet/AspNetKatana/wiki/Debugging

@dnovhorodov
Copy link

@Tratcher

@dnovhorodov I assume your /Account/ExternalLoginCallback request included the cookie?
Yes, external cookies in place.

I find out that it returns null only when I use HTTP protocol, but when I use HTTPS it works.
Before RC1 it works as well via HTTP. Is is possible to change setting to allow HTTP login flow?

@dnovhorodov
Copy link

dnovhorodov commented Mar 30, 2017

Noticed that scopes doesn't return info from FB. Earlier we had:

var fbAuthOptions = new FacebookAuthenticationOptions();
fbAuthOptions.Scope.Add("name");
fbAuthOptions.Scope.Add("email");

which works fine before. Now it should be changed to:

fbAuthOptions.Fields.Add("name");
fbAuthOptions.Fields.Add("email");

@embee8
Copy link

embee8 commented Mar 30, 2017

I haven't tested it @dnovhorodov, but I don't think that the fields are a replacement for the scope - those are two different things. In my view, you need to specify both, but the Katana middleware might take care of the scope if you specify the fields.

@embee8
Copy link

embee8 commented Mar 30, 2017

Okay folks, here is a configuration that works for me. The authentication itself works with minimal configuration. To retrieve specific fields, I still needed to use the FacebookAuthenticationProvider - otherwise the claims were not added and named properly.

var facebookOptions = new FacebookAuthenticationOptions()
{
	AppId = "xxxxx",
	AppSecret = "xxxxx",
};

// Set requested scope
facebookOptions.Scope.Add("email");
facebookOptions.Scope.Add("public_profile");

// Set requested fields
facebookOptions.Fields.Add("email");
facebookOptions.Fields.Add("first_name");
facebookOptions.Fields.Add("last_name");

facebookOptions.Provider = new FacebookAuthenticationProvider()
{
	OnAuthenticated = (context) =>
	{
		// Attach the access token if you need it later on for calls on behalf of the user
		context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));

		foreach (var claim in context.User)
		{
			//var claimType = string.Format("urn:facebook:{0}", claim.Key);
			var claimType = string.Format("{0}", claim.Key);
			string claimValue = claim.Value.ToString();

			if (!context.Identity.HasClaim(claimType, claimValue))
				context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Facebook"));
		}

		return Task.FromResult(0);
	}
};

app.UseFacebookAuthentication(facebookOptions);

With this configuration you can get and read from the user info like this:

var info = await AuthenticationManager.GetExternalLoginInfoAsync();

if (info != null)
{
	var firstName = info.ExternalIdentity.Claims.First(c => c.Type == "first_name").Value;
	var lastName = info.ExternalIdentity.Claims.First(c => c.Type == "last_name").Value;
}

@arinray73
Copy link

@Eilon
Thanks for your reply. I have used Webmatrix 3 starter site template code to implement external provider login in my site. The file is /Account/RegisterService.cshtml

These lines of code

var result = OAuthWebSecurity.VerifyAuthentication(Href("~/Account/RegisterService", new { returnUrl }));
if (result.IsSuccessful) {

-- never returns success after the fb API change

and moves to the following log in failure block

else {
Response.Redirect("~/Account/ExternalLoginFailure");
return;
}

I have updated the OWIN packages as described here but had no impact as I think it does not use these packages but uses the following which was last updated in February 2015

https://www.nuget.org/packages/Microsoft.AspNet.WebPages.OAuth/

@YovavGad
Copy link

I'm still unable to get it to work, even in the most simple way and without the provider, I am using https, I wonder if anyone else experiencing the same issues...

@Tratcher
Copy link
Member Author

@YovavGad what errors do you get?

@Tratcher
Copy link
Member Author

Tratcher commented Apr 5, 2017

@YovavGad @erikarenhill This should be fixed in the latest packages on https://dotnet.myget.org/f/katana-release/. We're doing final verification for release now, so please give these a try.

@YovavGad
Copy link

YovavGad commented Apr 5, 2017

@Tratcher what is this link? I get XML, are you going to create a RC2 nuget?

@Eilon
Copy link
Member

Eilon commented Apr 5, 2017

@YovavGad it's a NuGet package feed with the fixes. You can browse the feed here: https://dotnet.myget.org/gallery/katana-dev

And click on "Connect to feed" to see how to use the feed from your NuGet.config.

@YovavGad
Copy link

YovavGad commented Apr 5, 2017

This is not fixed, I used 4.0.0-alpha1-60323-110-rel, I double checked - all my references are updated.

(it does work correctly with the BackchannelHttpHandler from @erikarenhill that fixes the %26 character)

@erikarenhill
Copy link

@YovavGad @Eilon @Tratcher
I removed my custom backchannelhandler and tested with v4.0.0-alpha1-60405-127-dev and it's working for me from what I can tell. Thanks

@erikarenhill
Copy link

@YovavGad try with version 4.0.0-alpha1-60405-127-dev instead which is the latest, should work for you there as well.

@Tratcher
Copy link
Member Author

Tratcher commented Apr 5, 2017

Sorry about that, I need to purge some stale packages from the feed. We were working on 4.0.0 in the release branch before we went back to do 3.1.0. Be right back...

@Tratcher
Copy link
Member Author

Tratcher commented Apr 5, 2017

Cleared. You're looking for 3.1.0-rtm-60405-128-rel on https://dotnet.myget.org/f/katana-release/.

@YovavGad
Copy link

YovavGad commented Apr 6, 2017

Yes, I can confirm that 3.1.0-rtm-60405-128-rel is working.

@Tratcher are you planning to release v4 soon?

@Tratcher
Copy link
Member Author

Tratcher commented Apr 6, 2017

@YovavGad thanks for checking.

4.0 is waiting primarily on #7, which might be another few months due to needed dependency work.

@YovavGad
Copy link

YovavGad commented Apr 6, 2017

@Tratcher I'm just curious, are you releasing this fix before 4.0 as a nuget package? 3.1.0-rtm?

@Tratcher
Copy link
Member Author

Tratcher commented Apr 6, 2017

Yes, 3.1.0 will be released any day now. Just finishing the verification.

@Tratcher
Copy link
Member Author

@jpsvedal
Copy link

jpsvedal commented Apr 11, 2017

My Facebook login works great after upgrading to version 3.1.0 but when register, the email will not return automatically. This because the new Facebook API version requires me to request specific fields by name.
So i therefore changed from:

app.UseFacebookAuthentication(
               appId: " xxxxx ",
               appSecret: " xxxxx ");

to

app.UseFacebookAuthentication(new FacebookAuthenticationOptions
            {
                AppId = Environment.GetEnvironmentVariable("xxxxx "),
                AppSecret = Environment.GetEnvironmentVariable("xxxxx "),
                Scope = { "email" },
                Fields = { "name", "email" },
                CookieManager = new SystemWebCookieManager()
            });

But then i get the error

The 'AppId' option must be provided.
[ArgumentException: The 'AppId' option must be provided.]

when loading page.
Is the Environment.GetEnvironmentVariable ment to be referring to some place? In my case I only copied the appId and appSecret from my old one to the new one, but I’m not sure if I can hardcode it in this way?

@Tratcher
Copy link
Member Author

Tratcher commented Apr 11, 2017

Change
new FacebookAuthenticationOptions { AppId = Environment.GetEnvironmentVariable("xxxxx "), AppSecret = Environment.GetEnvironmentVariable("xxxxx "),
to
new FacebookAuthenticationOptions { AppId = "xxxxx ", AppSecret = "xxxxx ",

or define system environment variables facebook:appid=xxxx, facebook:appsecret=yyyy and set

new FacebookAuthenticationOptions { AppId = Environment.GetEnvironmentVariable("facebook:appid"), AppSecret = Environment.GetEnvironmentVariable("facebook:appsecret"),

@jpsvedal
Copy link

Thanks @Tratcher, the application no longer provide error, but the application does not seem to return the email, instead my URL looks like this: https://domain.com/Account/Login#_=_ after authenticated with Facebook.
I think this looks very similar to how it was before upgrading from 3.0.1 to 3.1.0.

@vpekarek
Copy link

I have problem with reading the identity from FB. My return url looks like this:
http://localhost:60723/signin-facebook?code=SOMELONGCODE#_=_

And I need to call facebook graph api with user id, but I can't read that from GetOwinContext().Authentication.User. It worked before.

How to set User.Identity to FB identity and Authenticate it?
When my login button is clicked, I call this:

properties = new AuthenticationProperties() { RedirectUri = this.ActualUrl(), IsPersistent = true};
Context.GetOwinContext().Authentication.Challenge(properties, "Facebook");

and when I apply the Facebook dialog, page is returned back with code param in query. How can I read user information here?

@Tratcher
Copy link
Member Author

@t00thy can you share a Fiddler trace of your login flow?

@vpekarek
Copy link

@Tratcher Hi, I finally got it work by using var loginInfo = Context.GetOwinContext().Authentication.GetExternalLoginInfo(); to load client info.

Now I have problem with login in on HTTPS. Getting 401 Unauthorized. But think, it would be problem with certificate on DEV machine.

So my working code is:
Startup.Auth.cs

var cookieOptions = new CookieAuthenticationOptions
{
    LoginPath = new PathString("/default.aspx")
};

app.UseCookieAuthentication(cookieOptions);
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
var facebookOptions = new FacebookAuthenticationOptions()
{
    AppId = "MyAppId",
    AppSecret = "MyAppSecret",
    SignInAsAuthenticationType = app.GetDefaultSignInAsAuthenticationType(),
    CallbackPath = new PathString("/Default.aspx/")
};

facebookOptions.Scope.Add("email");
facebookOptions.Scope.Add("public_profile");

facebookOptions.Fields.Add("email");
facebookOptions.Fields.Add("first_name");
facebookOptions.Fields.Add("last_name");

facebookOptions.Provider = new FacebookAuthenticationProvider()
{
    OnAuthenticated = (context) =>
    {
        // Attach the access token if you need it later on for calls on behalf of the user
        context.Identity.AddClaim(new System.Security.Claims.Claim("FacebookAccessToken", context.AccessToken));

        foreach (var claim in context.User)
        {
            var claimType = string.Format("{0}", claim.Key);
            var claimValue = claim.Value.ToString();

            if (!context.Identity.HasClaim(claimType, claimValue))
                context.Identity.AddClaim(new System.Security.Claims.Claim(claimType, claimValue, "XmlSchemaString", "Facebook"));
        }

        return Task.FromResult(0);
    }
};

app.UseFacebookAuthentication(facebookOptions);

When click on login button:

properties = new AuthenticationProperties() { RedirectUri = this.ActualUrl()};
Context.GetOwinContext().Authentication.Challenge(properties, "Facebook");
Response.StatusCode = 401;
Response.End();

When returning from FB:

var loginInfo = Context.GetOwinContext().Authentication.GetExternalLoginInfo();
var claimsPrincipal = new ClaimsPrincipal(loginInfo.ExternalIdentity);
Thread.CurrentPrincipal = claimsPrincipal;
var identity = (ClaimsPrincipal)Thread.CurrentPrincipal;

id = identity.Claims.Where(c => c.Type == ClaimTypes.NameIdentifier).Select(c => c.Value).SingleOrDefault();
email = identity.Claims.Where(c => c.Type == ClaimTypes.Email).Select(c => c.Value).SingleOrDefault();
//id = identity.Claims.Where(c => c.Type == "id").Select(c => c.Value).SingleOrDefault();
accessToken = identity.Claims.Where(c => c.Type == "FacebookAccessToken").Select(c => c.Value).SingleOrDefault();
firstName = identity.Claims.Where(c => c.Type == "first_name").Select(c => c.Value).SingleOrDefault();
lastName = identity.Claims.Where(c => c.Type == "last_name").Select(c => c.Value).SingleOrDefault();

@MichaelaIvanova
Copy link

I got it working thanks to : https://stackoverflow.com/questions/43058355/version-deprecation-facebook-graph-api-v2-2/43148543#43148543

I also updated to 3.1.0 and added the following properties:

 var fboptions = new FacebookAuthenticationOptions
            {

                AppId = ConfigHelper.GetValue("FacebookAppId"),
                AppSecret = ConfigHelper.GetValue("FacebookAppSecret"),
                BackchannelHttpHandler = new FacebookBackChannelHandler(),
                UserInformationEndpoint = "https://graph.facebook.com/v2.8/me?fields=id,name,email,first_name,last_name"
            };

            app.UseFacebookAuthentication(fboptions);

and the back channel class:

  public class FacebookBackChannelHandler : HttpClientHandler
    {
        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            if (!request.RequestUri.AbsolutePath.Contains("/oauth"))
            {
                request.RequestUri = new Uri(request.RequestUri.AbsoluteUri.Replace("?access_token", "&access_token"));
            }

            var result = await base.SendAsync(request, cancellationToken);
            if (!request.RequestUri.AbsolutePath.Contains("/oauth"))
            {
                return result;
            }

            var content = await result.Content.ReadAsStringAsync();
            var facebookOauthResponse = JsonConvert.DeserializeObject<FacebookOauthResponse>(content);

            var outgoingQueryString = HttpUtility.ParseQueryString(string.Empty);
            outgoingQueryString.Add(nameof(facebookOauthResponse.access_token), facebookOauthResponse.access_token);
            outgoingQueryString.Add(nameof(facebookOauthResponse.expires_in), facebookOauthResponse.expires_in + string.Empty);
            outgoingQueryString.Add(nameof(facebookOauthResponse.token_type), facebookOauthResponse.token_type);
            var postdata = outgoingQueryString.ToString();

            var modifiedResult = new HttpResponseMessage(HttpStatusCode.OK)
            {
                Content = new StringContent(postdata)
            };

            return modifiedResult;
        }
    }

    public class FacebookOauthResponse
    {
        public string access_token { get; set; }
        public long expires_in { get; set; }
        public string token_type { get; set; }
    }

@Tratcher
Copy link
Member Author

@MichaelaIvanova you really shouldn't need any of that stuff with 3.1.

@MichaelaIvanova
Copy link

@Tratcher I tried but works only with them

@Tratcher
Copy link
Member Author

What doesn't work without them? Do you get a specific error in the logs?

@MichaelaIvanova
Copy link

@Tratcher in the callback action method (ExternalLoginCallback)
var loginInfo = await OwinContext.Authentication.GetExternalLoginInfoAsync();

loginInfo is always null if I haven't included the following properties:

BackchannelHttpHandler = new FacebookBackChannelHandler(),
UserInformationEndpoint = "https://graph.facebook.com/v2.8/me?fields=id,name,email,first_name,last_name"

@Tratcher
Copy link
Member Author

Did you try the new Fields API shown above? #38 (comment)

@Tratcher Tratcher closed this as completed Sep 3, 2020
@ghost ghost locked as resolved and limited conversation to collaborators Oct 3, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests