Search code examples
androidoauth-2.0smackasmackgoogle-talk

Unable to login to google talk using asmack library for android


I have been trying to login to google talk using the asmack library without success. I don't really know what is happening behind the scenes, just gathered some code snippets from here and there. This is what I have currently for the android activity:

public class MainActivity extends Activity {                                                          

    public static final String HOST = "talk.google.com";                                              
    public static final int PORT = 5222;                                                              
    public static final String SERVICE = "gmail.com";                                                 
    public static final String USER = "[email protected]"; 
    public static final String PASSWORD = "password"; 

    @Override                                                                                         
    public void onCreate(Bundle savedInstanceState) {                                                 
        super.onCreate(savedInstanceState);                                                           
        setContentView(R.layout.main);                                                                
        Context context = getApplicationContext();                                                    
        SmackAndroid asmk = SmackAndroid.init(context);                                               
        SASLAuthentication.registerSASLMechanism("X-OAUTH2", SASLGoogleOAuth2Mechanism.class);        
        SASLAuthentication.supportSASLMechanism("X-OAUTH2", 0);                                       
        ConnectionConfiguration connConfig = new ConnectionConfiguration(HOST, PORT, SERVICE);        
        connConfig.setSecurityMode(SecurityMode.enabled);                                             
        connConfig.setReconnectionAllowed(true);                                                      

        XMPPTCPConnection connection = new XMPPTCPConnection(connConfig);                             
        try {                                                                                         
            connection.connect();                                                                     
            try {                                                                                     
                connection.login(USER, PASSWORD);                         
            } catch (XMPPException ex) {                                                              
                Log.w("XMPPChatDemoActivity", "Failed to log in");                                    
                Log.w("XMPPChatDemoActivity", ex.getMessage());                 
            }                                                                                         
        } catch (...) {                                                                  
           ...
        }
    }
}

and this is the SASLMechanism:

public class SASLGoogleOAuth2Mechanism extends SASLMechanism {

    private static final Logger log = Logger.getLogger("XMPPChatDemoActivity");
    public static final String NAME = "X-OAUTH2";

    public SASLGoogleOAuth2Mechanism(SASLAuthentication saslAuthentication) {
        super(saslAuthentication);
        log.info("Creating SASL mechanism for GTalk (X-OAUTH2)");
    }

    @Override
    public void authenticate(String username, String host, String serviceName, String password) throws IOException, SaslException, NotConnectedException {
        this.authenticationId = username;
        this.hostname = host;
        this.password = password;

        String[] mechanisms = { "PLAIN" };
        Map<String, String> props = new HashMap<String, String>();
        this.sc = Sasl.createSaslClient(mechanisms, username, "xmpp", host, props, this);
        log.info("sc " + sc);
        authenticate();
    }

    @Override
    public void authenticate(String host, CallbackHandler cbh) throws IOException, SaslException, NotConnectedException {
        String[] mechanisms = { "PLAIN" };
        Map<String, String> props = new HashMap<String, String>();

        sc = Sasl.createSaslClient(mechanisms, null, "xmpp", host, props, cbh);
        authenticate();
    }
    @Override
    protected void authenticate() throws IOException, SaslException, NotConnectedException {
        String authenticationText = null;

        try { 
            if (sc.hasInitialResponse()) {
                byte[] response = sc.evaluateChallenge(new byte[0]);
                authenticationText = Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
            }
        } catch (SaslException e) {
            throw new SaslException("SASL authentication failed", e);
        }

        // Send the authentication to the server
        getSASLAuthentication().send(new GoogleOAuthMechanism(authenticationText));
    }
    @Override
    protected String getName() {
        return NAME;
    }
    /**
     * Initiating SASL authentication by select a mechanism.
     */
    public static class GoogleOAuthMechanism extends Packet {
        private final String authenticationText;

        /**
         * Create a GoogleOAuthMechanism.
         *
         * @param authenticationText the authentification token
         *
         */
        public GoogleOAuthMechanism(final String authenticationText) {
            this.authenticationText = authenticationText;
        }

        @Override
        public String toXML() {

            StringBuilder stanza = new StringBuilder();
            stanza.append("<auth mechanism=\"").append(NAME);
            stanza.append("\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" "
                    + "auth:service=\"oauth2\" "
                    + "xmlns:auth=\"http://www.google.com/talk/protocol/auth\">");
            if (authenticationText != null
                    && authenticationText.trim().length() > 0) {
                stanza.append(authenticationText);
            }
            stanza.append("</auth>");
            return stanza.toString();
        }
    }
}

The code is ok and i don't get any exception, but I get a <not-authorized> response. The user name and password are correct. I couldn't find any reference code for this library. Any help will be appreciated.


Solution

  • After struggling for a few days and trying every imaginable combination of the snippets that I found on the Internet, I've come across a solution that I'm glad to share with the community.

    It appears that instead of passing the password to the XMPPTCPConnection.login() method, we should use an auth token from google. I found a post explaining a way to generate such a token. A similar question to mine exists, but it uses X-GOOGLE-TOKEN mechanism also for authentication, which is a different approach to mine of using X-OAUTH2 mechanism for authentication. Furthermore, all other posts I could find relating to the problem of authenticating to google talk using OAUTH2 are old. I am using the asmack build of smack 4.0.4.

    So the only modification required for the code shown in the question to work is this:

    AccountManager am = AccountManager.get(this);
    Account accounts[] = am.getAccountsByType("com.google");
    conn.login(USER, amf.blockingGetAuthToken(accounts[0], GOOGLE_TOKEN_TYPE, true));
    

    As you see, I used an account stored on the device to proof the solution, but you can generate the token by other means, as I commented above.

    Finally, as I found the solution by trial and error, I would appreciate anyone explaining what is really happening or any misinformation I could have given, so this answer can be further improved.