【问题标题】:Integrating Facebook chat集成 Facebook 聊天
【发布时间】:2011-11-05 12:04:40
【问题描述】:

我用 C# 编写了一个集成 Facebook 用户聊天的程序,但是在将响应发送到服务器后,我总是得到<failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><not-authorized/></failure>

我已经检查了 API 密钥和应用程序密钥,它们都是正确的。看起来我向服务器传递了一些错误的参数。

这是我的代码。

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

    }

我参考了这篇文章Facebook Chat Authentication in C#X-FACEBOOK-PLATFORM,我的应用类型是Native/Desktop。

有人能指出我正确的方向吗?

编辑:我认为问题出在创建签名时,有什么方法可以验证创建的签名吗?

编辑1:根据SO answer,访问令牌包含第一个| 之后的会话密钥。字符,我可以找到 |直到 2 天前的字符,但现在我找不到 |访问令牌中的字符,真的很奇怪,那么我现在如何找到会话密钥? (或者我现在应该去睡觉了。)

编辑2:奇怪的是,我总是以&lt;appId&gt;|&lt;sessionKey&gt;|&lt;digest&gt; 的形式获得本机/桌面应用程序的访问令牌。我做了进一步的搜索,发现会话密钥需要从auth.promoteSessionlegacy api中提取并使用HttpUtility.UrlEncode而不是HttpUtility.HtmlEncode对参数进行编码。

现在我已经硬编码了访问令牌(在Access Token Debugger 中验证它)、会话密钥、应用程序密钥和应用程序密钥我仍然收到相同的错误&lt;failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"&gt;&lt;not-authorized/&gt;&lt;/failure&gt;

编辑 3: 一个多星期以来,我一直在敲我的脑袋,但仍然无法正常工作,但今天我在 documentation 中发现了一个更新,上面写着 Note that this needs to be over TLS (Transport Layer Security) or you'll get an error. 我想我需要相应地修改我的代码。

Edit 4 :我试过文档中的代码,发现$SESSION_XML的值应该是

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

我将在完成转换后发布 C# 代码。

【问题讨论】:

  • OH... 很有趣,你得到了 ... 因为这是我目前因为一些奇怪的原因而遇到的错误。感谢我的博客链接! :)
  • 用户是否授予您的应用程序xmpp_login extended permission
  • 另外,您确定您的硬编码访问令牌没有过期吗?
  • @Casey 希望我使用的是我自己的帐户并且我已经授予了权限,我什至确保访问令牌没有过期,但它仍然不起作用。
  • 其他一些在黑暗中拍摄的照片——如果您拨打HtmlEncode 电话会发生什么?您要散列的参数应该与您传入的参数完全相同。另外,如果您不包含call_id 参数会怎样?一年前我在写类似的代码时,没有包含那个参数。

标签: c# facebook xmpp x-facebook-platform


【解决方案1】:

Facebook 开发者网站上有一个 Python 示例,可能有用: https://developers.facebook.com/docs/chat/

【讨论】:

  • 我看过python代码但看不懂,我想这是我唯一的解决方案,我再看一遍。
  • 在哪里可以找到pyxmpp文件,网上有具体的吗?
  • www.google.com/search?q=pyxmpp - 第一个结果可能是:pyxmpp.jajcus.net
【解决方案2】:

如果您从现有的 XMPP 库开始,您会走得更快。这是一个列表:http://xmpp.org/xmpp-software/libraries/

例如,您会希望自己没有对所有 XML 进行手工编码,而不是很快通过 DOM 非常 运行它。同时,使用以下字符测试您的所有输入:&lt;&gt;'"&amp;

【讨论】:

  • 感谢您的建议,我将非常愿意使用一些 XML 解析器。
  • 这个字符&lt;&gt;'"&amp;的测试输入是什么意思,我应该检查我对服务器的任何响应是否包含这个字符?
  • 我的意思是,除非您使用 XML DOM,否则您可能无法在所有需要的地方正确地将它们转义为 &amp;lt;&amp;gt;&amp;apos;&amp;quot;&amp;amp;
【解决方案3】:

要使用 X-FACEBOOK-PLATFORM,您需要使用旧版身份验证流程提供的用户会话。 但是,access_token 包含 | 之后的用户会话。正如您在edit1中指出的那样。

我们在上一篇博文中宣布 access_token 将被加密,这将从 10 月 1 日开始强制执行。在此之前,可以在高级应用设置http://developers.facebook.com/blog/post/553/ 中切换该选项。

今后,access_token 将能够用于 X-FACEBOOK-PLATFORM。

【讨论】:

  • 我是否必须使用 Legacy Connect Auth 而不是 OAuth 2.0 身份验证。
  • 在我们支持 X-FACEBOOK-PLATFORM 上的 access_token 之前,这是最简单的方法,这应该很快就会发生。您必须做一些小改动才能将 session_key 替换为 access_token。就个人而言,我宁愿通过从 access_token 中提取 session_key 来做这件事,就像我们启用 OAuth2.0 时所做的更改将是最小的一样(在此之前禁用 App 设置中的 access_token 加密)。
  • 所以这意味着我不必使用 Legacy Connect Auth,我只需要从访问令牌中提取会话密钥,对吗?我已经禁用了 access_token 加密。
  • 没错,我就是这样做的。
  • 当我们支持 X-FACEBOOK-PLATFORM 的 OAuth2.0 时,您只需传递将从 10 月 1 日起加密的完整 access_token。 developers.facebook.com/roadmap/#migrations
【解决方案4】:

在尝试使用 X-FACEBOOK-PLATFORM 进行身份验证之前,我遇到了同样的问题。我找到了一个我认为可以帮助你的解决方案。

XMPP with Java Asmack library supporting X-FACEBOOK-PLATFORM

虽然代码是 Java,但很容易理解它是如何工作的。诀窍是使用您的 OAuth 2.0 令牌从 Facebook 获取会话密钥。您必须以这种方式使用一个已弃用的名为 auth.promoteSession 的方法:

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

然后,Facebook 会为您提供会话密钥,您应该将其用作应用程序密钥参数。为了能够使用此方法,您必须在编辑设置部分和高级选项卡中禁用此 page 中的 Remove Deprecated APIs 参数。

希望对你有帮助。

【讨论】:

  • 感谢您的回复。我已经尝试过了,但由于某种原因它对我不起作用。现在 facebook 支持加密访问令牌,您不再需要传递签名。查看文档developers.facebook.com/docs/chat
  • @Searock 当我遇到这个问题时,我正在查看那个页面。对我来说,直到我使用 auth.promoteSession 方法并将会话密钥用作所需的应用程序密钥之后,这才开始起作用。但是,如果你确定你已经尝试过了,那么我不知道为什么它不起作用。
  • @Adrian X-FACEBOOK-PLATFORM 上的 OAuth2.0 支持昨天刚刚添加:developers.facebook.com/blog/post/555 Session_key 和 sig 被 access_token 替换。
  • 是的,你是对的。谢谢你指出我。我会努力让它发挥作用。
【解决方案5】:

不知道这是否是问题所在,我也没有时间进行测试,但您的签名对我来说似乎是错误的。还请注意 Facebook 已移至 OAuth 2.0 at the beginning of Sept,因此请确保您的 access_token 正确。

试试这个:

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

【讨论】:

    猜你喜欢
    • 2011-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-16
    • 1970-01-01
    • 2012-06-26
    相关资源
    最近更新 更多