我编写了一个程序,用于在C#中集成Facebook用户聊天,但是在将响应发送到服务器后,我总是得到<failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><not-authorized/></failure>
。
我已经检查了API密钥和App秘密,两者都是正确的。看起来我将一些错误的参数传递给服务器。
这是我的代码。
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:奇怪的是,我始终以<appId>|<sessionKey>|<digest>
的形式获取本地/桌面应用程序的访问令牌。我进一步搜索并发现会话密钥需要从auth.promoteSession传统api中提取,并使用HttpUtility.UrlEncode
而不是HttpUtility.HtmlEncode
对参数进行编码。
现在我已经对访问令牌进行了硬编码(在Access Token Debugger中验证了它),会话密钥,应用密钥和应用秘密仍然会出现同样的错误<failure xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><not-authorized/></failure>
编辑3:我一直在敲打我的头一个星期仍然无效,但今天我在documentation找到了Note that this needs to be over TLS (Transport Layer Security) or you'll get an error.
的更新我想我需要相应地修改我的代码。
编辑4:我已经尝试了文档中的代码,发现$SESSION_XML
的值应为
$SESSION_XML = '<iq type="set" id="4">'.
'<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>';
我完成转换后会发布C#代码。
答案 0 :(得分:12)
要使用X-FACEBOOK-PLATFORM,您将需要随传统身份验证流程提供的用户会话。 但是,access_token包含|之后的用户会话正如你在edit1中提到的那样。
我们在上一篇博客文章中宣布access_token将被加密,这将是从10月1日开始强制执行的。在此之前,该选项可以在高级应用设置http://developers.facebook.com/blog/post/553/中切换。
继续前进,access_token将可用于X-FACEBOOK-PLATFORM。
答案 1 :(得分:7)
如果从现有的XMPP库开始,您将获得更快的速度。这是一个列表:http://xmpp.org/xmpp-software/libraries/
例如,您希望您没有手动编码所有XML,而不是很快通过DOM 非常运行它。在此期间,使用以下字符测试所有输入:<>'"&
答案 2 :(得分:5)
在Facebook Developer网站上的Python中有一个例子可能有用: https://developers.facebook.com/docs/chat/
答案 3 :(得分:1)
在尝试使用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中的删除不推荐使用的API 参数。
我希望这会对你有所帮助。
答案 4 :(得分:1)
不知道这是不是问题我没时间测试,但你的签名看起来不对。还记得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));