I am trying to use the CTP to connect with Facebook over OAuth 2.0.
I can get the initial request to Facebook working OK, but when it comes back and we call:
// Where null will become an HttpRequestInfo object
client.ProcessUserAuthorization(null);
I get:
The remote server returned an error: (400) Bad Request.
I haven't really done much with the initial codebase; merely set the optional values to null (we're still on .NET 3.5). Any clues would be much appreciated.
Also, and i guess this is more of a question to Andrew specifically; is there a forum / blog for any of this stuff, or anywhere that will give regular updates? It would be great to know a few things:
Anyway, any suggestions would be most welcome.
After hitting this issue, I wrote my own code to authorize, and get the users details. Another approach would be to use Facebook C# SDK. As a starter for anyone else thinking about doing there own, here is how I did it. Please note I have not looked into error cases.
Firstly, read facebooks doc on how it works (its rather simple!)
I consume it like this:
private static readonly FacebookClient facebookClient = new FacebookClient();
public ActionResult LoginWithFacebook()
{
var result = facebookClient.Authorize();
if (result == FacebookAuthorisationResult.RequestingCode)
{
//The client will have already done a Response.Redirect
return View();
} else if (result == FacebookAuthorisationResult.Authorized)
{
var user = facebookClient.GetCurrentUser();
}
return Redirect("/");
}
And the client code:
using System;
using System.IO;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Web;
namespace Web.Services
{
public enum FacebookAuthorisationResult
{
Denied,
Authorized,
RequestingCode
}
public class FacebookClient
{
private const String SESSION_NAME_TOKEN = "UserFacebookToken";
public FacebookClient()
{
TokenEndpoint = new Uri("https://graph.facebook.com/oauth/access_token");
AuthorizationEndpoint = new Uri("https://graph.facebook.com/oauth/authorize");
MeGraphEndpoint = new Uri("https://graph.facebook.com/me");
ClientIdentifier = "xxxxxxxxxxxxxxxxxx";
Secret = "xxxxxxxxxxxx";
LocalSubDomain = "local.xxxxxxx.com";
}
public Uri TokenEndpoint { get; set; }
public Uri AuthorizationEndpoint { get; set; }
public Uri MeGraphEndpoint { get; set; }
public String Secret { get; set; }
public String ClientIdentifier { get; set; }
private String LocalSubDomain { get; set; }
public FacebookAuthorisationResult Authorize()
{
var errorReason = HttpContext.Current.Request.Params["error_reason"];
var userDenied = errorReason != null;
if (userDenied)
return FacebookAuthorisationResult.Denied;
var verificationCode = HttpContext.Current.Request.Params["code"];
var redirectUrl = GetResponseUrl(HttpContext.Current.Request.Url);
var needToGetVerificationCode = verificationCode == null;
if (needToGetVerificationCode)
{
var url = AuthorizationEndpoint + "?" +
"client_id=" + ClientIdentifier + "&" +
"redirect_uri=" + redirectUrl;
HttpContext.Current.Response.Redirect(url);
return FacebookAuthorisationResult.RequestingCode;
}
var token = ExchangeCodeForToken(verificationCode, redirectUrl);
HttpContext.Current.Session[SESSION_NAME_TOKEN] = token;
return FacebookAuthorisationResult.Authorized;
}
public Boolean IsCurrentUserAuthorized()
{
return HttpContext.Current.Session[SESSION_NAME_TOKEN] != null;
}
public FacebookGraph GetCurrentUser()
{
var token = HttpContext.Current.Session[SESSION_NAME_TOKEN];
if (token == null)
return null;
var url = MeGraphEndpoint + "?" +
"access_token=" + token;
var request = WebRequest.CreateDefault(new Uri(url));
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
using (var responseReader = new StreamReader(responseStream))
{
var responseText = responseReader.ReadToEnd();
var user = FacebookGraph.Deserialize(responseText);
return user;
}
}
}
}
private String ExchangeCodeForToken(String code, Uri redirectUrl)
{
var url = TokenEndpoint + "?" +
"client_id=" + ClientIdentifier + "&" +
"redirect_uri=" + redirectUrl + "&" +
"client_secret=" + Secret + "&" +
"code=" + code;
var request = WebRequest.CreateDefault(new Uri(url));
using (var response = request.GetResponse())
{
using (var responseStream = response.GetResponseStream())
{
using (var responseReader = new StreamReader(responseStream))
{
var responseText = responseReader.ReadToEnd();
var token = responseText.Replace("access_token=", "");
return token;
}
}
}
}
private Uri GetResponseUrl(Uri url)
{
var urlAsString = url.ToString();
var doesUrlContainQuestionMark = urlAsString.Contains("?");
if (doesUrlContainQuestionMark)
{
// Remove any parameters. Apparently Facebook does not support state: http://forum.developers.facebook.net/viewtopic.php?pid=255231
// If you do not do this, you will get 'Error validating verification code'
urlAsString = urlAsString.Substring(0, urlAsString.IndexOf("?"));
}
var replaceLocalhostWithSubdomain = url.Host == "localhost";
if (!replaceLocalhostWithSubdomain)
return new Uri(urlAsString);
// Facebook does not like localhost, you can only use the configured url. To get around this, log into facebook
// and set your Site Domain setting, ie happycow.com.
// Next edit C:\Windows\System32\drivers\etc\hosts, adding the line:
// 127.0.0.1 local.happycow.cow
// And lastly, set LocalSubDomain to local.happycow.cow
urlAsString = urlAsString.Replace("localhost", LocalSubDomain);
return new Uri(urlAsString);
}
}
[DataContract]
public class FacebookGraph
{
private static DataContractJsonSerializer jsonSerializer = new DataContractJsonSerializer(typeof(FacebookGraph));
// Note: Changed from int32 to string based on Antonin Jelinek advise of an overflow
[DataMember(Name = "id")]
public string Id { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "first_name")]
public string FirstName { get; set; }
[DataMember(Name = "last_name")]
public string LastName { get; set; }
[DataMember(Name = "link")]
public Uri Link { get; set; }
[DataMember(Name = "birthday")]
public string Birthday { get; set; }
public static FacebookGraph Deserialize(string json)
{
if (String.IsNullOrEmpty(json))
{
throw new ArgumentNullException("json");
}
return Deserialize(new MemoryStream(Encoding.UTF8.GetBytes(json)));
}
public static FacebookGraph Deserialize(Stream jsonStream)
{
if (jsonStream == null)
{
throw new ArgumentNullException("jsonStream");
}
return (FacebookGraph)jsonSerializer.ReadObject(jsonStream);
}
}
}
Iain's solution is finally something I made this thing work with.
There is one note for future implementers - seems like Facebook ID property is now exceeding capacity of Int32 type. You might need to change this in FacebookGraph class, I used plain string.
Thanks Iain, your code really helped me!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With