Integrating Facebook chat
Asked Answered
G

5

16

I have written a program to integrate Facebook user chat in C#, however I always get <failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><not-authorized/></failure> after sending the response to the server.

I've checked the API key and the App secret, both of them are correct. It looks like I'm passing some wrong parameters to the server.

Here is my code.

private void GetDetailsButton_Click(object sender, EventArgs e)
{
     TcpClient FacebookClient = new TcpClient();
     FacebookClient.Connect("chat.facebook.com", 5222);
     NetworkStream myns = FacebookClient.GetStream();

     string xml = "<?xml version='1.0'?>" +
     "<stream:stream " +
     "id='1' " +
     "to='chat.facebook.com' " +
     "xmlns='jabber:client' " +
     "xmlns:stream='http://etherx.jabber.org/streams' " +
     "version='1.0' >";

     StreamWriter mySw = new StreamWriter(myns);
     mySw.WriteLine(xml);  //sending initial request
     mySw.Flush();

     byte[] serverResponseByte = new byte[1024];
     int myBytesRead = 0;
     StringBuilder myResponseAsSB = new StringBuilder();

     //reading response from the server to see the supported authentication methods 
     do
     {
            myBytesRead = myns.Read(serverResponseByte, 0, serverResponseByte.Length);
            myResponseAsSB.Append(System.Text.Encoding.UTF8.GetString(serverResponseByte, 0, myBytesRead));

     } while (myns.DataAvailable);


     myResponseAsSB.Clear();

     xml = "<auth " +
     "xmlns='urn:ietf:params:xml:ns:xmpp-sasl' " +
     "mechanism='X-FACEBOOK-PLATFORM'  />";

     mySw.WriteLine(xml);
     mySw.Flush();   //sending response to server to use X-FACEBOOK-PLATFORM


     //reading challenge send by the server
     do
     {
          myBytesRead = myns.Read(serverResponseByte, 0, serverResponseByte.Length);
          myResponseAsSB.Append(System.Text.Encoding.UTF8.GetString(serverResponseByte, 0, myBytesRead));

     } while (myns.DataAvailable);


     myResponseAsSB.Replace("<challenge xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">", "");
     myResponseAsSB.Replace("</challenge>", "");

     //converting challenge string to normal string
     byte[] myregularstrigbytes = Convert.FromBase64String(myResponseAsSB.ToString());
     string myregularstring = System.Text.Encoding.UTF8.GetString(myregularstrigbytes);


     //I've hardcoded the accesstoken here for testing purpose. 
     string SessionKey = AccessToken.Split('|')[1]; 

     string response = ComposeResponse(myregularstring);

     byte[] myResponseByte = Encoding.UTF8.GetBytes(response.ToString());

     string myEncodedResponseToSend = Convert.ToBase64String(myResponseByte);
     xml = String.Format("<response xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">{0}</response>", myEncodedResponseToSend);
     mySw.WriteLine(xml);
     mySw.Flush();   //sending the response to the server with my parameters

     myResponseAsSB.Clear();

     //checking if authentication succeed 
     do
     {
          myBytesRead = myns.Read(serverResponseByte, 0, serverResponseByte.Length);
          myResponseAsSB.Append(System.Text.Encoding.UTF8.GetString(serverResponseByte, 0, myBytesRead));

     } while (myns.DataAvailable);

     MessageBox.Show(myResponseAsSB.ToString());

}

    private string ComposeResponse(string serverresponse)
    {
         string version = serverresponse.Split('&')[0].Split('=')[1];
         string method = serverresponse.Split('&')[1].Split('=')[1];
         string nonce = serverresponse.Split('&')[2].Split('=')[1];
         string SessionKey = AccessToken.Split('|')[1];

         long callId = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;

         string sig = "api_key=" + appId
         + "call_id=" + callId
         + "method=" + method
         + "nonce=" + nonce
         + "session_key=" + SessionKey
         + "v=" + "1.0"
         + AppSecret;

         MD5 md = MD5.Create();
         var hash = md.ComputeHash(Encoding.UTF8.GetBytes(sig));

         sig = hash.Aggregate("", (current, b) => current + b.ToString("x2"));

         return "api_key=" + HttpUtility.UrlEncode(appId)
         + "&call_id=" + HttpUtility.UrlEncode(callId)
         + "&method=" + HttpUtility.UrlEncode(method)
         + "&nonce=" + HttpUtility.UrlEncode(nonce)
         + "&session_key=" + HttpUtility.UrlEncode(SessionKey)
         + "&v=" + HttpUtility.UrlEncode("1.0")
         + "&sig=" + HttpUtility.UrlEncode(sig);

    }

I've refereed to this articles Facebook Chat Authentication in C# and X-FACEBOOK-PLATFORM and my application type is of Native/Desktop.

Can some point me in the right direction?

Edit : I think the problem is while creating the signature, is there any way to verify the created signature?

Edit 1 : According to this SO answer the access token contains the session key after the first | character and I could find the | character till 2 days ago, but now I can't find the | character in the access token, its really strange, so how do I find the session key now? (Or may be I should go to sleep now.)

Edit 2 : Its strange that I always got the access token in form of <appId>|<sessionKey>|<digest> for native/desktop application. I did further searching and found out that the session key needs to be extracted from auth.promoteSession legacy api and encode the parameters using HttpUtility.UrlEncode instead of HttpUtility.HtmlEncode.

Now I've hard coded the Access token (verified it in the Access Token Debugger), the session key, App key and app secret still I get the same error <failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><not-authorized/></failure>

Edit 3 : I have been banging my head over a week and still this doesn't work, but today I found a update in the documentation which says Note that this needs to be over TLS (Transport Layer Security) or you'll get an error. I guess I need to modify my code accordingly.

Edit 4 : I've tried out the code in the documentation and found that the value of $SESSION_XML should be

$SESSION_XML = '<iq type="set" id="4">'.
  '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>';

I will post the C# code once I finish converting it.

Giarla answered 28/8, 2011 at 14:40 Comment(6)
OH... it's funny that you're getting <not-authorized/> ... because this is the error I'm currently getting for some weird reason. thanks for the link to my blog! :)Isolde
Has the user granted your application the xmpp_login extended permission?Hyposthenia
Also, are you sure your hardcoded access token isn't expired?Hyposthenia
@Casey Hope I'm using my own account and I've granted the permission and I've even ensured that the access token has not expired, but still it doesn't work.Giarla
Some other shots in the dark -- what happens if you take out the HtmlEncode calls? The parameters you are hashing should be exactly the same as the ones you passed in. Also, what happens if you don't include the call_id parameter? When I was writing similar code a year ago, I did not include that parameter.Hyposthenia
@Casey Hope Initially I did not use HtmlEncode but the documentation says that it should be Url encoded, well still it does not work, and I'm passing the call_id because it's mentioned in the documentation, let's see what happens if I don't pass the call_id : )Giarla
M
13

To use X-FACEBOOK-PLATFORM you will need the user session which is provided with the legacy auth flow. However, the access_token contain the user session after the | as you noted in your edit1.

We announced in the last blog post that access_token will be encrypted and this will be mandatory from Octber 1st. Until then, the option can be toggle in the Advanced App settings http://developers.facebook.com/blog/post/553/.

Moving forward, access_token will be able to be use for X-FACEBOOK-PLATFORM.

Mccarthyism answered 6/9, 2011 at 18:11 Comment(9)
Do I have to use Legacy Connect Auth instead of OAuth 2.0 authentication.Giarla
It is the easiest way to go until we support access_token on X-FACEBOOK-PLATFORM which should happen soon enough. You'll have to do some little changes to replace the session_key by the access_token. Personally, I'd rather doing it the hacky way by extracting the session_key from the access_token, like that the changes will be minimum when we will enable OAuth2.0 (disable the access_token encryption in the App settings until then).Mccarthyism
So this means that I don't have to use Legacy Connect Auth and I just need to extract the session key from the access token right? and I have disabled the access_token encryption.Giarla
Exactly, I'm doing it that way.Mccarthyism
When we will support the OAuth2.0 for X-FACEBOOK-PLATFORM, you will just need to pass the full access_token which will be encrypted from October 1st. developers.facebook.com/roadmap/#migrationsMccarthyism
Oh, thanks for the update. I'm logging into the user using OAuth, then extracting the session key from the access token and then send all the parameters like nonce, sig, call_id, session_key still I get authentication failed error? Am I missing something?Giarla
Do I need to do App Login too?Giarla
No, you are acting on behalf of the user so you need a user access_token with the 'xmpp_login' permission. facebook.com/dialog/oauth/…Mccarthyism
let us continue this discussion in chatMccarthyism
S
7

You'll get farther faster if you start with an existing XMPP library. Here is a list: http://xmpp.org/xmpp-software/libraries/

For example, you're going to wish you hadn't hand-coded all of your XML instead of running it through the DOM very soon. In the meantime, test all of your inputs with these characters: <>'"&

Soffit answered 29/8, 2011 at 8:8 Comment(3)
Thanks for your suggestions, I will suerly use some XML parser.Giarla
What do you mean by test input by this characters <>'"&, should I check if any of my responses to the server contains this characters?Giarla
I mean that unless you're using an XML DOM, you're likely not to escape them properly as &lt;&gt;&apos;&quot;&amp; in all of the places you need to.Soffit
O
5

There's an example in Python on the Facebook Developer site which may be of use: https://developers.facebook.com/docs/chat/

Ohmmeter answered 29/8, 2011 at 6:12 Comment(3)
I had seen the python code but couldn't understand it, I guess that's my only solution, I'll look at it again.Giarla
where do I find pyxmpp file, is there any specific one on the net?Giarla
www.google.com/search?q=pyxmpp - first result might be it: pyxmpp.jajcus.netOhmmeter
W
1

I had the same issue before trying to authenticate using the X-FACEBOOK-PLATFORM. I found a solution that I think can help you.

XMPP with Java Asmack library supporting X-FACEBOOK-PLATFORM

Althought the code is Java, is easy to understand how it works. The trick is to get the session secret key from Facebook using your OAuth 2.0 token. You have to use a deprecated method called auth.promoteSession in this way:

https://api.facebook.com/method/auth.promoteSession?access_token=yourAccessToken

Then, Facebook gives you the session secret key that you should use as the application secret key parameter. To be able to use this method you have to disable the Remove Deprecated APIs parameter in this page in the edit settings section and the advance tab.

I hope this helps you.

Witenagemot answered 10/9, 2011 at 3:56 Comment(4)
Thanks for the reply. I have already tried it out, but it did not work for me for some reason. Now facebook supports encrypted access token and you don't need to pass the signature any more. Check out the documentation developers.facebook.com/docs/chatGiarla
@Giarla When I was suffering this problem I was checking out that page. For me, this didn't start to work until I used the auth.promoteSession method and used the session secret as the application secret key required. But, if you are sure that you have tried that, then I don't know why it doesn't work.Witenagemot
@Witenagemot The support for OAuth2.0 on X-FACEBOOK-PLATFORM was just added yesterday: developers.facebook.com/blog/post/555 Session_key and sig are replaced by the access_token.Mccarthyism
Yeah, you are right. Thanks for point me that. I'm going to try to get that it works.Witenagemot
G
1

No idea if this is the problem and I don't have time to test, but your sig looks wrong to me. Also bare in mind Facebook moved to OAuth 2.0 at the beginning of Sept, so make sure your access_token is correct.

Try this:

string sig = "api_key=" + appId
     + "call_id=" + callId
     + "method=" + method
     + "nonce=" + nonce
     + "session_key=" + SessionKey
     + "v=1.0"
     + APP_SECRET;

MD5 md = MD5.Create();
var hash = md.ComputeHash(Encoding.UTF8.GetBytes(sig));
sig = hash.Aggregate("", (current, b) => current + b.ToString("x2"));

var response = "api_key=" + appId
     + "&call_id=" + callId
     + "&method=" + method
     + "&nonce=" + nonce
     + "&session_key=" + SessionKey
     + "&v=1.0";

 // response & signature
 response = string.Concat(Uri.EscapeDataString(response),
                         "&", Uri.EscapeDataString(sig))

 // base64 encode
 var myEncodedResponseToSend = Convert.ToBase64String(
                                        ASCIIEncoding.ASCII.GetBytes(response));
Grum answered 10/9, 2011 at 21:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.