X-FACEBOOK-PLATFORM authentication error

661 Views Asked by At

I'm trying to connect to Facebook chat api via SMACK using the code below but I always receive X-FACEBOOK-PLATFORM failed: not-authorized. I have a valid token with xmpp permission but I think I'm missing something... it's working with user/pass, but not with accessToken.

Thanks for your help guys, this thing is driving me crazy.

public class FacebookChatSample {

public static void main(   )
{

    String accessToken = //internet example "AAACjg0Eh1N8BAFuhUFZAN0EteV6pjZAsZAI46i8oV3iVmyLdaKiwaBcM5DPFjbEZAq9LZAZAA0qXsvaXkKB7SqnhubzlUAK7tmr3UYLaeQgaXSJ6EZCKn2G";
    String consumerKey = //internet example "179784128779487";

    long targetFacebookId = 550121201669650L;

    String toID = "1002221765";

    String message = "HELLOOOOO FROM MY JAVA PROGRAM!!";

    XMPPConnection connection = createXMPPConnection();
    try
    {
        connection.connect();
        connection.login( consumerKey, accessToken );

        String to = String.format( "-%[email protected]", Long.valueOf( targetFacebookId ) );
        Chat chat = connection.getChatManager().createChat( to, null );
        chat.sendMessage( message );
    }
    catch( XMPPException e )
    {
       e.printStackTrace();
    }
    finally
    {
        connection.disconnect();
    }
}

private static synchronized XMPPConnection createXMPPConnection()
{
    SASLAuthentication.registerSASLMechanism(
            SASLXFacebookPlatformMechanism.NAME,
            SASLXFacebookPlatformMechanism.class );
    SASLAuthentication.supportSASLMechanism(
            SASLXFacebookPlatformMechanism.NAME, 0 );

    ConnectionConfiguration configuration = new ConnectionConfiguration(
            "chat.facebook.com", 5222 );
    configuration.setSASLAuthenticationEnabled( true );

    return new XMPPConnection( configuration );
}

public static class SASLXFacebookPlatformMechanism extends SASLMechanism
{
    public static final String NAME = "X-FACEBOOK-PLATFORM";

    public SASLXFacebookPlatformMechanism(
            SASLAuthentication saslAuthentication )
    {
        super( saslAuthentication );
    }

    private String apiKey = "";

    private String accessToken = "";

    @Override
    protected void authenticate() throws IOException, XMPPException
    {
        AuthMechanism stanza = new AuthMechanism( getName(), null );
        getSASLAuthentication().send( stanza );
    }

    @SuppressWarnings( "hiding" )
    @Override
    public void authenticate( String apiKey, String host, String accessToken )
            throws IOException, XMPPException
    {
        if( apiKey == null || accessToken == null )
        {
            throw new IllegalStateException( "Invalid parameters!" );
        }

        this.apiKey = apiKey;
        this.accessToken = accessToken;
        this.hostname = host;

        String[] mechanisms = { "DIGEST-MD5" };
        Map<String, String> props = new HashMap<String, String>();
        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<String, String> props = new HashMap<String, String>();
        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 decodedResponse = new String(
                    org.jivesoftware.smack.util.Base64.decode( challenge ) );
            Map<String, String> parameters = getQueryMap( decodedResponse );

            String version = "1.0";
            String nonce = parameters.get( "nonce" );
            String method = parameters.get( "method" );

            Long callId = Long.valueOf( System.currentTimeMillis() );

            String composedResponse = String
                    .format(
                            "method=%s&nonce=%s&access_token=%s&api_key=%s&call_id=%s&v=%s",
                            URLEncoder.encode( method, "UTF-8" ),
                            URLEncoder.encode( nonce, "UTF-8" ),
                            URLEncoder.encode( this.accessToken, "UTF-8" ),
                            URLEncoder.encode( this.apiKey, "UTF-8" ),
                            callId, URLEncoder.encode( version, "UTF-8" ) );
            response = composedResponse.getBytes();
        }

        String authenticationText = "";

        if( response != null )
        {
            authenticationText = org.jivesoftware.smack.util.Base64
                    .encodeBytes(
                            response,
                            org.jivesoftware.smack.util.Base64.DONT_BREAK_LINES );
        }

        Response stanza = new Response( authenticationText );

        getSASLAuthentication().send( stanza );
    }

    private Map<String, String> getQueryMap( String query )
    {
        String[] params = query.split( "&" );
        Map<String, String> map = new HashMap<String, String>();
        for( String param : params )
        {
            String name = param.split( "=" )[0];
            String value = param.split( "=" )[1];
            map.put( name, value );
        }
        return map;
    }
}
}
1

There are 1 best solutions below

0
JSv4 On

My God, I have been struggling with this forever as well. I just figured it out! Once I got it, it seemed so easy, but the confusing thing is the problem that seems to come from XMPP is actually a problem with Facebook. I think your problem (my problem was) that Facebook requires you to request application specific login permissions that have nothing to do with the XMPP protocol. If you look at the Chat API specifications, it says you need to request xmpp_login extended permission for your application if you're using X-FACEBOOK-PLATFORM. You have to do this through the session object in the Facebook SDK you used to get your session_token.

Here is the code I used:

session.requestNewReadPermissions(new NewPermissionsRequest(getActivity(), Arrays.asList("xmpp_login")));

As soon as I did that it worked like a charm. Last caveat is that you need to make sure the popup dialog asking the user to grant your app this extended permission comes up and gets the required permission BEFORE you moved on and try to xmpp.login. Otherwise you'll have the same problem as your app will request permission but not actually get permission.

I hope this works for you.