Facebook chat - X-FACEBOOK-PLATFORM authentication - android

I want to build an XMPP client on android, I've got it running perfect with authentication using Digest-MD-5, however when I try to convert it to X-FACEBOOK-PLATFORM it keeps failing.

So basically the X-FACEBOOK-PLATFORM authentication uses only a part of a access token. That is called the session key.
The access token is seperated by "|" characters, so you split the access token and only take the characters that are in the center. Refer below.
******|a681464febcefb8*-**|******
long callId = new GregorianCalendar().getTimeInMillis() / 1000L;
String sig = "api_key=" + apiKey
+ "call_id=" + callId
+ "method=" + method
+ "nonce=" + nonce
+ "session_key=" + sessionKey
+ "v=" + version
+ appSecret;
try {
sig = MD5(sig);
}
catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
}
String composedResponse = "api_key=" + URLEncoder.encode(apiKey, "utf-8")
+ "&call_id=" + callId
+ "&method=" + URLEncoder.encode(method, "utf-8")
+ "&nonce=" + URLEncoder.encode(nonce, "utf-8")
+ "&session_key=" + URLEncoder.encode(sessionKey, "utf-8")
+ "&v=" + URLEncoder.encode(version, "utf-8")
+ "&sig=" + URLEncoder.encode(sig, "utf-8");

I never got FB chat to work with my appSecret but used sessionSecret instead. You can get it using oldish REST API.
http://developers.facebook.com/docs/reference/rest/auth.promoteSession/
This way you can keep your appSecret as a secret. Also it's worth noticing X-FACEBOOK-PLATFORM authentication rarely succeeds on first try but requires 3-6 retries usually. Beats me why though as I'm using same session key and secret..

Related

OAuth2 authorization to 'My Anime List' not working

I am trying to authenticate a "My Anime List" user using Oauth2 (following this guide) for my Android application.
Step 1: getting the authorization token
Here, I am using a WebView to prompt the user for its username and password. This step seems to work as far as I can see.
private static final String REDIRECT_URL = "http://localhost/oauth";
private static final String CLIENT_ID = "9c..."; // omitted
private static final String OAUTH_BASE_URL = "https://myanimelist.net/v1/oauth2/";
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
/*
* Before you can authenticate a user, your client needs to generate a Code Verifier and a
* Code Challenge. A Code Verifier is a high-entropy, cryptographic, random string
* containing only the characters [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~".
* The length of the string must be between 43 and 128 characters.
*
* MAL only allows the plain transformation for the Code Challenge.
* In other words, it means that you have to set the Code Challenge equal to the
* Code Verifier.
*/
String codeChallenge = PKCEGenerator.generateVerifier(128);
webview = findViewById(R.id.login_webview);
webview.getSettings().setJavaScriptEnabled(true);
webview.setWebViewClient(new WebViewClient(){
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request){
Log.d(TAG, "Redirecting to: " + request.getUrl());
Uri url = request.getUrl();
if(url.toString().contains(REDIRECT_URL)){
String authorizationCode = url.getQueryParameter("code");
Log.d(TAG, "Received authorization code: " + authorizationCode);
webview.setVisibility(View.GONE);
getUserAccessToken(authorizationCode, codeChallenge);
}
return false;
}
});
authenticateMAL(codeChallenge);
}
private void authenticateMAL(String codeChallenge) {
Log.d(TAG, "Code challenge (" + codeChallenge.length() + "): " + codeChallenge);
String loginUrl = OAUTH_BASE_URL + "authorize" +
"?response_type=code" +
"&redirect_uri=" + REDIRECT_URL +
"&client_id=" + CLIENT_ID +
"&code_challenge=" + codeChallenge;
Log.d(TAG, "Login url: " + loginUrl);
webview.loadUrl(loginUrl);
}
As far as I can see, this works well. I am getting the authorizationCode as expected.
Step 2: Getting the user access token & refresh token
Here, I am using Mal4J for the next authentication step:
private void getUserAccessToken(String authorizationCode, String codeChallenge) {
Single.fromCallable(() -> {
MyAnimeListAuthenticator authenticator = new MyAnimeListAuthenticator(
CLIENT_ID, null, authorizationCode, codeChallenge);
return authenticator.getAccessToken();
})
.subscribeOn(Schedulers.io())
.doOnError(throwable -> {
Log.e(TAG, "Error while retrieving token!", throwable);
})
.onErrorComplete()
.subscribe(token -> {
Log.d(TAG, "--> access token: " + token.getToken());
Log.d(TAG, "--> refresh token: " + token.getRefreshToken());
});
}
Unfortunately, this results in the following error:
E/LoginActivity: Error while retrieving token!
com.kttdevelopment.mal4j.HttpException: Server returned code 400 from 'https://myanimelist.net/v1/oauth2/token':
at com.kttdevelopment.mal4j.MyAnimeListAuthenticator.parseToken(MyAnimeListAuthenticator.java:505)
at com.kttdevelopment.mal4j.MyAnimeListAuthenticator.<init>(MyAnimeListAuthenticator.java:139)
at florian.baierl.daily_anime_news.ui.LoginActivity.lambda$getUserAccessToken$0(LoginActivity.java:99)
at florian.baierl.daily_anime_news.ui.-$$Lambda$LoginActivity$-bBBIb9OKRzdaFNsFkQdJSeVW74.call(Unknown Source:4)
at io.reactivex.rxjava3.internal.operators.single.SingleFromCallable.subscribeActual(SingleFromCallable.java:43)
at io.reactivex.rxjava3.core.Single.subscribe(Single.java:4813)
at io.reactivex.rxjava3.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:89)
at io.reactivex.rxjava3.core.Scheduler$DisposeTask.run(Scheduler.java:614)
at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:65)
at io.reactivex.rxjava3.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:56)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
Any ideas as to why that may happen? Am I missing some Android specific stuff for Oauth2? As far as I can see, I am correctly retrieving the auth code from step 1. After that, my code seems very straight-forward, so I fail to see where the error could be. Any hints are greatly appreciated!
Edit:
This is how the request looks like (from the android studio profile view):
and here is the reply:
Edit 2:
Hard coding the code challenge/verifier to 128 times 'A' (AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA) doesn't change the behavior either:
When you include the redirect_uri in the authorization request you also need to include it in the /token request. Maybe it't that.

JTDS connect to remote server fail

don't know why, the app when connecting to the SQL Server that is on the same network as the device, I can connect to it without any problems, but now I need to connect from the device over the internet to the SQL Server, isn't working, don't know why. I have a button to test the connection and it will call a method that contains this, the method is on background (AsyncTaskRunner)
try
{
String host, port, dbname, user, password, instance;
host = _editTextHost.getText().toString();
port = _editTextPort.getText().toString();
instance = _editTextInstance.getText().toString();
dbname = _editTextDbName.getText().toString();
user = _editTextUser.getText().toString();
password = _editTextPass.getText().toString();
String driver = "net.sourceforge.jtds.jdbc.Driver";
String conString;
if (TextUtils.isEmpty(port))
{
conString = "jdbc:jtds:sqlserver://" + host + ";databaseName=" + dbname + ";instance=" + instance;
}
else
{
conString = "jdbc:jtds:sqlserver://" + host + ":" + port + ";databaseName=" + dbname + ";instance=" + instance;
}
Connection con;
Class.forName(driver);
con = DriverManager.getConnection(conString, user, password);
con.close();
conSuccess = true;
}
catch (Exception e)
{
e.printStackTrace();
Log.e("SQLConfig", "Fail to connect");
Log.e("SQLConfig", e.toString());
Log.e("SQLConfig", e.getMessage());
}
return null;
When I do try to connect to the SQL Server on the same network works without any problems, but when I activate the 4g on the device I allways get the same error, that it can't find the instance. But if I connect to the server through the "SQL Server Management Studio" using the same information I can connect to the server without any problems.
I'm using the jtds driver, 1.3.1.
What could be doing this? Thanks
P.S. I all rdy have read some stuff about webservice, but I want to remove this option for now out of the picture
Edit 1: To clarify, I can connect to the server using the credentials on the version of Windows CE of the program or SQL Server Management Studio. When I put the outside IP and all the require information it connects to the server, it not connect on the Android only
Well by changing the conString a little I was able to connect without any problems either from the localnetwork or the internet.
if (TextUtils.isEmpty(port))
{
conString = "jdbc:jtds:sqlserver://" + host + ";databaseName=" + dbname + ";instance=" + instance;
}
else
{
conString = "jdbc:jtds:sqlserver://" + host + ":" + port + ";databaseName=" + dbname + ";instance=" + instance;
}
To
if (TextUtils.isEmpty(port))
{
conString = "jdbc:jtds:sqlserver://" + host + "/" + instance + ";DatabaseName=" +dbname;
}
else
{
conString = "jdbc:jtds:sqlserver://" + host + ":" + port + "/" + instance + ";DatabaseName=" + dbname;
}
Now works without any problems either using the public host or the localnetwork to access the db.

Open url in another application or in browser [Titanium]

I am writing a functionality that needs to open a URL either in the another app [if installed in my phone] or else, in the browser.
To open the URL in browser, I can use Titanium.Platefor.openURL();
To open the app I am creating the intent.
var intent = Titanium.Android.createIntent({
packageName : appUrl,
action : Titanium.Android.ACTION_SEND,
data : url
});
intent.addCategory(Titanium.Android.CATEGORY_BROWSABLE);
Titanium.Android.currentActivity.startActivity(intent);
I have stuck in below things:
How to pass the url to other app to open - I tried passing url using url : 'http://someurl' and data: 'http://someurl' - but didn't help. I got the error: No Activity found to handle Intent
How to find out whether the app is install or not? If yes - ask for the application to open, if no - open the url in browser.
Can anyone help?
Thanks in advance!
You can identify app is install or not using URL schema with Titanium.Platefor.openURL(); method in android. (if app is not installed it will return false).
and for ios there is one method for identify Titanium.Platform.canOpenURL().
and also you can passed something value to application for example if you open google map application with source and destination lat long in ios then call like this
var strUrl = "http://maps.google.com/maps?saddr=" + Alloy.Globals.UserLocation.latitude + "," + Alloy.Globals.UserLocation.longitude + "&daddr=" + dLatitude + "," + dLongitude;
if (OS_IOS) {
strUrl = "comgooglemaps://?saddr=" + Alloy.Globals.UserLocation.latitude + "," + Alloy.Globals.UserLocation.longitude + "&daddr=" + dLatitude + "," + dLongitude + "&directionsmode=driving";
if (Titanium.Platform.canOpenURL(strUrl)) {
Ti.Platform.openURL(strUrl);
} else {
strUrl = "http://maps.google.com/maps?saddr=" + Alloy.Globals.UserLocation.latitude + "," + Alloy.Globals.UserLocation.longitude + "&daddr=" + dLatitude + "," + dLongitude;
Ti.Platform.openURL(strUrl);
}
} else {
var result = Ti.Platform.openURL(strUrl);
Ti.API.info('RESULT = ' + result);
}
one more example.. if you want opening whatsApp application with given message text.
var whatsappUrl = encodeURI('whatsapp://send?text=' + msgBody);
if (OS_IOS) {
if (Ti.Platform.canOpenURL(whatsappUrl)) {
Ti.Platform.openURL(whatsappUrl);
} else {
Ti.Platform.openURL("https://itunes.apple.com/ae/app/whatsapp-messenger/id310633997?mt=8");
}
} else {
var isSuccess = Ti.Platform.openURL(whatsappUrl);
if (!isSuccess) {
Ti.Platform.openURL("https://play.google.com/store/apps/details?id=com.whatsapp&hl=en");
}
}
Hop this is helps you.. :)
Thanks

Receiving HTTP Bad Request when calling a .NET Webservice using SOAP only from Android

EDIT: For anyone using the same method as I am (building a Soap.java file and using sockets), stop! Use the kSoap-Android library as it automates pretty much everything and has little to no bugs like this.
I'm creating an android application where you can quicly check your timeroster for the classes in the next week. To do this, I use 2 servers:
The server that hosts the timerosters for every student in HTML form
(I do not manage this server)
The server that hosts my webservice (which I manage). It's a .NET webservice and is finished.
The android device connects to the Webservice-server and sends the function "login" with parameters "username": "usernameX" and "password": "passwordX".
The webservice-server then retrieves the login-page from the timeroster-server with the "username" and "password" fields as post parameters. The timeroster-server will respond with a html-page, that will be parsed by the webservice-server and depending on it's contents, the webservice will respond to the Android device with either "Success" or "AuthenticationFail".
To do this, I use the SOAP-architecture on my Android Device, which sends a SOAP-enveloppe to the webservice. The webservice then creates HttpWebRequest- and HttpWebResponse-objects that retrieve the html-source from the timeroster-server.
This approach has worked for me so far using the Socket-class and I have been able to login to the timeroster-server, using my android device. However, the problem lies with another function.
My webservice supports 2 functions: "login" and "GetList". I can perfectly call the "login"-function from my android and receive a "Success"-string. But when I call the "GetList"-function (that takes 3 parameters: a username, a password and a listType), it returns a 400 Bad Request.
When I send the exact same http-request to the webservice-server with Fiddler, I don't receive a 400 Bad Request error.
Why am I getting a 400 error ONLY when I use my android-device? Is there any way I can fix this?
Images for visualisation:
Login-function (using Android):
Login-function (using Fiddler):
GetList-function (using Android) - ERROR HERE:
GetList-function (using Fiddler):
Code used in Android to send data to socket:
public String sendRequest()
{
String s = "";
Socket socket = null;
try
{
socket = new Socket(Server, Port);
}
catch(Exception ex)
{
return "UnknownClientError";
}
String stringbuffer = "";
try
{
socket.getOutputStream();
boolean flag = true;
PrintWriterSuper printwriter = new PrintWriterSuper(socket.getOutputStream());
Scanner scanner = new Scanner(socket.getInputStream());
int i = 295 + MethodName.length() * 2 + XmlNamespace.length();
for(int j = 0; j < ParamNames.size(); j++)
{
String s1 = (String)ParamNames.elementAt(j);
String s2 = (String)ParamData.elementAt(j);
i += s1.length();
i += s2.length();
}
printwriter.println("POST " + WebServicePath + " HTTP/1.1");
printwriter.println("Host: " + Server);
printwriter.println("Content-Type: text/xml; charset=utf-8");
printwriter.println("Content-Length: " + String.valueOf(i));
if(!SoapAction.equals(""))
printwriter.println("SOAPAction: \"" + SoapAction + "\"");
printwriter.println("Connection: Close");
printwriter.println();
printwriter.println("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
printwriter.println("<soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">");
printwriter.println("<soap:Body>");
printwriter.println("<" + MethodName + " xmlns=\"" + XmlNamespace + "\">");
for(int k = 0; k < ParamNames.size(); k++)
{
String s3 = (String)ParamNames.elementAt(k);
String s5 = (String)ParamData.elementAt(k);
printwriter.println("<" + s3 + ">" + s5 + "</" + s3 + ">");
}
printwriter.println("</" + MethodName + ">");
printwriter.println("</soap:Body>");
printwriter.println("</soap:Envelope>");
printwriter.println();
boolean flag1 = false;
int byte0 = 10;
long l = System.currentTimeMillis();
String s4;
while(scanner.hasNextLine() && !flag1)
{
s4 = scanner.nextLine();
stringbuffer += s4 + "\n";
if(System.currentTimeMillis() - l > (long)(1000 * byte0))
flag1 = true;
}
scanner.close();
if(!flag1)
{
String requestString = printwriter.toString();
String s6 = MethodName + "Result";
int i1 = stringbuffer.toString().indexOf("<" + s6 + ">") + s6.length() + 2;
int j1 = stringbuffer.toString().indexOf("</" + s6 + ">");
s = stringbuffer.substring(i1, j1);
} else
{
s = "Error: timed out by client";
}
try{
socket.close();
}catch(Exception ex){}
}
catch (Exception e) {
s = e.getMessage() + "\n" + e.getStackTrace().toString();
}
return s;
}

FB.api('/me') always giving error code:2500 in phonegap android

I am using facebook plugin to login and logout a user, which are working fine. The problem is when I request for the logged in user details using the function FB.api('/me'), it always gives the following error:
{"message":"An active access token must be used to query information about the current user.","type":"OAuthException","code":2500}
I used the debug mode to check PluginResult(pr) and JSONObject of the response. JSONObject contains the user information, which I required, I dont get where I am doing wrong.
Plz help......
MY CODE:
function login() {
FB.login(function(response) {
if (response.session) {
console.log('Welcome! Fetching your information.... ');
FB.api('/me', function(response) {
console.log('Good to see you, ' + JSON.stringify(response) + '.');
});
} else {
console.log('User cancelled login or did not fully authorize.');
}
},{scope: 'email,user_likes'});
}
function logout() {
FB.logout(function(response) {
console.log(localStorage.getItem("user_fb_log_status"));
localStorage.setItem("user_fb_log_status","LOGGED_OUT");
alert('logged out');
});
}
The above code is working fine to login and logout the user. Below is the code i used to get the user details,
function me() {
FB.api('/me', { fields: 'id, name, picture' }, function(response) {
if (response.error) {
alert(JSON.stringify(response.error));
} else {
var data = document.getElementById('data');
fdata=response.data;
console.log("fdata: "+fdata);
response.data.forEach(function(item) {
var d = document.createElement('div');
d.innerHTML = "<img src="+item.picture+"/>"+item.name;
data.appendChild(d);
});
}
});
}
You need access token to retrieve more details than basic user information. Check that whether you have correct access token in Debug Tool to and ensure that you have all require permissions set permission.
Problem solved after changing the "session" in 'getResponse' method in ConnectPlugin to "authResponse"
FB.api method is working fine for me to get the user details and post a feed to the facebook after I change the following method in ConnectPlugin.java as following.
public JSONObject getResponse() {
String response = "{" + "\"status\": \""
+ (facebook.isSessionValid() ? "connected" : "unknown") + "\","
+
// "\"session\": {" + "\"access_token\": \""
// + facebook.getAccessToken() + "\"," + "\"expires\": \""
// + facebook.getAccessExpires() + "\","
// + "\"session_key\": true," + "\"sig\": \"...\","
// + "\"uid\": \"" + this.userId + "\"" +
"\"authResponse\": {" +
"\"accessToken\": \"" + facebook.getAccessToken() + "\"," +
"\"expiresIn\": \"" + facebook.getAccessExpires() + "\"," +
"\"session_key\": true," +
"\"sig\": \"...\"," +
"\"userId\": \"" + this.userId + "\"" +
"}" + "}";
try {
return new JSONObject(response);
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return new JSONObject();
}

Categories

Resources