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.

Community
  • 1
  • 1
Searock
  • 5,488
  • 8
  • 58
  • 95
  • 2
    OH... it's funny that you're getting ... because this is the error I'm currently getting for some weird reason. thanks for the link to my blog! :) – Ian Quigley Aug 31 '11 at 08:27
  • Has the user granted your application the `xmpp_login` [extended permission](https://developers.facebook.com/docs/reference/api/permissions/)? – Casey Chu Sep 03 '11 at 22:01
  • 1
    Also, are you sure your hardcoded access token isn't expired? – Casey Chu Sep 03 '11 at 22:17
  • @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. – Searock Sep 03 '11 at 22:20
  • 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. – Casey Chu Sep 03 '11 at 22:45
  • @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 : ) – Searock Sep 03 '11 at 22:57

5 Answers5

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.

Alexcode
  • 1,598
  • 7
  • 15
  • Do I have to use Legacy Connect Auth instead of OAuth 2.0 authentication. – Searock Sep 08 '11 at 13:03
  • 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). – Alexcode Sep 08 '11 at 14:52
  • 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. – Searock Sep 08 '11 at 14:57
  • Exactly, I'm doing it that way. – Alexcode Sep 08 '11 at 15:00
  • 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. http://developers.facebook.com/roadmap/#migrations – Alexcode Sep 08 '11 at 15:01
  • 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? – Searock Sep 08 '11 at 15:04
  • Do I need to do App Login too? – Searock Sep 08 '11 at 15:06
  • No, you are acting on behalf of the user so you need a user access_token with the 'xmpp_login' permission. http://www.facebook.com/dialog/oauth/?scope=xmpp_login&client_id=YOUR_APP_ID&redirect_uri=REDIRECT_URL&response_type=token – Alexcode Sep 08 '11 at 16:56
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/3293/discussion-between-alexandre-couturon-and-searock) – Alexcode Sep 09 '11 at 10:48
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: <>'"&

Joe Hildebrand
  • 9,616
  • 1
  • 32
  • 45
  • Thanks for your suggestions, I will suerly use some XML parser. – Searock Sep 02 '11 at 16:22
  • What do you mean by test input by this characters `<>'"&`, should I check if any of my responses to the server contains this characters? – Searock Sep 03 '11 at 20:51
  • 1
    I mean that unless you're using an XML DOM, you're likely not to escape them properly as `<>'"&` in all of the places you need to. – Joe Hildebrand Sep 05 '11 at 03:06
5

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

Igy
  • 43,312
  • 8
  • 86
  • 115
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.

Community
  • 1
  • 1
Adrian
  • 722
  • 2
  • 11
  • 34
  • 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 http://developers.facebook.com/docs/chat/ – Searock Sep 10 '11 at 04:19
  • @Searock 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. – Adrian Sep 10 '11 at 04:31
  • 4
    @Adrian The support for OAuth2.0 on X-FACEBOOK-PLATFORM was just added yesterday: http://developers.facebook.com/blog/post/555/ Session_key and sig are replaced by the access_token. – Alexcode Sep 10 '11 at 13:32
  • Yeah, you are right. Thanks for point me that. I'm going to try to get that it works. – Adrian Sep 10 '11 at 13:57
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));
TheCodeKing
  • 18,516
  • 2
  • 45
  • 69