Disclaimer
By viewing this webpage you have just agreed to the following; this article is not written or endorsed by Facebook, the information contained within it may or may not be accurate, your mileage may very and that I am in no way liable for your stupidity.
Updated (26 Sept '11) to include StartTls and new usage of access_token.
Download the source FacebookXmpp.zip
X-Facebook-Platform mechanism
In order to authenticate with Facebook's chat platform, you need to use either DIGEST-MD5 (a hash of the users username/password - old skool) or if your application uses OAuth, then use Facebook's X-FACEBOOK-PLATFORM mechanism. I outline here how I implemented this authentication as part of the XDA Facebook for Windows Mobile application, which will include Facebook chat.
This article is a repeat of the X-FACEBOOK-PLATFORM in VB.NET article which I found to be very useful.
Credit goes to Joel Day pointing me in this direction and getting me started.
XMPP Conversation
Xmpp is basically a XML document conversation, where client and server 'build' their documents in parallel. From a coding point of view, I found it easier to write my own XML parser [yes, insane I know!] One of the main motivators behind this, is that an XMPP conversation deals in XML fragments.
Open a connection to chat.facebook.com
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect("chat.facebook.com", 5222);Send 'stream:stream' You initialise a conversation by opening a stream with the server. This is a "hello server" step.
<?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' >
You should then receive a response; "hello back" followed by "list of ways to authenticate". Notice that this includes <starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/> which indicates that the server supports SSL communication. There is more information which we ignore for now.
<?xml version="1.0"?> <stream:stream id="90854922" from="chat.facebook.com" version="1.0" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" xml:lang="en"> <stream:features> <starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/> <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> <mechanism>X-FACEBOOK-PLATFORM</mechanism> <mechanism>DIGEST-MD5</mechanism> </mechanisms> </stream:features>
We switch to Transport Layer Security by continuing our plain text conversation by sending StartTLS message
<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>
The expected plain text response is
<proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>
In my Connection class code (which has 3 public methods 'Send' 'Receive' and now 'StartTls') I also have NetworkStream and a SslSocket members.
private NetworkStream _networkStream; private SslStream _sslStream; public void StartTls() { UseSecure = true; if (_networkStream == null) _networkStream = new NetworkStream( _socket ); if (_sslStream != null) return; _sslStream = new SslStream(_networkStream, false); _sslStream.AuthenticateAsClient("chat.facebook.com", new X509CertificateCollection(), SslProtocols.Tls, false); }
Once you have the SslStream open, use this for the reading/writing to the socket. Begin a new stream <stream:stream... as above and get new stream:features which does not include StartTls since we are using TLS.
This time we are interested the //stream:stream/stream:features/mechanisms/mechanism X-FACEBOOK-PLATFORM element.
<?xml version="1.0"?> <stream:stream id="90854922" from="chat.facebook.com" version="1.0" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" xml:lang="en"> <stream:features> <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> <mechanism>X-FACEBOOK-PLATFORM</mechanism> <mechanism>DIGEST-MD5</mechanism> </mechanisms> </stream:features>
Select authentication mechanism by telling the server we'll use X-Facebook-Platform authorisation.
<auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' mechanism='X-FACEBOOK-PLATFORM' />
The service responds with...
<challenge xmlns="urn:ietf:params:xml:ns:xmppasl"> dmVyc2lvbj0xJm1ldG....3NzlFRkJCMDVERUY2MEM= </challenge>
This challenge ('dmVyc2...') is a Base64 Encoded string, which we can convert to a string by doing the following
string decoded = Encoding.UTF8.GetString( Convert.FromBase64String( challengeData ) );
Which gives us: version=1&method=auth.xmpp_login&nonce=3B53CFB6EE...FB424B68 I parse this in my code by doing a Split('&') to get three name=value pairs, then Split('=') to get name / value.
We use these to create our response which includes the following fields (in & separated name=value pairs)
- method - the Method field received in the challenge
- api_key - your Facebook Application key
- access_token - the Users OAuth Access Token
- call_id - some unique value (i.e. seconds since 1970)
- v - the Version field received in the challenge
- nonce - the Nonce field received in the challenge
In this new version, we no longer need the to calculate a signature. We simply covert this string into a Base64 Encoded string and pass it back to the service in a response.
Convert.ToBase64String( Encoding.UTF8.GetBytes( response ) )
<response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> BpX2tleT1kN2Q5ZTEwODU14ZTdiYzAxY2 EwNSZjYWxsX2lkPTEzMTM0MTQwNDEm .... bWV0aG9kPWF1dGgueG1wcF9sb2dpbiZu 25jZT1BQzQxRjE2QTBENEI0REE3ODcX2tlx </response>
Assuming that your user has permitted xmpp_login permission during the OAuth process, you should get the response
<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>
Now you're ready to actually do something with the service!
