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.
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.