Facebook OAuth stopped working suddenly - facebook

I noticed yesterday that my Facebook login for my website has stopped working.
This has been working great for the last 2 months, as far as I am aware I have not changed anything. I have tried everything I can on links such as: - as well as many more...
ASP.NET MVC5 OWIN Facebook authentication suddenly not working
I have noticed that the Stack Overflow Facebook auth has also stopped working.
Has anyone else noticed this and found any solution? It's worth noting I am using azure app services to host. But this issue is also found when I am using localhost.
My current setup looks like this...
in Startup.Auth.cs
var facebookOptions = new Microsoft.Owin.Security.Facebook.FacebookAuthenticationOptions()
{
AppId = "xxxxxxxxxxxxx",
AppSecret = "xxxxxxxxxxxx"
};
facebookOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookOptions);
In the following method, loginInfo is null every time.
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
I also added a session "WAKEUP" from a different post suggestion, fb auth failed once before and this fixed the issue this time, but it has come back.
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult ExternalLogin(string provider, string returnUrl)
{
Session["WAKEUP"] = "NOW!";
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}

As RockSheep explained. Facebook dropped the support vor API v2.2. You need to update your OWIN nuget packages.
You can find the issue on github (from the Katanaproject).
Ensure to activate pre releases in your nuget manager, than you are able to update the nuget packages to version v3.1.0-rc1. But beware: After the update, you need to test your login carefully (maybe you also have other authentication providers like Microsoft or Google, you should test them as well).
Technical
The Api changed the version number to v2.8 and the return value from the API is now in JSON-Format and no longer escaped in the URI. The 'old' OWIN packages can not handle this changes.
[Oauth Access Token] Format - The response format of
https://www.facebook.com/v2.3/oauth/access_token returned when you
exchange a code for an access_token now return valid JSON instead of
being URL encoded. The new format of this response is {"access_token":
{TOKEN}, "token_type":{TYPE}, "expires_in":{TIME}}. We made this
update to be compliant with section 5.1 of RFC 6749.
Here you can find the code-changes on GitHub for further informations and better understanding.

A lot of people started having trouble after yesterday. This is due to Facebook dropping support for v2.2 of their API. For some reason their system still redirects auth calls that don't use a version number to the 2.2 API. A quickfix is to ensure that the API version gets sent with the API call.
Starting at v2.3 Facebook also started returning JSON objects. So make sure to change that in the code as well.

I had the same issue, found solution here Fix facebook oauth 2017
Basically, you need to extend HttpClientHandler and decode JSON response instead of body

Here is a solution for those who are using scribe java.
public Token extract(String response)
{
Preconditions.checkEmptyString(response, "Response body is incorrect. Can't extract a token from an empty string");
JSONObject obj = new JSONObject(response);
return new Token(obj.get("access_token").toString(), EMPTY_SECRET, response);
}

Create a new class and set the extractor to JSON.
import org.scribe.builder.api.DefaultApi20;
import org.scribe.extractors.AccessTokenExtractor;
import org.scribe.extractors.JsonTokenExtractor;
import org.scribe.model.OAuthConfig;
public class FaceFmApi extends DefaultApi20 {
#Override
public String getAccessTokenEndpoint()
{
return "https://graph.facebook.com/oauth/access_token";
}
#Override
public AccessTokenExtractor getAccessTokenExtractor()
{
return new JsonTokenExtractor();
}
#Override
public String getAuthorizationUrl(OAuthConfig config) {
return null;
}
}
and inject your newly created class as below. Then getAccessToken() will work as expected.
public OAuthService getService() {
return new ServiceBuilder().provider(FaceFmApi.class)
.apiKey(config.getApiKey()).apiSecret(config.getApiSecret())
.callback(config.getCallback()).build();
}

Related

Spring Security Authorize request from native Postman app

I am exploring/learning Spring security modules by implementing it through REST API.
To test the impact, we are using Postman native application as a rest client.
#RestController
#RequestMapping("/auth")
public class Employee {
#GetMapping("/status")
public ResponseEntity<String> getStatus()
{
ResponseEntity<String> responseEntity = new ResponseEntity<>("Resource is fetched", HttpStatus.OK);
return responseEntity;
}
}
above is a piece of resource for sake of consumption.
and below is the code snippet to configure Authentication and authorization
#EnableWebSecurity
public class AppSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("ashish").password("{noop}admin").roles("USER")
.and().withUser("foo").password("{noop}foo").roles("ADMIN");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/auth/status").hasRole("ADMIN").and()
.formLogin()
;
}
#Bean
public PasswordEncoder getPasswordEncoder()
{
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
now above authorization code is working fine when tried in browser - it uses its default spring login page.
however i am not quite able to understand how to execute/test the same through postman.
in method protected void configure(HttpSecurity http) , i tried removing formLogin() it did not work.
i added httpBasic - it also did not worked.
in postman, basic authentication is used by me.
while searching on internet, i came across some really good articles but almost all of them uses some sort of UI technology like angular or thymleaf to demonstrate the concept which i am finding hard to grasp.
I am referring below video tutorials to learn spring security.
https://www.youtube.com/watch?v=payxWrmF_0k&list=PLqq-6Pq4lTTYTEooakHchTGglSvkZAjnE&index=6&t=0s
Thanks in advance!
Ashish Parab
Do a GET request http://localhost:8080/login via postman and it will return you an html. Extract the _csrf token from the response. It will look like
<input name="_csrf" type="hidden"
value="1c470a6c-dff3-43aa-9d08-d308545dc880" />
Do a POST request as follows to http://localhost:8080/login, copying the _csrf token, username and password as form params
Take note of the JESSIONID Cookie value in the response from step two. And that is the session Id of the authenticated session.
As long as you sent the JESSIONID in subsequent requests as a cookie, spring security knows who you are. Postman will add that Cookie automatically to subsequent requests.
you can add it manually as header with that cookie header or update the postman settings to always send JESSIONID cookie
You will have to implement JWT token for the same and add it to the request header 'Authorization' in Postman. You can take a look at Java Brains Spring security videos on youtube.

Facebook OAuth LoginInfo is null on callback

I am building a test application and testing out externallogins via facebook.
I am using fiddler and when I click the button to login via facebook I am getting code 200 results (but they are locked due to HTTPS), but I am receiving a null value for loginInfo.
Here specifically:
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync(); // this is null
if (loginInfo == null)
{
return RedirectToAction("Login");
}
I have reset my secret key and that does not work. I have the most updated version of Microsoft.AspNet.Identity.Owin (2.2.1).
What is the deal with this?
Any help is appreciated.
Assuming you created a new Facebook App as well, it'll work on the latest Facebook Api versions. Default templates don't seem to work out of the box anymore, due to Facebook's changing Api. They've reduced the number of fields that are returned by default and all additional fields now have to be specifically asked for.
Have a look at the following post, which contain 2 possible workarounds for this.
Why new fb api 2.4 returns null email on MVC 5 with Identity and oauth 2?

Return 401 in a Web API Facebook Login

community, I was following an example of how to make a service that offers Facebook login on my web api but I can not make it work.
The link for the example. I did try the another example and still not working.
Well, in my AccountController I have the method GetExternalLogin and in the line:
if (!User.Identity.IsAuthenticated)
{
return new ChallengeResult(provider, this);
}
The method return the error 401. I don't work with OWIN before, but I want in the method call the Facebook Login API. And this don't call the Facebook login page, just return 401.
I copied all the sample code and not worked. What should I do?
The code in the ChallengeResult:
public class ChallengeResult : IHttpActionResult
{
public string LoginProvider { get; set; }
public HttpRequestMessage Request { get; set; }
public ChallengeResult(string loginProvider, ApiController controller)
{
LoginProvider = loginProvider;
Request = controller.Request;
}
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
Request.GetOwinContext().Authentication.Challenge(LoginProvider);
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.Unauthorized);
response.RequestMessage = Request;
return Task.FromResult(response);
}
}
I don't know any thing about OWIN, sorry. I will learn
Returning 401 (Unauthorized) is correct. This is what the External Login provider (Facebook in your case) use to know that have to display the login page.
As I see, you are already following a tutorial, but maybe this one can help you to understand the authentication and authorization process with external providers. This tutorial explains how to authorize with Google and Facebook, but in your case you can skip the Google parts.
I hope this helps.
Hit the same problem, burned the same neurons. After losing enough brain mass, I found the cause in my case: In the query string, I have written Facebook with a small f. When I changed it to a capital F, it started working.
Hope this helps.

Log out from facebook

Well i developing a Flex desktop app and i cant logout form facebook. I mean after loggin in and updating the photo i want to update, i run the method to log out, which looks like this
FacebookDesktop.logout(handleLogout);
Where handleLogout is a function where i can do other things.
The method runs but never log out. I think that maybe loading an other request i could log out, and i find that using:
"https://www.facebook.com/logout.php?" + info.get_accessToken() +
"&next=http://www.Google.com"
would log out, but i dont know where i ca get the accesToken.
Thanks in advance!
The following code is implemented in for asp.net page using C# code.
EXPLANATION
First you need to send a request to authenticate the user(the IF part). You will get a "CODE" on successfull authentication. Then send a request with this code to authorize the application. On successful authorization you will get the access token as response.
protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString["code"] != null)
{
Response.Redirect("https://graph.facebook.com/oauth/access_token?client_id=CLIENT_ID&redirect_uri=CURRENT_URL&client_secret=APP_SECRET&code="+Request.QueryString["code"]);
}
else
{
Response.Redirect("https://www.facebook.com/dialog/oauth?client_id=CLIENT_ID&redirect_uri=CURRENT_URL&scope=read_stream");
}
}
HERE IS THE PROCEDURE
Create an asp.net website
In the default.aspx page implement the above code.
Replace CLIENT_ID,APP_SECRET with the AppId and AppSecret respectively
CURRENT_URL should be the url of the page in which you are implementing the code.
The part "&scope=read_stream" is not mandatory. If you need any additional permissions please enter it here as comma separated values.
You will get a string in the format
access_token=ACCESS_TOKEN_VALUE&expires=EXPIRY_TIME
as response.
Try this to send a POST request using flex
var urlLoader:URLLoader = new URLLoader();
var request:URLRequest = new URLRequest("https://www.facebook.com/logout.php?next=YOUR_URL&access_token=ACCESS_TOKEN");
request.data = binaryData;
request.method = URLRequestMethod.POST
urlLoader.load(request);

Best practice for dual-use iFrame + External authentication for Facebook enabled app

Okay, if cookies are a no-no, then I need a little guidance as to the best way to implement the application(s) that I'm creating.
The scenario is that I'm trying to create a single Asp.Net MVC application that can authenticate a user regardless of whether the user visits a site directly or via an iFrame in Facebook. There are separate actions (in separate controllers, actually) for getting INTO the app depending on whether the user enters via Facebook or not, but there are also places in the Facebook app where I'm opening up a new window to "extended" functionality in other areas of the application that can't really work well within the iFrame. It is supposed to transition seamlessly. It's currently working quite well using cookies, but I've from multiple sources that this is not a good thing for iFrame apps. However, I'm not sure exactly what this means.
Without cookies, can you still somehow get server-side access to the authentication token? If not, then what is the "right" way to handle this. Do I need to resort to manually parsing the token using the JS API and sending an AJAX notification to the server of the fact that the user is authenticated and create a forms auth token? Will the CanvasAuthorize attribute work without cookies? Right now I have added code to the FormsAuthentication_OnAuthenticate event in Global.asax to create the forms auth token if the user is logged in via Facebook (and properly associated with a valid user in the external app) as follows:
protected void FormsAuthentication_OnAuthenticate(Object sender, FormsAuthenticationEventArgs args)
{
if (FormsAuthentication.CookiesSupported)
{
if (Request.Cookies[FormsAuthentication.FormsCookieName] == null)
{
// Attempt to authenticate using Facebook
try
{
FacebookApp fbApp = new FacebookApp();
if (fbApp.Session != null)
{
dynamic me = fbApp.Get("me");
String fbID = "" + me.id;
MembershipUser mUser = AppMembershipProvider.GetUserByFacebookID(fbID);
if (mUser != null)
{
FormsAuthentication.SetAuthCookie(mUser.UserName, false);
AppMembershipProvider.UpdateLastLogin(mUser.UserName);
Session["FacebookLogin"] = true;
}
}
}
catch (Exception e)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(e);
}
}
}
else
{
throw new HttpException("Cookieless Forms Authentication is not " +
"supported for this application.");
}
}
Will I need to change this?
Sorry if this is basic knowledge, but I'm confused as to how best to implement this. Thanks!
First, let me address the issue with the cookies. So, when I say to not use cookies in iFrames I am saying that for a couple reasons. First in IE, there are some security issues. You need to add the following header to your app to make cookies work correctly inside iframes:
P3P: CP="CAO PSA OUR"
The second big issue with cookies in iframe apps is Safari. Due to security settings in Safari, cookies cannot be created by iframes. As such, you will not be able to rely on cookies for authentication inside of iframes.
Give that you are using the app inside and outside of the iframe, you should have cookie support turned on. However, your app must be designed in a way that will work around the iframe issues. That is going to be the hard part.
The most reliable authentication inside iframe apps is the signed request method. What happens is facebook will append a query parameter to your url when the url is rendered inside the iframe. This query parameter contains the user's session. The Facebook C# SDK handles reading this for you, so you dont need to parse it etc. But you need to be aware that it is there. If you view the incoming request url of your iframe app in facebook you will see something like http://www.mysite.com/page/?signed_request={blahblahblah}.
So the key is that you need to make sure that if you are in the iframe you keep that ?signed_request value on the url.
You can do this several ways. First, you can use the CanvasRedirect methods. These are extension methods on System.Web.Mvc.Controller in the Facebook.Web.Mvc namespace. The canvas redirect uses javascript to redirect your page in the top url. This way Facebook is actually handling the redirects and will always add the signed_request to your iframe url. The problem for you is that this method of redirecting will only work in the iframe, not outside.
The second way would be to manually add the ?signed_request to the url when you redirect. You would do something like:
public ActionResult Something() {
return RedirectToAction("something", new { signed_request = Request.Querystring["signed_requets"]);
}
There are other ways also, like storing data in the session or something, but I wouldn't recommend going down that path.
What you are doing is definitely an advanced senario, but hopefully the above will help you get going in the right direction. Feel free to contact me directly if you have any questions. nathan#ntotten.com or #ntotten on twitter.
I am in a similar situation to you. What I do to handle the various situations that can arise is:
Enable cookies in both the C# and
JavaScript SDK.
Create a custom actionfilter that
inherits from
FacebookAuthorizeAttribute and
overrides the
HandleUnauthorizedRequest method to
redirect to either a connect
authorization page or an action
decorated with the
CanvasAuthorizeAttribute.
Pass either the signed_request
(canvas app) or auth_token (connect
app) as a querystring parameter to
everything.
Check for null sessions and oauth
tokens that don't match what has been
passed in the querystring.
The main point is to ensure that both the session and oauth tokens are valid. When inside Facebook the signed_request will ensure this is true. By passing the token from your connect auth page you can ensure you have a valid token to inject into the FacebookApp constructor.
public class FbAuthenticateAttribute : FacebookAuthorizeAttribute
{
private FacebookApp _fbApp;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
var accessToken = filterContext.HttpContext.Request.Params["access_token"];
if (FacebookApp.AccessToken != accessToken && !string.IsNullOrEmpty(accessToken))
{
_fbApp = new FacebookApp(accessToken);
}
else
{
_fbApp = FacebookApp;
}
filterContext.Controller.ViewBag.Context = GetContext().ToString();
filterContext.RequestContext.HttpContext.Response.AppendHeader("p3p", "CP=\"CAO PSA OUR\"");
try
{
dynamic user = _fbApp.Get("me");
var signedRequest = filterContext.HttpContext.Request.Params["signed_request"];
filterContext.Controller.ViewBag.QueryString = string.IsNullOrEmpty(signedRequest)
? "?access_token=" + _fbApp.AccessToken
: "?signed_request=" + signedRequest;
}
catch (Exception ex)
{
string url = GetRedirectUrl(filterContext);
filterContext.Result = new RedirectResult(url);
}
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
string url = GetRedirectUrl(filterContext);
filterContext.Result = new RedirectResult(url);
}
private string GetRedirectUrl(ControllerContext filterContext)
{
return new UrlHelper(filterContext.RequestContext).Action(GetRedirectAction(GetContext()), "Authentication");
}
private Enums.AppContext GetContext()
{
//Note: can't rely on this alone - find something more robust
return FacebookApp.SignedRequest == null ? Enums.AppContext.FBWeb : Enums.AppContext.FBApp;
}
private string GetRedirectAction(Enums.AppContext context)
{
return context == Enums.AppContext.FBWeb ? "ConnectAuthenticate" : "Authenticate";
}
}
It could definitely do with a refactor and still has problems but is the best solution I have found so far.