XMPP with Java Asmack library supporting X-FACEBOOK-PLATFORM

后端 未结 5 1191
栀梦
栀梦 2020-11-29 03:55

I\'m trying to make a Facebook Chat on Android with the Smack library. I\'ve read the Chat API from Facebook, but I cannot understand how I have to authenticate with Faceboo

5条回答
  •  [愿得一人]
    2020-11-29 04:07

    Finally, thanks to the no.good.at.coding code and the suggestion of harism, I've been able to connect to the Facebook chat. This code is the Mechanism for the Asmack library (the Smack port for Android). For the Smack library is necessary to use the no.good.at.coding mechanism.

    SASLXFacebookPlatformMechanism.java:

    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.net.URLEncoder;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.GregorianCalendar;
    import java.util.HashMap;
    import java.util.Map;
    
    import org.apache.harmony.javax.security.auth.callback.CallbackHandler;
    import org.apache.harmony.javax.security.sasl.Sasl;
    import org.jivesoftware.smack.SASLAuthentication;
    import org.jivesoftware.smack.XMPPException;
    import org.jivesoftware.smack.sasl.SASLMechanism;
    import org.jivesoftware.smack.util.Base64;
    
    public class SASLXFacebookPlatformMechanism extends SASLMechanism
    {
    
        private static final String NAME              = "X-FACEBOOK-PLATFORM";
    
        private String              apiKey            = "";
        private String              applicationSecret = "";
        private String              sessionKey        = "";
    
        /**
         * Constructor.
         */
        public SASLXFacebookPlatformMechanism(SASLAuthentication saslAuthentication)
        {
            super(saslAuthentication);
        }
    
        @Override
        protected void authenticate() throws IOException, XMPPException
        {
    
            getSASLAuthentication().send(new AuthMechanism(NAME, ""));
        }
    
        @Override
        public void authenticate(String apiKeyAndSessionKey, String host,
                String applicationSecret) throws IOException, XMPPException
        {
            if (apiKeyAndSessionKey == null || applicationSecret == null)
            {
                throw new IllegalArgumentException("Invalid parameters");
            }
    
            String[] keyArray = apiKeyAndSessionKey.split("\\|", 2);
            if (keyArray.length < 2)
            {
                throw new IllegalArgumentException(
                        "API key or session key is not present");
            }
    
            this.apiKey = keyArray[0];
            this.applicationSecret = applicationSecret;
            this.sessionKey = keyArray[1];
    
            this.authenticationId = sessionKey;
            this.password = applicationSecret;
            this.hostname = host;
    
            String[] mechanisms = { "DIGEST-MD5" };
            Map props = new HashMap();
            this.sc =
                    Sasl.createSaslClient(mechanisms, null, "xmpp", host, props,
                            this);
            authenticate();
        }
    
        @Override
        public void authenticate(String username, String host, CallbackHandler cbh)
                throws IOException, XMPPException
        {
            String[] mechanisms = { "DIGEST-MD5" };
            Map props = new HashMap();
            this.sc =
                    Sasl.createSaslClient(mechanisms, null, "xmpp", host, props,
                            cbh);
            authenticate();
        }
    
        @Override
        protected String getName()
        {
            return NAME;
        }
    
        @Override
        public void challengeReceived(String challenge) throws IOException
        {
            byte[] response = null;
    
            if (challenge != null)
            {
                String decodedChallenge = new String(Base64.decode(challenge));
                Map parameters = getQueryMap(decodedChallenge);
    
                String version = "1.0";
                String nonce = parameters.get("nonce");
                String method = parameters.get("method");
    
                long callId = new GregorianCalendar().getTimeInMillis();
    
                String sig =
                        "api_key=" + apiKey + "call_id=" + callId + "method="
                                + method + "nonce=" + nonce + "session_key="
                                + sessionKey + "v=" + version + applicationSecret;
    
                try
                {
                    sig = md5(sig);
                } catch (NoSuchAlgorithmException e)
                {
                    throw new IllegalStateException(e);
                }
    
                String composedResponse =
                        "api_key=" + URLEncoder.encode(apiKey, "utf-8")
                                + "&call_id=" + callId + "&method="
                                + URLEncoder.encode(method, "utf-8") + "&nonce="
                                + URLEncoder.encode(nonce, "utf-8")
                                + "&session_key="
                                + URLEncoder.encode(sessionKey, "utf-8") + "&v="
                                + URLEncoder.encode(version, "utf-8") + "&sig="
                                + URLEncoder.encode(sig, "utf-8");
    
                response = composedResponse.getBytes("utf-8");
            }
    
            String authenticationText = "";
    
            if (response != null)
            {
                authenticationText =
                        Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
            }
    
            // Send the authentication to the server
            getSASLAuthentication().send(new Response(authenticationText));
        }
    
        private Map getQueryMap(String query)
        {
            Map map = new HashMap();
            String[] params = query.split("\\&");
    
            for (String param : params)
            {
                String[] fields = param.split("=", 2);
                map.put(fields[0], (fields.length > 1 ? fields[1] : null));
            }
    
            return map;
        }
    
        private String md5(String text) throws NoSuchAlgorithmException,
                UnsupportedEncodingException
        {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(text.getBytes("utf-8"), 0, text.length());
            return convertToHex(md.digest());
        }
    
        private String convertToHex(byte[] data)
        {
            StringBuilder buf = new StringBuilder();
            int len = data.length;
    
            for (int i = 0; i < len; i++)
            {
                int halfByte = (data[i] >>> 4) & 0xF;
                int twoHalfs = 0;
    
                do
                {
                    if (0 <= halfByte && halfByte <= 9)
                    {
                        buf.append((char) ('0' + halfByte));
                    }
                    else
                    {
                        buf.append((char) ('a' + halfByte - 10));
                    }
                    halfByte = data[i] & 0xF;
                } while (twoHalfs++ < 1);
            }
    
            return buf.toString();
        }
    }
    

    To use it:

    ConnectionConfiguration config = new ConnectionConfiguration("chat.facebook.com", 5222);
    config.setSASLAuthenticationEnabled(true);
    XMPPConnection xmpp = new XMPPConnection(config);
    try
    {
        SASLAuthentication.registerSASLMechanism("X-FACEBOOK-PLATFORM", SASLXFacebookPlatformMechanism.class);
        SASLAuthentication.supportSASLMechanism("X-FACEBOOK-PLATFORM", 0);
        xmpp.connect();
        xmpp.login(apiKey + "|" + sessionKey, sessionSecret, "Application");
    } catch (XMPPException e)
    {
        xmpp.disconnect();
        e.printStackTrace();
    }
    

    apiKey is the API key given in the application settings page in Facebook. sessionKey is the second part of the access token. If the token is in this form, AAA|BBB|CCC, the BBB is the session key. sessionSecret is obtained using the old REST API with the method auth.promoteSession. To use it, it's needed to make a Http get to this url:

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

    Despite of the Facebook Chat documentation says that it's needed to use your application secret key, only when I used the key that returned that REST method I was able to make it works. To make that method works, you have to disable the Disable Deprecated Auth Methods option in the Advance tab in your application settings.

提交回复
热议问题