Reputation: 424
I'm writing a chat app which is connecting to a XMPP server, and if the user chooses, I want to give them the option to connect to their google chat account, without having to enter the credentials... I used google's javascript api to popout the google login form and after successful login access token will generate. Now using that access token and users email id i want to communicate with xmpp server so that users can chat with their gtalk friends.
I searched a lot but didn't found the solution. Whatever i found required users password but i want to use access token.
SASLAuthentication.registerSASLMechanism("X-OAUTH2", GoogleConnectSASLMechanism.class);
SASLAuthentication.supportSASLMechanism("X-OAUTH2", 0);
config = new ConnectionConfiguration(server, 5222, 'gmail.com');
config.setSASLAuthenticationEnabled(true);
config.setSecurityMode(SecurityMode.enabled);
config.setReconnectionAllowed(true);
connection = new XMPPConnection(config);
connection.connect();
connection.login(username, session_key, "Chat");
setServer(SERVER_TYPE.GTALK);
GoogleConnectSASLMechanism.java code is as follows:-
package org.jivesoftware.smack;
import java.io.IOException;
import java.net.URLEncoder;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import javax.security.auth.callback.CallbackHandler;
import javax.security.sasl.Sasl;
import org.jivesoftware.smack.SASLAuthentication;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Packet;
import org.jivesoftware.smack.sasl.SASLMechanism;
import org.jivesoftware.smack.util.Base64;
public class GoogleConnectSASLMechanism extends SASLMechanism {
public static final String NAME="X-OAUTH2";
public GoogleConnectSASLMechanism(SASLAuthentication saslAuthentication) {
super(saslAuthentication);
}
@Override
protected String getName() {
return NAME;
}
static void enable() { }
@Override
protected void authenticate() throws IOException, XMPPException
{
String authCode = password;
String jidAndToken = "\0" + URLEncoder.encode( authenticationId, "utf-8" ) + "\0" + authCode;
StringBuilder stanza = new StringBuilder();
//stanza.append( "<auth mechanism=\"" ).append( getName() );
//stanza.append( "\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">" );
// stanza.append( new String(Base64.encode( jidAndToken.getBytes( "UTF-8" ), Base64.DEFAULT ) ) );
stanza.append( "<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\" mechanism=\"" ).append( getName() );
stanza.append( "\" auth:service=\"oauth2\"" );
stanza.append( "\" xmlns:auth=\"http://www.google.com/talk/protocol/auth\">" );
stanza.append( "\" base64(\"\\0"+user_name+"\\0" + authCode+")" );
stanza.append( "</auth>" );
//Log.v("BlueTalk", "Authentication text is "+stanza);
// Send the authentication to the server
getSASLAuthentication().send( new Auth2Mechanism(stanza.toString()) );
}
public class Auth2Mechanism extends Packet {
String stanza;
public Auth2Mechanism(String txt) {
stanza = txt;
}
public String toXML() {
return stanza;
}
}
/**
* Initiating SASL authentication by select a mechanism.
*/
public class AuthMechanism extends Packet {
final private String name;
final private String authenticationText;
public AuthMechanism(String name, String authenticationText) {
if (name == null) {
throw new NullPointerException("SASL mechanism name shouldn't be null.");
}
this.name = name;
this.authenticationText = authenticationText;
}
public String toXML() {
StringBuilder stanza = new StringBuilder();
stanza.append("<auth mechanism=\"").append(name);
stanza.append("\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
if (authenticationText != null &&
authenticationText.trim().length() > 0) {
stanza.append(authenticationText);
}
stanza.append("</auth>");
return stanza.toString();
}
}
}
But I got an Exception from connection.login() that "username or password are not correct"
To do this, I'd get the permission to use the google account, get the token and authenticate to google talk (XMPP server, using Smack) using the token..
The problem is.. how do I do that? I mean, how do I authenticate to the GTalk server if I know the login and the token?
Any help would be highly appreciated...:)
Upvotes: 5
Views: 2844
Reputation: 11
I really appreciate Ketan's answer, it saved my whole project. I have been fighting with this problem for days... Anyway, I hope more people could find their solution from this post.
I use Smack 3.4, but I think this answer also works for later versions.
I'm only explaining Ketan's answer in detail, which is working for me (Just registered Stack Overflow, can not comment on the original answer).
Just override the authenticate() method of the GoogleConnectSASLMechanism class, such as creating a GoogleConnectSASLMechanism class locally, instead of using the one provided by Smack.
Google has its own OAUth2 mechanism which has custom attributes. This is why "X-OAUTH2 is experimental" in Smack. You can find this statement in Smack's documentation here. The "custom attributes" is the piece of xml:
<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl"
mechanism="X-OAUTH2"
auth:service="oauth2"
xmlns:auth="http://www.google.com/talk/protocol/auth">
base64("\0" + user_name + "\0" + oauth_token)
</auth>
.
This could be found in google's instruction. The reason Smack is not working for X-OAUth2 is because it does not contain this piece of XML in its stanza. So, the solution is just adding it.
Upvotes: 1
Reputation: 2223
Vijay,
Change your authenticate function as follows :
protected void authenticate() throws IOException, XMPPException {
final StringBuilder stanza = new StringBuilder();
byte response[] = null;
stanza.append("<auth xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\"" +
"mechanism=\"X-OAUTH2\"" +
"auth:service=\"oauth2\"" +
"xmlns:auth= \"http://www.google.com/talk/protocol/auth\">");
String composedResponse = "\0" + username + "\0" + sessionKey;
response = composedResponse.getBytes("UTF-8");
String authenticationText = "";
if (response != null) {
authenticationText = Base64.encodeBytes(response, Base64.DONT_BREAK_LINES);
}
stanza.append(authenticationText);
stanza.append("</auth>");
// Send the authentication to the server
Packet p=new Packet() {
@Override
public String toXML() {
return stanza.toString();
}
};
getSASLAuthentication().send(p);
}
Its same as your original authenticate function. I have just changed stanza.
Upvotes: 3
Reputation: 293
Your problem seems to be with the line:
stanza.append( "\" base64(\"\\0"+user_name+"\\0" + authCode+")" );
So I think this is what your authenticate
function should look like:
protected void authenticate() throws IOException, XMPPException
{
String authCode = password;
String jidAndToken = "\0" + authenticationId + "\0" + authCode;
StringBuilder stanza = new StringBuilder();
stanza.append("<auth mechanism=\"").append( getName());
stanza.append("\" xmlns=\"urn:ietf:params:xml:ns:xmpp-sasl\">");
stanza.append(new String(Base64.encode(jidAndToken.getBytes("UTF-8"), Base64.DEFAULT)));
stanza.append("</auth>");
//Log.v("BlueTalk", "Authentication text is "+stanza);
// Send the authentication to the server
getSASLAuthentication().send( new Auth2Mechanism(stanza.toString()) );
}
So, it's more like your commented code. base64(..)
shouldn't appear in the <auth>
content at all. There should just be a base64 encoded string in there.
With your help on my thread regarding this same topic, I was able to log in successfully.
Upvotes: 2