SASL Authentication failed while integrating facebook chat using Smack - facebook

I am trying to integrate facebook chat using smack API.But i get an error telling authentication failed using digest md5...
Here s the code for authentication:
SASLAuthentication.registerSASLMechanism("DIGEST-MD5", SASLDigestMD5Mechanism.class);
SASLAuthentication.supportSASLMechanism("DIGEST-MD5", 0);
ConnectionConfiguration config = new ConnectionConfiguration("chat.facebook.com",5222);
connection = new XMPPConnection(config);
config.setSASLAuthenticationEnabled(true);
connection.connect();
connection.login(userName, password);
below is the error i get wen i run it:
Exception in thread "main" SASL authentication failed using mechanism DIGEST-MD5:
at org.jivesoftware.smack.SASLAuthentication.authenticate(SASLAuthentication.java:325)
at org.jivesoftware.smack.XMPPConnection.login(XMPPConnection.java:395)
at org.jivesoftware.smack.XMPPConnection.login(XMPPConnection.java:349)
at JabberSmackAPIFacebook.login(JabberSmackAPIFacebook.java:31)
at JabberSmackAPIFacebook.main(JabberSmackAPIFacebook.java:77)
I can successfully connect to gtalk but am having no success vit fb...
can sumone tel me wat s the problem

For me the solution was to not include the host part in the username when calling login() without DNS SRV and not agains the Google Talk services. This is also described in the ignite forums.
E.g.
connection.login("user#jabber.org", "password", "resource");
becomes
connection.login("user", "password", "resource");

There is a huge thread at Ignite that deals with this issue. You may want to take a look at it as there are several solutions for Java and Android given that seem to work.

I have succesfully connected using DIGEST-MD5 to facebook, the code you have posted looks good.
But still we need to check the contents of your SASLDigestMD5Mechanism class
I have used the class provided in here with success
http://community.igniterealtime.org/message/200878#200878
Also you have to notice that in the DIGEST-MD5 mechanism you have to login with your facebook username and not with the email address. By default the facebook accounts don't have a username, you have to create one fisrt, you can check that in here:
http://www.facebook.com/username/

MainActivity.java
public class MainActivity extends Activity {
XMPPConnection xmpp;
ArrayList<HashMap<String, String>> friends_list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Session.openActiveSession(this, true, new StatusCallback() {
#Override
public void call(Session session, SessionState state, Exception exception) {
if ( session.isOpened()){
new testLoginTask().execute();
}
}
});
}
private class testLoginTask extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
testLogin();
return null;
}
}
private void testLogin(){
ConnectionConfiguration config = new ConnectionConfiguration("chat.facebook.com", 5222);
config.setSASLAuthenticationEnabled(true);
config.setSecurityMode(ConnectionConfiguration.SecurityMode.enabled);
config.setDebuggerEnabled(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
config.setTruststoreType("AndroidCAStore");
config.setTruststorePassword(null);
config.setTruststorePath(null);
} else {
config.setTruststoreType("BKS");
String path = System.getProperty("javax.net.ssl.trustStore");
if (path == null)
path = System.getProperty("java.home") + File.separator + "etc"
+ File.separator + "security" + File.separator
+ "cacerts.bks";
config.setTruststorePath(path);
}
xmpp = new XMPPConnection(config);
SASLAuthentication.registerSASLMechanism("X-FACEBOOK-PLATFORM",SASLXFacebookPlatformMechanism.class);
SASLAuthentication.supportSASLMechanism("X-FACEBOOK-PLATFORM", 0);
try {
xmpp.connect();
Log.i("XMPPClient","Connected to " + xmpp.getHost());
} catch (XMPPException e1) {
Log.i("XMPPClient","Unable to " + xmpp.getHost());
e1.printStackTrace();
}
try {
String apiKey = Session.getActiveSession().getApplicationId();
String sessionKey = Session.getActiveSession().getAccessToken();
String sessionSecret = "replace with your app secret key";
xmpp.login(apiKey + "|" + sessionKey, sessionSecret , "Application");
Log.i("XMPPClient"," its logined ");
Log.i("Connected",""+xmpp.isConnected());
if ( xmpp.isConnected()){
Presence presence = new Presence(Presence.Type.available);
xmpp.sendPacket(presence);
}
} catch (XMPPException e) {
e.printStackTrace();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Session.getActiveSession().onActivityResult(this, requestCode, resultCode, data);
}
SASLXFacebookPlatformMechanism.java
public class SASLXFacebookPlatformMechanism extends SASLMechanism{
private static final String NAME = "X-FACEBOOK-PLATFORM";
private String apiKey = "";
private String accessToken = "";
/**
* Constructor.
*/
public SASLXFacebookPlatformMechanism(SASLAuthentication saslAuthentication) {
super(saslAuthentication);
}
#Override
protected void authenticate() throws IOException, XMPPException {
getSASLAuthentication().send(new AuthMechanism(NAME, ""));
}
#Override
public void authenticate(String apiKey, String host, String accessToken) throws IOException, XMPPException {
if (apiKey == null || accessToken == null) {
throw new IllegalArgumentException("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 decodedChallenge = new String(Base64.decode(challenge));
Map<String, String> parameters = getQueryMap(decodedChallenge);
String version = "1.0";
String nonce = parameters.get("nonce");
String method = parameters.get("method");
long callId = new GregorianCalendar().getTimeInMillis();
String composedResponse = "api_key="
+ URLEncoder.encode(apiKey, "utf-8") + "&call_id=" + callId
+ "&method=" + URLEncoder.encode(method, "utf-8")
+ "&nonce=" + URLEncoder.encode(nonce, "utf-8")
+ "&access_token="
+ URLEncoder.encode(accessToken, "utf-8") + "&v="
+ URLEncoder.encode(version, "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<String, String> getQueryMap(String query) {
Map<String, String> map = new HashMap<String, String>();
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;
}
}

Related

Use apache httpclient as backend for jersey client base on PoolingHttpClientConnectionManager

i try to use apache httpclient as backend for jersey client to handle cookie automatically and here is my code
class ClientHelper {
public static HttpClientConnectionManager customConnectionManager() throws Exception {
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]{new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s)
throws CertificateException {
System.out.println("========checkClientTrusted=========");
}
#Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s)
throws CertificateException {
System.out.println("========checkServerTrusted==========");
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}}, new SecureRandom());
SSLConnectionSocketFactory
sslConnectionSocketFactory =
new SSLConnectionSocketFactory(sslContext);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("https", sslConnectionSocketFactory)
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager phcc = new PoolingHttpClientConnectionManager(registry);
return phcc;
}
public static Client createClient() {
HttpClient apacheClient = null;
try {
apacheClient =
HttpClientBuilder.create().setConnectionManager(customConnectionManager()).build();
} catch (Exception e) {
e.printStackTrace();
}
Client client = new Client(new ApacheHttpClient4Handler(apacheClient,
new BasicCookieStore(),
true));
return client;
}
}
I try to use the apache httpclient as the backend of jersey client (in order to handle the cookie)
Then, I create a simple class to test client,
import com.sun.jersey.api.client.Client;
....
public class ApiTest {
private static Client client;
public String getAuth(String username, String passwd) {
Map<String, String> formParams = new HashMap<String, String>();
formParams.put("username", String.valueOf(username));
formParams.put("passwd", String.valueOf(passwd));
try {
String basePath = "https://xyzhost/login";
if (client == null) {
client = ClientHelper.createClient();
client.addFilter(new LoggingFilter(System.out));
}
WebResource webResource = client.resource(basePath);
ClientResponse response = webResource.type("application/x-www-form-urlencoded").accept("application/json")
.post(ClientResponse.class, processFormParams(formParams));
if (response != null) {
String authRes = response.getEntity(String.class);
response.close();
return authRes;
} else {
return null;
}
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
public String getSummary(){
try {
String basePath = "https://xyzhost/summary";
if (client == null) {
client = ClientHelper.createClient();
}
WebResource webResource = client
.resource(basePath);
ClientResponse response = webResource.type("application/x-www-form-urlencoded").accept("application/json")
.post(ClientResponse.class, processFormParams(formParams));
if (response != null) {
String serviceRes = response.getEntity(String.class);
response.close();
return serviceRes;
} else {
return null;
}
} catch (Exception ex) {
ex.printStackTrace();
return null;
}
}
public static void main(String[] args) throws ApiException {
String username = "testuser";
String passwd = "testpasswd";
AuthApi apiTest = new ApiTest();
String auth =apiTest.getAuth(username, passwd);
String reslut1 = apiTest.getSummary();
String result2 = apiTest.getSummary();
String result3 = apiTest.getSummary();
String result4 = apiTest.getSummary();
}
}
So, I use the same client to hit the service under the same host.
I can success get the response for "auth" and "result1" but the client stuck in "result2" in the following part
ClientResponse response = webResource.type("application/x-www-form-urlencoded").accept("application/json")
.post(ClientResponse.class, processFormParams(formParams));
I try to modify the following part:
PoolingHttpClientConnectionManager phcc= new PoolingHttpClientConnectionManager(registry);
phcc.setDefaultMaxPerRoute(10);
then, ApiTest works, won't stuck. I guess there is some issue about the connection, since by default, the max per route for poolingHttpClientConnectionManager is 2, so my ApiTest will stuck in the 3rd request. I think the connection has been release since I have consume the response entity ,
if (response != null) {
String serviceRes = response.getEntity(String.class);
response.close();
return serviceRes;
}
but it seems not work at all, the connection seems not released.
anyone can help ? Appreciate!
I get one solution: switch the version of jersey-client from 1.17 to 1.18 , then problem solved!
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.18</version>
<scope>compile</scope>
</dependency>

How do I implement facebook chat using aSmack XMPP library in Android (2014)? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I searched many places to find ways to integrate facebook chat into Android/Smack but none of them worked completely. Could someone provide a working example that works with the latest version of the API?
Authenticating using X-FACEBOOK-PLATFORM SASLAuthentication. Verified working on 14 Jan 2014 with Android 4.2.2.
The jabber ID is not username#chat.facebook.com. It is resolved to a numeric id that you can check against the roster.
ChatActivity.java
public void connectToFb() throws XMPPException {
ConnectionConfiguration config = new ConnectionConfiguration("chat.facebook.com", 5222);
SASLAuthentication.registerSASLMechanism("X-FACEBOOK-PLATFORM",SASLXFacebookPlatformMechanism.class);
SASLAuthentication.supportSASLMechanism("X-FACEBOOK-PLATFORM", 0);
config.setSASLAuthenticationEnabled(true);
config.setSecurityMode(SecurityMode.required);
config.setSendPresence(false);
XMPPConnection xmpp = new XMPPConnection(config);
try {
xmpp.connect();
xmpp.login(Session.getActiveSession().getApplicationId(), Session.getActiveSession().getAccessToken(), "Application");
//send a chat message
ChatManager chatmanager = xmpp.getChatManager();
Chat newChat = chatmanager.createChat("<jabber-id-here>#chat.facebook.com", new MessageListener() {
#Override
public void processMessage(Chat chat, Message msg) {
Log.d("test", "message sent = "+ msg);
}
});
newChat.sendMessage("Cowdy!");
//get roster
Roster roster = xmpp.getRoster();
Collection<RosterEntry> entries = roster.getEntries();
System.out.println("Connected!");
System.out.println("\n\n" + entries.size() + " buddy(ies):");
for (RosterEntry entry : entries) {
Log.i("test", entry.getName());
Log.i("test", entry.getUser());
}
} catch (XMPPException e) {
xmpp.disconnect();
e.printStackTrace();
}
}
SASLXFacebookPlatformMechanism.java
public class SASLXFacebookPlatformMechanism extends SASLMechanism {
public static final String NAME = "X-FACEBOOK-PLATFORM";
private String apiKey = "";
private String accessToken = "";
/**
* Constructor.
*/
public SASLXFacebookPlatformMechanism(SASLAuthentication saslAuthentication) {
super(saslAuthentication);
}
#Override
protected void authenticate() throws IOException, XMPPException {
// Send the authentication to the server
getSASLAuthentication().send(new AuthMechanism(getName(), ""));
}
#Override
public void authenticate(String apiKey, String host, String accessToken) throws IOException, XMPPException {
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
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<String, String> parameters = getQueryMap(decodedChallenge);
String version = "1.0";
String nonce = parameters.get("nonce");
String method = parameters.get("method");
long callId = new GregorianCalendar().getTimeInMillis() / 1000L;
String composedResponse = "api_key=" + URLEncoder.encode(apiKey, "utf-8")
+ "&call_id=" + callId
+ "&method=" + URLEncoder.encode(method, "utf-8")
+ "&nonce=" + URLEncoder.encode(nonce, "utf-8")
+ "&access_token=" + URLEncoder.encode(accessToken, "utf-8")
+ "&v=" + URLEncoder.encode(version, "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<String, String> getQueryMap(String query) {
Map<String, String> map = new HashMap<String, String>();
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;
}
}
Authentication flow using SASL/Plain (when username and password are supplied)
Refer to Android Facebook chat example project

Testing Intuit IPP

I would like to create some unit tests for inserting data to QuickBooks Online. I am having a problem with the authentication step:
public DataServices Authenticate(IntuitServicesType intuitDataServicesType)
{
DataServices dataServices = null;
string accessToken = HttpContext.Current.Session["accessToken"].ToString();
string accessTokenSecret = HttpContext.Current.Session["accessTokenSecret"].ToString();
string companyID = HttpContext.Current.Session["realm"].ToString();
// now auth to IA
OAuthRequestValidator oauthValidator = new OAuthRequestValidator(accessToken, accessTokenSecret, ConfigurationManager.AppSettings["consumerKey"].ToString(), ConfigurationManager.AppSettings["consumerSecret"].ToString());
ServiceContext context = new ServiceContext(oauthValidator, accessToken, companyID, intuitDataServicesType);
dataServices = new DataServices(context);
if (HttpContext.Current != null && HttpContext.Current.Session != null)
{
HttpContext.Current.Session["DataServices"] = dataServices;
}
return dataServices;
}
In my unit test project, which has no user interface, how can I obtain an access token and an access token secret? I cannot log into Intuit from that area.
[TestMethod()]
public void AuthorizeWithHeadersTest()
{
string accessToken = ConfigurationManager.AppSettings["AccessTokenQBD"];
string accessTokenSecret = ConfigurationManager.AppSettings["AccessTokenSecretQBD"];
string consumerKey = ConfigurationManager.AppSettings["ConsumerKeyQBD"];
string consumerKeySecret = ConfigurationManager.AppSettings["ConsumerSecretQBD"];
string requestUri = "https://appcenter.intuit.com/Developer/Create";
WebRequest webRequest = WebRequest.Create(requestUri);
webRequest.Headers.Add("ContentType", "text/xml");
OAuthRequestValidator target = new OAuthRequestValidator(accessToken, accessTokenSecret, consumerKey, consumerKeySecret);
target.Authorize(webRequest, string.Empty);
Assert.IsTrue(webRequest.Headers.Count > 0);
}
I'm sharing a sample standalone java code snippet. You can try the same in .net
From appcenter, you can create an app to get consumer key, consumer secret and app token.
Using apiexplorer and the above consumer key, consumer secret, you can get access tokens.
AppCenter - https://appcenter.intuit.com/
Apiexplorer - https://developer.intuit.com/apiexplorer?apiname=V2QBO
You can set all the 5 values in the standalone program(setupQBO method). It will work fine.
import java.util.ArrayList;
import java.util.List;
import com.intuit.ds.qb.PartyType;
import com.intuit.ds.qb.QBCustomer;
import com.intuit.ds.qb.QBCustomerService;
import com.intuit.ds.qb.QBInvalidContextException;
import com.intuit.ds.qb.QBObjectFactory;
import com.intuit.ds.qb.QBServiceFactory;
import com.intuit.platform.client.PlatformSessionContext;
import com.intuit.platform.client.PlatformServiceType;
import com.intuit.platform.client.security.OAuthCredentials;
import org.slf4j.Logger;
// QBO API Docs - https://developer.intuit.com/docs/0025_quickbooksapi/0050_data_services/v2/0400_quickbooks_online/Customer
// JavaDocs - http://developer-static.intuit.com/SDKDocs/QBV2Doc/ipp-java-devkit-2.0.10-SNAPSHOT-javadoc/
public class CodegenStubCustomerall {
static String accesstoken = "";
static String accessstokensecret = "";
static String appToken = "";
static String oauth_consumer_key = "";
static String oauth_consumer_secret = "";
static String realmID = "";
static String dataSource = "";
final PlatformSessionContext context;
public CodegenStubCustomerall(PlatformSessionContext context) {
this.context = context;
}
public void testAdd(){
try {
QBCustomer entityPojo = QBObjectFactory.getQBObject(context, QBCustomer.class);
entityPojo.setName("TestQBCustomer12345");
entityPojo.setTypeOf(PartyType.PERSON);
QBCustomerService service = QBServiceFactory.getService(context, QBCustomerService.class);
QBCustomer qbQBCustomer = service.addCustomer(context, entityPojo);
} catch (QBInvalidContextException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public List<QBCustomer> testGetAll() {
final List<QBCustomer> entityList = new ArrayList<QBCustomer>();
try {
QBCustomerService service = QBServiceFactory.getService(context, QBCustomerService.class);
List<QBCustomer> qbCustomerList = service.findAll(context, 1,100);
for (QBCustomer each : qbCustomerList) {
entityList.add(each);
}
} catch (QBInvalidContextException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return entityList;
}
public static void main(String args[]) {
PlatformSessionContext context = getPlatformContext("QBO");
CodegenStubCustomerall testObj = new CodegenStubCustomerall(context);
testObj.testGetAll();
}
public static PlatformSessionContext getPlatformContext(String dataSource) {
PlatformServiceType serviceType = null;
if (dataSource.equalsIgnoreCase("QBO")) {
serviceType = PlatformServiceType.QBO;
setupQBO();
}
final OAuthCredentials oauthcredentials = new OAuthCredentials(
oauth_consumer_key, oauth_consumer_secret, accesstoken,
accessstokensecret);
final PlatformSessionContext context = new PlatformSessionContext(
oauthcredentials, appToken, serviceType, realmID);
return context;
}
private static void setupQBO() {
System.out.println("QBO token setup");
accesstoken = "replace your tokens";
accessstokensecret = "replace your tokens";
appToken = "replace your tokens";
oauth_consumer_key = "replace your tokens";
oauth_consumer_secret = "replace your tokens";
realmID = "7123456720";
dataSource = "QBO";
}
}
For sample .net code, you can refer this link.
https://developer.intuit.com/docs/0025_quickbooksapi/0055_devkits/0100_ipp_.net_devkit/0299_synchronous_calls/0001_data_service_apis
Thanks

PayPal roundtrip sandbox testing

I am looking for a way for the PayPal sandbox for a round trip test:
create a payment and redirect the user to PayPal so he can log in and approve
user follow the redirect and log in and approve the payment
verify payment on shop side
The steps 1. and 3. are not the problem. But how can I approve the payment automatically in the sand box. IMHO this is a scenario which every developer should need for automatic regression testing but I could not find any solution.
I use Java JUnit for regression tests.
I have tried with WebClient, but PayPal nags about cookies and JavaScript. So I got no success with that.
With the following code I could log in into PayPal developer page. Maybe for someone else this is useful.
package paypaltest;
import java.io.IOException;
public class htmlUnitTest extends TestCase {
#Test
public void test() {
final WebClient wc = new WebClient(BrowserVersion.FIREFOX_10);
wc.getCookieManager().setCookiesEnabled(true);
wc.setJavaScriptEnabled(false);
wc.setWebConnection(new TestHttpWebConnection(wc));
try {
final HtmlPage page = wc.getPage("https://developer.paypal.com/");
System.out.println(page.asXml());
final List<HtmlAnchor> anchors = page.getAnchors();
HtmlAnchor loginAnchor = null;
for (final HtmlAnchor htmlAnchor : anchors) {
if (htmlAnchor.getHrefAttribute().startsWith("https://www.paypal.com/webapps/auth/protocol/openidconnect/v1/authorize?client_id")) {
loginAnchor = htmlAnchor;
break;
}
}
if (loginAnchor != null) {
System.out.println("### login anchor");
System.out.println(loginAnchor.asXml());
System.out.println("### login to: " + loginAnchor.getHrefAttribute());
final HtmlPage loginPage = wc.getPage(loginAnchor.getHrefAttribute());
System.out.println("### login page");
System.out.println(loginPage.asXml());
final HtmlForm loginForm = loginPage.getForms().get(0);
final HtmlInput email = loginForm.getInputByName("email");
final HtmlInput password = loginForm.getInputByName("password");
final HtmlInput login = loginForm.getInputByValue("Log In");
email.setValueAttribute("my#email.com");
password.setValueAttribute("password");
final HtmlPage loggedInPage = login.click();
System.out.println("### logged in page");
System.out.println(loggedInPage.asXml());
}
final HtmlPage pageLoggedIn = wc.getPage("https://developer.paypal.com/");
System.out.println("### page logged in ");
System.out.println(pageLoggedIn.asXml());
} catch (final FailingHttpStatusCodeException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (final MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (final IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static final class EasySSLSocketFactory implements SchemeLayeredSocketFactory {
// ** Log object for this class. *//*
private static final Log LOG = LogFactory.getLog(EasySSLSocketFactory.class);
private SSLContext sslcontext = null;
public Socket createSocket(final HttpParams params) throws IOException {
final SSLSocket sock = (SSLSocket) getSSLContext().getSocketFactory().createSocket();
return sock;
}
public Socket createLayeredSocket(final Socket socket, final String target, final int port, final HttpParams params) throws IOException,
UnknownHostException {
final SSLSocket sslSocket = (SSLSocket) getSSLContext().getSocketFactory().createSocket(socket, target, port, true);
// verifyHostName() didn't blowup - good!
return sslSocket;
}
public Socket connectSocket(final Socket socket, final InetSocketAddress remoteAddress, final InetSocketAddress localAddress, final HttpParams params)
throws IOException, UnknownHostException, ConnectTimeoutException {
if (remoteAddress == null)
throw new IllegalArgumentException("Remote address may not be null");
if (params == null)
throw new IllegalArgumentException("HTTP parameters may not be null");
final Socket sock = socket != null ? socket : getSSLContext().getSocketFactory().createSocket();
if (localAddress != null) {
sock.setReuseAddress(HttpConnectionParams.getSoReuseaddr(params));
sock.bind(localAddress);
}
final int connTimeout = HttpConnectionParams.getConnectionTimeout(params);
final int soTimeout = HttpConnectionParams.getSoTimeout(params);
try {
sock.setSoTimeout(soTimeout);
sock.connect(remoteAddress, connTimeout);
} catch (final SocketTimeoutException ex) {
throw new ConnectTimeoutException("Connect to " + remoteAddress + " timed out");
}
String hostname;
if (remoteAddress instanceof HttpInetSocketAddress) {
hostname = ((HttpInetSocketAddress) remoteAddress).getHttpHost().getHostName();
} else {
hostname = remoteAddress.getHostName();
}
SSLSocket sslsock;
// Setup SSL layering if necessary
if (sock instanceof SSLSocket) {
sslsock = (SSLSocket) sock;
} else {
final int port = remoteAddress.getPort();
sslsock = (SSLSocket) getSSLContext().getSocketFactory().createSocket(sock, hostname, port, true);
}
return sslsock;
}
public boolean isSecure(final Socket sock) throws IllegalArgumentException {
if (sock == null)
throw new IllegalArgumentException("Socket may not be null");
// This instanceof check is in line with createSocket() above.
if (!(sock instanceof SSLSocket))
throw new IllegalArgumentException("Socket not created by this factory");
// This check is performed last since it calls the argument object.
if (sock.isClosed())
throw new IllegalArgumentException("Socket is closed");
return true;
}
private static SSLContext createEasySSLContext() {
try {
final SSLContext context = SSLContext.getInstance("SSL");
context.init(null, new TrustManager[] { new EasyX509TrustManager(null) }, null);
return context;
} catch (final Exception e) {
LOG.error(e.getMessage(), e);
throw new HttpClientError(e.toString());
}
}
private SSLContext getSSLContext() {
if (this.sslcontext == null) {
this.sslcontext = createEasySSLContext();
}
return this.sslcontext;
}
}
public static final class TestHttpWebConnection extends HttpWebConnection {
public TestHttpWebConnection(final WebClient webClient) {
super(webClient);
final SchemeRegistry schemeRegistry = getHttpClient().getConnectionManager().getSchemeRegistry();
final SchemeSocketFactory socketFactory = new EasySSLSocketFactory();
schemeRegistry.register(new Scheme("https", 443, socketFactory));
}
}
}

X-FACEBOOK-PLATFORM authentication with SMACK Java library using OAuth 2.0

First post here so please be gentle.
I'm building a facebook chat client, using Smack library.
I'm using X-FACEBOOK-PLATFORM method in order not to save any passwords. I had it working properly using oauth 1.0, and want to change it to 2.0, cause of the october 1st deadline ;p. From what I understand the only thing I'd have to do in order to migrate to 2.0 is removing "sig" and "session_key" parameters and adding an "access_token" parameter, but I'm getting an "SASL authentication X-FACEBOOK-PLATFORM failed: not-authorized:".
I'm using this custom SASLMechanism class:
package smackresearching;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import 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 {
public static final String NAME = "X-FACEBOOK-PLATFORM";
private String apiKey = "";
private String accessToken = "";
/**
* Constructor.
*/
public SASLXFacebookPlatformMechanism(SASLAuthentication saslAuthentication) {
super(saslAuthentication);
}
#Override
protected void authenticate() throws IOException, XMPPException {
// Send the authentication to the server
getSASLAuthentication().send(new AuthMechanism(getName(), ""));
}
#Override
public void authenticate(String apiKey, String host, String accessToken) throws IOException, XMPPException {
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
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<String, String> parameters = getQueryMap(decodedChallenge);
String version = "1.0";
String nonce = parameters.get("nonce");
String method = parameters.get("method");
long callId = new GregorianCalendar().getTimeInMillis() / 1000L;
String composedResponse = "api_key=" + URLEncoder.encode(apiKey, "utf-8")
+ "&call_id=" + callId
+ "&method=" + URLEncoder.encode(method, "utf-8")
+ "&nonce=" + URLEncoder.encode(nonce, "utf-8")
+ "&access_token=" + URLEncoder.encode(accessToken, "utf-8")
+ "&v=" + URLEncoder.encode(version, "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<String, String> getQueryMap(String query) {
Map<String, String> map = new HashMap<String, String>();
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;
}
}
And this is the Main code:
ConnectionConfiguration config = new ConnectionConfiguration("chat.facebook.com", 5222);
config.setSASLAuthenticationEnabled(true);
// config.setDebuggerEnabled(true);
XMPPConnection connection = new XMPPConnection(config);
try {
//ESTA LINEA HACE QUE NO DE TIMEOUT
SmackConfiguration.setPacketReplyTimeout(15000);
XMPPConnection.DEBUG_ENABLED = true;
SASLAuthentication.registerSASLMechanism("X-FACEBOOK-PLATFORM", SASLXFacebookPlatformMechanism.class);
SASLAuthentication.supportSASLMechanism("X-FACEBOOK-PLATFORM", 0);
connection.connect();
String apiKey = "3290282390339";
String accessToken = "ADSJGFGFKDKSJKSJD0-43DKJSDJKSDKJSD094JJSDKJSDKJDSNDSKJEWEWNSDLkljdkjs";
connection.login(apiKey, accessToken);
...
Thanks in advance for any advice.
There is a missing ampersand for the access_token parameter in the composedResponse string. Is this a typo?
Could you post the authenticationText you are sending?