集成 Facebook 聊天

发布于 2024-12-01 22:50:13 字数 6134 浏览 0 评论 0原文

我已经编写了一个用 C# 集成 Facebook 用户聊天的程序,但是我总是得到 将响应发送到服务器后,

我检查了 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);

    }

我参考了这篇文章 C# 中的 Facebook 聊天身份验证X-FACEBOOK-PLATFORM我的应用程序类型是本机/桌面。

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

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

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

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

现在我已经对访问令牌进行了硬编码(在访问令牌调试器中验证了它),会话密钥、应用程序密钥和应用程序密钥仍然出现相同的错误 <未授权/>

编辑3: 我一直在敲击我花了一个多星期的时间,仍然不起作用,但今天我在 文档 上面写着请注意,这需要通过 TLS(传输层安全),否则您会收到错误。 我想我需要相应地修改我的代码。

编辑4:我已经尝试了文档中的代码,发现$SESSION_XML的值应该是

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

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

×纯※雪 2024-12-08 22:50:13

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

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

展望未来,access_token 将能够用于 X-FACEBOOK-PLATFORM。

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.

生死何惧 2024-12-08 22:50:13

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

对于例如,您会希望自己没有手动编码所有 XML,而不是很快通过 DOM 运行它。同时,使用以下字符测试所有输入:<>'"&

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: <>'"&

森林散布 2024-12-08 22:50:13

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

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

尽揽少女心 2024-12-08 22:50:13

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

带有 Java Asmack 库的 XMPP 支持 X-FACEBOOK- PLATFORM

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

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

然后,Facebook 为您提供会话密钥,您应将其用作应用程序密钥参数。为了能够使用此方法,您必须在此页面中禁用删除已弃用的 API 参数 在编辑设置部分和高级选项卡中。

我希望这对你有帮助。

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.

囚你心 2024-12-08 22:50:13

不知道这是否是问题所在,而且我没有时间测试,但你的签名对我来说看起来是错误的。另请记住,Facebook 9 月初已转向 OAuth 2.0,因此请确保您的 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));

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));
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文