Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DotNetOpenAuth CTP - Facebook bad request

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:

  1. Planned release date of the DotNetOpenAuth with OAuth 2.0
  2. Whether .NET 4.0 will be a pre-requisite

Anyway, any suggestions would be most welcome.

like image 537
peteisace Avatar asked Sep 16 '10 08:09

peteisace


2 Answers

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);
        }
    }


}
like image 137
Iain Avatar answered Nov 01 '22 17:11

Iain


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!

like image 30
Antonin Jelinek Avatar answered Nov 01 '22 17:11

Antonin Jelinek