Chat API

This document is intended for developers working with the chat features of the Facebook Platform. If you are a user looking for help with Facebook Chat, please refer to the Help Center

You can integrate Facebook Chat into your Web-based, desktop, or mobile instant messaging products. Your instant messaging client connects to Facebook Chat via the Jabber/XMPP service. This document describes the features and limitations of Facebook Chat's XMPP protocol for the developer who intends to implement a Facebook Chat client.

Before reading this document, you should be familiar with the terms and concepts involved in XMPP chat clients and servers.

Please do not use this API to send spammy messages to users. Facebook takes user experience and spam extremely seriously and if users report your app as using the Chat API to spam them, we will disable your app.

Features and Limitations

Features

  • Facebook Platform authentication using X-FACEBOOK-PLATFORM or PLAIN SASL authentication mechanism
  • Sending and receiving plain-text messages (not HTML messages)
  • Sending and receiving typing notifications using the XEP-0085 protocol extension (not the XEP-0022 extension)
  • Setting the user idle using a show element in presence stanzas (there will be a delay before the user appears idle)
  • Receiving vCards using the XEP-0054 extension
  • Retrieving friends' photos (either with vCard or XMPP presence)

Limitations

Facebook Chat should be compatible with every XMPP client, but is not a full XMPP server. It should be thought of as a proxy into the world of Facebook Chat on www.facebook.com. As a result, it has several behaviors that differ slightly from what you would expect from a traditional XMPP service:

  • Your client cannot send or receive HTML messages
  • Because roster items and presence subscriptions are based on the user's Facebook friends, they cannot be created or deleted using the standard XMPP mechanisms.
  • Facebook Chat is terse when sending updates for new friends, because the negotiation happens outside of XMPP. Future versions of Facebook Chat may be more conformant.
  • The user's own Jabber ID (JID) is different from the Jabber ID that their contacts will see because the translation is done internally.
  • Arbitrary IQ stanzas cannot be passed between clients.
  • Presence probes do not currently work.
  • Non-SASL authentication with the jabber:iq:auth namespace as described in XEP-0078 is not currently supported.
  • The XML parser does not yet fully handle XML namespaces. Please stick to the same style as the examples in XMPP RFCs 3920 and 3921 when using XML namespaces.

Best Practices

In order to provide the best user experience, we recommend your chat integration do the following:

  • Your Facebook Chat integration should only be used for applications facilitating real time conversation or interaction between users. Links or advertisements should not be sent via chat, unless the sending user types in this message.
  • Your Facebook Chat integration should only be used for sessions that are expected to be long-lived. Clients should not rapidly churn on and off.
  • vCards retrieved through Facebook Chat will contain profile pictures if available. Clients should cache these pictures using the content hash, not the user ID, as the key. vCards should only be fetched if the client does not already have that user's picture cached.
  • Clients should not automatically reconnect if they receive a stream-error of type conflict.
  • Clients should be able to handle a single contact with multiple group elements.
  • Incoming messages from the JIDs chat.facebook.com or facebook.com should be displayed as administrative messages.

Authenticating with X-FACEBOOK-PLATFORM

We support a custom SASL mechanism called X-FACEBOOK-PLATFORM that allows clients to connect to chat using Facebook authentication.

In order to connect using this mechanism, the user must first log in to your application and grant the xmpp_login extended permission. Follow the client side flow to get a valid access_token for the user with the xmpp_login extended permission.

Your application may now log in to Facebook Chat via Jabber using the X-FACEBOOK-PLATFORM mechanism. The user's Jabber ID will be assigned during the resource binding step of XMPP. Please keep in mind that while all of the messages defined by the X-FACEBOOK-PLATFORM mechanism are UTF-8 strings, XMPP specifies that they should be Base64-encoded before being sent over the wire.

The mechanism starts with a server challenge, in the form of a common HTTP query string: an ampersand-separated sequence of equals-sign-delimited key/value pairs. The keys and values are UTF-8-encoded and URL-encoded. The query string contains two items: method and nonce.

The client's reply should be a similarly-encoded query string prepared as if it were going to call a method against the Facebook API. The call should contain the following parameters:

  • string method: Should be the same as the method specified by the server.
  • string api_key: The application key associated with the calling application.
  • string access_token: The access_token obtained in the above step.
  • float call_id: The request's sequence number.
  • string v: This must be set to 1.0 to use this version of the API.
  • string format: Optional - Ignored.
  • string cnonce: Optional - Client-selected nonce. Ignored.
  • string nonce: Should be the same as the nonce specified by the server.

The server will then respond with a success or failure message. Note that this needs to be over TLS or you'll get an error.

Clients should retrieve their user's vCard from the server according to XEP-0054 in order to be able to display a more natural name to the user. For example:

<iq id='1' type='get'><vCard xmlns='vcard-temp'/></iq>

Sample code

Below is some sample, self-explanatory code that shows you how to authenticate a user and connect with XMPP.

    <?php
    // Copyright 2004-present Facebook. All Rights Reserved.

    $STREAM_XML = '<stream:stream '.
      'xmlns:stream="http://etherx.jabber.org/streams" '.
      'version="1.0" xmlns="jabber:client" to="chat.facebook.com" '.
      'xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace">';

    $AUTH_XML = '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" '.
      'mechanism="X-FACEBOOK-PLATFORM"></auth>';

    $CLOSE_XML = '</stream:stream>';

    $RESOURCE_XML = '<iq type="set" id="3">'.
      '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">'.
      '<resource>fb_xmpp_script</resource></bind></iq>';

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

    $START_TLS = '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>';


    function open_connection($server) {
      print "[INFO] Opening connection... ";

      $fp = fsockopen($server, 5222, $errno, $errstr);
      if (!$fp) {
        print "$errstr ($errno)<br>";
      } else {
        print "connnection open<br>";
      }

      return $fp;
    }

    function send_xml($fp, $xml) {
      fwrite($fp, $xml);
    }

    function recv_xml($fp,  $size=4096) {
      $xml = fread($fp, $size);
      if ($xml === "") {
         return null;
      }

      // parses xml
      $xml_parser = xml_parser_create();
      xml_parse_into_struct($xml_parser, $xml, $val, $index);
      xml_parser_free($xml_parser);

      return array($val, $index);
    }

    function find_xmpp($fp,  $tag, $value=null, &$ret=null) {
      static $val = null, $index = null;

      do {
        if ($val === null && $index === null) {
          list($val, $index) = recv_xml($fp);
          if ($val === null || $index === null) {
            return false;
          }
        }

        foreach ($index as $tag_key => $tag_array) {
          if ($tag_key === $tag) {
            if ($value === null) {
              if (isset($val[$tag_array[0]]['value'])) {
                $ret = $val[$tag_array[0]]['value'];
              }
              return true;
            }
            foreach ($tag_array as $i => $pos) {
              if ($val[$pos]['tag'] === $tag && isset($val[$pos]['value']) &&
                $val[$pos]['value'] === $value) {
                  $ret = $val[$pos]['value'];
                  return true;
              }
            }
          }
        }
        $val = $index = null;
      } while (!feof($fp));

      return false;
    }


    function xmpp_connect($options, $access_token) {
      global $STREAM_XML, $AUTH_XML, $RESOURCE_XML, $SESSION_XML, $CLOSE_XML, $START_TLS;

      $fp = open_connection($options['server']);
      if (!$fp) {
        return false;
      }

      // initiates auth process (using X-FACEBOOK_PLATFORM)
      send_xml($fp,  $STREAM_XML);
      if (!find_xmpp($fp, 'STREAM:STREAM')) {
        return false;
      }
      if (!find_xmpp($fp,  'MECHANISM', 'X-FACEBOOK-PLATFORM')) {
        return false;
      }

      // starting tls - MANDATORY TO USE OAUTH TOKEN!!!!
      send_xml($fp,  $START_TLS);
      if (!find_xmpp($fp, 'PROCEED', null, $proceed)) {
        return false;
      }
      stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);

      send_xml($fp, $STREAM_XML);
      if (!find_xmpp($fp, 'STREAM:STREAM')) {
        return false;
      }
      if (!find_xmpp($fp, 'MECHANISM', 'X-FACEBOOK-PLATFORM')) {
        return false;
      }

      // gets challenge from server and decode it
      send_xml($fp, $AUTH_XML);
      if (!find_xmpp($fp,  'CHALLENGE', null, $challenge)) {
        return false;
      }
      $challenge = base64_decode($challenge);
      $challenge = urldecode($challenge);
      parse_str($challenge, $challenge_array);

      // creates the response array
      $resp_array = array(
        'method' => $challenge_array['method'],
        'nonce' => $challenge_array['nonce'],
        'access_token' => $access_token,
        'api_key' => $options['app_id'],
        'call_id' => 0,
        'v' => '1.0',
      );
      // creates signature
      $response = http_build_query($resp_array);

      // sends the response and waits for success
      $xml = '<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">'.
        base64_encode($response).'</response>';
      send_xml($fp, $xml);
      if (!find_xmpp($fp, 'SUCCESS')) {
        return false;
      }

      // finishes auth process
      send_xml($fp, $STREAM_XML);
      if (!find_xmpp($fp,'STREAM:STREAM')) {
        return false;
      }
      if (!find_xmpp($fp, 'STREAM:FEATURES')) {
        return false;
      }
     send_xml($fp, $RESOURCE_XML);
      if (!find_xmpp($fp, 'JID')) {
        return false;
      }
      send_xml($fp, $SESSION_XML);
      if (!find_xmpp($fp, 'SESSION')) {
        return false;
      }

      // we made it!
      send_xml($fp, $CLOSE_XML);
      print ("Authentication complete<br>");
      fclose($fp);

      return true;
    }



    //Gets access_token with xmpp_login permission
    function get_access_token($app_id, $app_secret, $my_url){ 

      $code = $_REQUEST["code"];

      if(empty($code)) {
        $dialog_url = "http://www.facebook.com/dialog/oauth?scope=xmpp_login".
         "&client_id=" . $app_id . "&redirect_uri=" . urlencode($my_url) ;
        echo("<script>top.location.href='" . $dialog_url . "'</script>");
      }
       $token_url = "https://graph.facebook.com/oauth/access_token?client_id="
        . $app_id . "&redirect_uri=" . urlencode($my_url) 
        . "&client_secret=" . $app_secret 
        . "&code=" . $code;
       $access_token = file_get_contents($token_url);
        parse_str($access_token, $output);

        return($output['access_token']);
    }

    function _main() {
      print "Test platform connect for XMPP<br>";
      $app_id='YOUR_APP_ID';
      $app_secret='YOUR-APP_SECRET';
      $my_url = "YOUR_APP_URL";
      $uid = 'USER_ID';
      $access_token = get_access_token($app_id,$app_secret,$my_url);
      print "access_token: ".$access_token."<br>";

      $options = array(
        'uid' => $uid,
        'app_id' => $app_id,
        'server' => 'chat.facebook.com',
       );

      // prints options used
      print "server: ".$options['server']."<br>";
      print "uid: ".$options['uid']."<br>";
      print "app id: ".$options['app_id']."<br>";

      if (xmpp_connect($options, $access_token)) {
        print "Done<br>";
      } else {
        print "An error ocurred<br>";
      }

    }

    _main();

Authenticating with PLAIN

Facebook also allows you to authenticate with a PLAIN SASL method. This is some relatively self-explanatory code that you can use to test out logging in with the PLAIN method.

To connect and use PLAIN authentication, you must use a TLS connection.

Sample code


<?php $email = $argv[1]; $pwd = $argv[2]; $STREAM_XML = '<stream:stream '. 'xmlns:stream="http://etherx.jabber.org/streams" '. 'version="1.0" xmlns="jabber:client" to="chat.facebook.com" '. 'xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace">'; $AUTH_PLAIN_XML = '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="PLAIN">'. base64_encode("\x00".$email."\x00".$pwd). '</auth>'; $CLOSE_XML = '</stream:stream>'; $RESOURCE_XML = '<iq type="set" id="3">'. '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">'. '<resource>fb_xmpp_script</resource></bind></iq>'; $SESSION_XML = '<iq type="set" id="4" to="chat.facebook.com">'. '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>'; $START_TLS = '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>'; function open_connection($server) { print "[INFO] Opening connection... \n"; $fp = fsockopen($server, 5222, $errno, $errstr); if (!$fp) { print "$errstr ($errno)\n"; } else { print "connection opened\n"; } return $fp; } function send_xml($fp, $xml) { print "[Client]:\n".$xml."\n\n"; fwrite($fp, $xml); } function recv_xml($fp, $size=4096) { $xml = fread($fp, $size); while (substr($xml, -1) != ">") { $xml .= fread($fp, $size); } print "[Server]:\n".$xml."\n\n"; if ($xml === "") { return null; } // parses xml $xml_parser = xml_parser_create(); xml_parse_into_struct($xml_parser, $xml, $val, $index); xml_parser_free($xml_parser); return array($val, $index); } function find_xmpp($fp, $tag, $value=null, &$ret=null) { static $val = null, $index = null; do { if ($val === null && $index === null) { list($val, $index) = recv_xml($fp); if ($val === null || $index === null) { return false; } } foreach ($index as $tag_key => $tag_array) { if ($tag_key === $tag) { if ($value === null) { if (isset($val[$tag_array[0]]['value'])) { $ret = $val[$tag_array[0]]['value']; } return true; } foreach ($tag_array as $i => $pos) { if ($val[$pos]['tag'] === $tag && isset($val[$pos]['value']) && $val[$pos]['value'] === $value) { $ret = $val[$pos]['value']; return true; } } } } $val = $index = null; } while (!feof($fp)); return false; } function xmpp_connect($options) { global $STREAM_XML, $AUTH_PLAIN_XML, $RESOURCE_XML, $SESSION_XML, $CLOSE_XML, $START_TLS; $fp = open_connection($options['server']); if (!$fp) { return false; } // initiates auth process (using PLAIN auth) send_xml($fp, $STREAM_XML); if (!find_xmpp($fp, 'STREAM:STREAM')) { return false; } if (!find_xmpp($fp, 'MECHANISM', 'PLAIN')) { return false; } // starting tls send_xml($fp, $START_TLS); if (!find_xmpp($fp, 'PROCEED', null, $proceed)) { return false; } stream_socket_enable_crypto($fp, true, STREAM_CRYPTO_METHOD_TLS_CLIENT); send_xml($fp, $STREAM_XML); if (!find_xmpp($fp, 'STREAM:STREAM')) { return false; } if (!find_xmpp($fp, 'MECHANISM', 'PLAIN')) { return false; } // send plain auth xml containing base64-encoded email and pwd send_xml($fp, $AUTH_PLAIN_XML); if (!find_xmpp($fp, 'SUCCESS')) { return false; } // finishes auth process send_xml($fp, $STREAM_XML); if (!find_xmpp($fp,'STREAM:STREAM')) { return false; } if (!find_xmpp($fp, 'STREAM:FEATURES')) { return false; } send_xml($fp, $RESOURCE_XML); if (!find_xmpp($fp, 'JID')) { return false; } send_xml($fp, $SESSION_XML); if (!find_xmpp($fp, 'SESSION')) { return false; } // we made it! send_xml($fp, $CLOSE_XML); print ("Authentication complete/n"); fclose($fp); return true; } function _main() { print "Test PLAIN auth for XMPP/n"; $options = array( 'server' => 'chat.facebook.com', ); if (xmpp_connect($options)) { print "Done\n"; } else { print "An error ocurred\n"; } } _main();
Was this document helpful?