Make authorized request to Google Endpoint API from Android Client - android

I need to identify user who made request to my endpoint api, via Android client. Though I am able to follow best practice by keeping my Api and App within one project using gradle and android studio. Also I am able to send request to my endpoint api and receive response without authorization.
Basically I need to send authorization token as header in the request, people suggest that, merely by adding instance of "GoogleAccountCredential" along with the request will do the trick as in the code below. The class in which below code is present that extends android.os.AsyncTask; I have been following https://cloud.google.com/appengine/docs/java/endpoints/consume_android#using_the_account_picker, but code fragments are not very clear.
#Override
protected String doInBackground(Pair<Context, String>... params) {
..
MyApi.Builder builder = new MyApi.Builder(AndroidHttp.newCompatibleTransport(), new AndroidJsonFactory(), credential).setRootUrl("https://myapp.appspot.com/_ah/api/");
..
What I have :
I have an Activity called ExpandedListViewActivity
another thing is ExpandedListAdaptor, which populates views dynamically ( form ).
My Objective
When User clicks on submit present in the form.
Android should be able to find the google account and its credentials and attach it with the request.
If it does not find then show account selector view, so that user can select account, if we can do it silently without user consent that would be very nice.
Extra methods that I have in :
class EndpointsAsyncTask extends AsyncTask, Void,
String>
void chooseAccount() {
mActivity.startActivityForResult(credential.newChooseAccountIntent(),
REQUEST_ACCOUNT_PICKER);
}
protected String fetchToken() throws IOException {
try {
return GoogleAuthUtil.getToken(mActivity, mEmail, mScope);
} catch (UserRecoverableAuthException userRecoverableException) {
// GooglePlayServices.apk is either old, disabled, or not present
// so we need to show the user some UI in the activity to recover.
userRecoverableException.printStackTrace();
} catch (GoogleAuthException fatalException) {
// Some other type of unrecoverable exception has occurred.
// Report and log the error as appropriate for your app.
}
return null;
}
public void getSettings(){
Log.d(APP, "get Settings ");
settings = mActivity.getSharedPreferences("Api", 0);
credential = GoogleAccountCredential.usingAudience(mActivity,
"server:client_id:Android-clientId.apps.googleusercontent.com");
setSelectedAccountName(settings.getString(PREF_ACCOUNT_NAME, null));
}
// setSelectedAccountName definition
private void setSelectedAccountName(String accountName) {
SharedPreferences.Editor editor = settings.edit();
editor.putString(PREF_ACCOUNT_NAME, accountName);
editor.commit();
credential.setSelectedAccountName(accountName);
this.accountName = accountName;
}
Please take into account that my endpoint server side is properly configured and running.
It should be straight forward, but I am not able to solve this, Please point the mistake or show me a direction to solve this..
Thanks for reading.
Shashank

This solves my problem.
http://android-developers.blogspot.in/2013/01/verifying-back-end-calls-from-android.html
https://cloud.google.com/appengine/docs/java/endpoints/auth
Specify the client IDs (clientIds) of apps authorized to make requests to your API backend.
Add a User parameter to all exposed methods to be protected by authorization.
Generate the client library again for any Android clients
Redeploy your backend API. <-- This was the key, to solve this problem.
Thanks,
Shashank

Related

Connecting to Azure Blob Storage from Android

I'm trying to make a mobile app that, eventually in one of its activities will connect to a Blob storage in Android to transfer a collection of images.
But I couldnt even get there, since one of the requirements is making that connection to be safe, since the client could be anyone from his mobile phone, so my first step here is requesting a SAS token prior to start any transaction.
So this is basically two steps that I'm following,
1 -> Implemented an Azure Function inside my App Service that returns a SAS token (I got that function from here: Sas Token Function
)
2 -> Trying to call that function from my Android Code and get my SAS token.
Looks really easy and I'm sure it is, the function in the link explains the required http body to ask for concrete access, and I think is there where I'm failing, below is my code to call the function:
private void getStringFromAzure() throws MalformedURLException {
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("container", "uploadedimages");
jsonObject.addProperty("permissions", "Write, Create");
// Create the MobileService Client object and set your backend URL
String yourURL = "https://mydirectory.azurewebsites.net/";
MobileServiceClient mClient = new MobileServiceClient(yourURL, this);
// Your query pointing to yourURL/api/values
ListenableFuture<JsonElement> query = mClient.invokeApi("GetSasToken-Net", jsonObject, "POST", null);
// Callback method
Futures.addCallback(query, new FutureCallback<JsonElement>() {
#Override
public void onSuccess(JsonElement jsonElement) {
final String result = jsonElement.toString();
// Since you are on a async task, you need to show the result on the UI thread
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(mContext, result, Toast.LENGTH_LONG).show();
}
});
}
#Override
public void onFailure(Throwable throwable) {
Log.d(TAG, "onFailure: " + throwable.getMessage());
}
});
}
The code is failing in "InvokeApi" line function, where throws an exception before even warning me that is going to connect to Internet from mobile.
Another thing that I think could be wrong is that, nowhere in the Azure Get Sas Token Function is specifying my account credentials ( I didnt developed that function, but should work fine as it is, it anyways doesnt let you change anything, imported via GitHub )
I have really small base/background in this kind of things and Im sure I'm missing something (or "lots" of somethings), but I really appreciate a hand, this is driving me crazy.
PD. Internet permissions already given in manifest.
Thank you all in advance, first post in StackOverflow after following for many years!

How and when is the token refreshed when using BMS Client, and is there a way to force the refresh?

We are using:
BMSClient.getInstance().registerAuthenticationListener("realm", new CustomAuthentication(this));
and:
AuthorizationManager.createInstance(this.getApplicationContext());
AuthorizationManager.getInstance().setAuthorizationPersistencePolicy(AuthorizationManager.PersistencePolicy.ALWAYS);
to store the authorization data on the phone. "The authorization data will be saved on local storage" is set to ALWAYS.
The code above is always run on our splashscreen on startup so it is always run when the app restarted.
The problem we have had is that after some time (hours or days) when what we suspect the token has expired we get response in the form of HTTP 307. Even after restarting the app we keep getting this response on our requests. The only way to get around it is to go into the app from settings and clear all data.
The following questions would help us go forward in our testing and possible solution:
How long is the token cached in BMSClient? (testing purposes)
Can AuthorizationManager help us in any way to force a new fetch of token?
Are they working on log out functionality?
Our custom listener:
public class CustomAuth implements AuthenticationListener {
private Context activityContext;
public CustomAuth(Context activityContext) {
this.activityContext = activityContext;
}
#Override
public void onAuthenticationChallengeReceived(AuthenticationContext authContext, JSONObject challenge, Context context) {
//1. read the challenge JSONObject
//2. handle the challenge (use the context for handling UI based operations)
//3. return response using the AuthenticationContext authContext
SharedPreferences preferences = activityContext.getSharedPreferences("UserPreference", Context.MODE_PRIVATE);
String email = preferences.getString("email", "");
if(email.equals("")) {
email = "unidentified-user#error.com";
}
JSONObject jsonEmail = new JSONObject();
try {
jsonEmail.put("email", email);
} catch (JSONException e) {
authContext.submitAuthenticationChallengeAnswer(null);
}
authContext.submitAuthenticationChallengeAnswer(jsonEmail);
}
#Override
public void onAuthenticationSuccess(Context context, JSONObject info) {
//additional operations in case of authentication success
Log.d("Authentication", "Auth success: " + String.valueOf(info));
}
#Override
public void onAuthenticationFailure(Context context, JSONObject info) {
//additional operations in case of authentication failure
Log.d("Authentication", "Auth failure ." + String.valueOf(info));
}
}
Have you tried using AuthorizationManager.clearAuthorizationData() API when 307 is received and resending the request?
To answer your questions:
1) The authorization token will be cached indefinitely. The token expires after a 60 minute time period, but will remain cached until a new token is obtained.
It is best practice to obtain a new token once the original token has expired. This can be accomplished by running a new authorization challenge once the previous token has expired.
2) You can always use AuthorizationManager to obtain a new token once the previous token has expired by accessing a protected resource, using obtainAuthorizationHeader, etc.
3) There is currently no way to log out of MCA using the AuthorizationManager. I will speak with the development team about future plans.
In regards to the main problem I see in your question. I would expect you are experiencing this problem because you are attempting to use an expired token against the authorization service. Even though the token is still cached on the device it expired an hour after creation. I would attempt to run a new authorization challenge against the MCA service once the token expires.
If you want to provide your code that may also help me investigate further.

Facebook for Android FacebookDialog.ShareDialogBuilder doesn't recognize URI returned by Request.newUploadStagingResourceWithImageRequest

I am relatively new to android and facebook so please bear with me. IMPORTANT NOTE: Wherever I type h.. that means http://www. I'm not intending to post links here but I have to in order to explain this (my permission only allows 2 links) so please bear with me.
This app does a facebook post using the FacebookDialog.ShareDialogBuilder. This all works great now IF the image for the post using the .setPicture method is given a static hardcoded URL h..example.com/share_name/image_name.png. In that case the post works and the picture shows up on the post and everything is fine.
However, the image sent to the post is dynamically created by the app. Therefore I am sending the image to facebook's staging area which also works fine.
The Request.newUploadStagingResourceWithImageRequest returns a response that has the JSON encoded URI of the location of
the image in facebook's staging area.
The problem is that the FacebookDialog.ShareDialogBuilder doesn't like that URI location. Somehow it's not formed properly or something or I'm just doing something wrong.
Here are the details and what I've tried:
1) uriMine, the location where the image gets stored, as it is originally returned from facebook's staging resource upload call is:
"fbstaging://graph.facebook.com/staging_resources/MDE4NTY0NzE4MDQ0MTUwNjA6MTM5ODI2Nzc3Ng==". I don't know what the protocol "fbstaging:" is all about (I searched and searched online but nothing) but I
ran the app as is with that at first. The result was, well, unpredictable results apparently, as it got stuck in a loop (the looper class kept repeating in no particular pattern). It would show the post screen but you couldn't type in a message as it would lock up, close, repeat etc...
2) After getting a little education online about well formed URL's I replaced the fbstaging:// with h.. and thus changed the uriMine variable to the following:
h..graph.facebook.com/staging_resources/MDE4NTY0NzE4MDQ0MTUwNjA6MTM5ODI2Nzc3Ng==
This solved the endless loop problem (made the post work fine) except it would not show any image.
3) To see if it would work with any old normal URL of the form h..blablabla.com/image_resource I hardcoded URL's of a few images online and it worked fine, and showed the images.
4) Ok, I promise, I'm all most done (whew!). So, where it stands right now is:
a) passing uriMine as fbstaging://graph.facebook.com/staging_resources/etc etc
makes it freak out.
b) sending a normal URL of an online resource works fine (formed as a browser forms it, by the way).
c) prepending http://www. instead of the fbstaging:// makes the post work but facebook doesn't show the image, as if it can't find it.
By the way, going directly to the above by copy/pasting it into a browser gets redirected to the following:
h..dnsrsearch.com/index.php?origURL=http%3A//www.graph.facebook.com/staging_resources/MDE4NTY0NzE4MDQ0MTUwNjA6MTM5ODI2Nzc3Ng%3D%3D&r=
as apparently it can't find it.
SO:
What is it about that URI that is wrong or what am I missing? Please help.
Thank you very much for your time and patience reading this.
public class FacebookActivity extends Activity {
// initialize the global object to enable passing of activity to facebook dialog
public GlobalClass globalObject = new GlobalClass();
private UiLifecycleHelper uiHelper; // for Facebook...to mimic android's activity life cycle
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
uiHelper = new UiLifecycleHelper(this, null);
uiHelper.onCreate(savedInstanceState);
// set the calling activity...to pass to Face book
globalObject.setCurrentActivity(this);
// start Facebook Login
Session currentSession = new Session(this);
currentSession = Session.openActiveSession(this, true, new Session.StatusCallback() {
// callback when session changes state
#Override
public void call(final Session session, SessionState state, Exception exception) {
// this callback should fire multiple times, be sure to get the right one i.e. session.isOpened()
if (session.isOpened()) {
// make request to the /me API
Request.newMeRequest(session, new Request.GraphUserCallback() {
// callback after Graph API response with user object
#Override
public void onCompleted(GraphUser user, Response response) {
if (user != null) {
Bitmap bitmap = takeScreenshot();
Request imageRequest = Request.newUploadStagingResourceWithImageRequest(Session.getActiveSession(), bitmap, new Request.Callback() {
#Override
public void onCompleted(Response response) {
String uriMine = "";
JSONObject data = response.getGraphObject().getInnerJSONObject();
try {
uriMine = data.getString("uri");
uriMine = "http://www." + uriMine.substring(12); // strip off the "fbstaging://" from the uri
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (FacebookDialog.canPresentShareDialog(getApplicationContext(),
FacebookDialog.ShareDialogFeature.SHARE_DIALOG))
{
FacebookDialog shareDialog = new FacebookDialog.ShareDialogBuilder(globalObject.getCurrentActivity())
.setLink("https://play.google.com/store")
.setPicture(uriMine)
.setRequestCode(NativeProtocol.DIALOG_REQUEST_CODE)
.setApplicationName("This is the App Name")
.setName("This is the name")
.setDescription("This is the description")
.setCaption("This is the caption")
.build();
uiHelper.trackPendingDialogCall(shareDialog.present());
}
else
{
Toast.makeText(globalObject.getCurrentActivity(), "Please install the Facebook App first from Google Play.", Toast.LENGTH_LONG).show();
}
}
});
imageRequest.executeAsync();
}
}
}).executeAsync();
}
}
});
}
The staging resource endpoint is only used for staging binary data for open graph objects or actions, and is not meant for the regular link shares. See the documentation here: https://developers.facebook.com/docs/reference/android/current/class/Request/#newUploadStagingResourceWithImageRequest
In this case, you can either use the PhotoShareDialogBuilder (but then you can't add a link), or you can upload the image to your own hosting service, and use an http/https url in the setPicture method.

Quickblox manage session and recalling?

I am working with QuickBlox library for video chat. How can i manage it session?? because when i move to the next activity from the live chat activity i just lost the session because it says "Chat can't initialized" then i have to create the session again to do the calling. So what's the lifetime of quickblox session and how can i manage that.
I am also facing problem with recalling when stop the call or move to the next activity and try to recall i was not able to do that actually i tried different things so each time i am getting different errors. So if any one has experience with QuickBlox library need help here.
When i stop a call i call this function.
private void stopCall() {
//Toggle view show the smile view again
//ToggleSmileView();
try
{
cancelCallTimer();
if (videoChat != null) {
videoChat.stopCall();
videoChat = null;
}
if (videoChannel != null) {
videoChannel.close();
videoChannel = null;
}
sessionId = null;
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
and when i do the call i call this function
private void call() {
//toggle view
//ToggleSmileView();
// get opponent
//
VideoChatApplication app = (VideoChatApplication)getApplication();
opponent = new QBUser();
opponent.setId((app.getCurrentUser().getId() == VideoChatApplication.FIRST_USER_ID ? VideoChatApplication.SECOND_USER_ID : VideoChatApplication.FIRST_USER_ID));
// call
//
callTimer = new Timer();
callTimer.schedule(new CancelCallTimerTask(), 30 * 1000);
createSenderChannel();
initVideoChat();
if (videoChat != null)
{
videoChat.call(opponent, getCallType(), 3000);
//toggleMicrophoneMute();
}
else
{
logAndToast("Stop current chat before call");
}
}
For: Lifetime of quickblox session and how can i manage that.
To authenticate your application you have to set valid a auth_key and
generate a signature using your application auth_secret and receive a
session token which you should use to send requests to QuickBlox API
And,
Expiration time for token is 2 hours. Please, be aware about it. If
you will perform query with expired token - you will receive error
Required session does not exist.
Source: Authentication and Authorization Session Info
That part fits the Android sample code of creating the session,
QBAuth.createSession(new QBEntityCallbackImpl<QBSession>() {
#Override
public void onSuccess(QBSession session, Bundle params) {
Log.i(TAG, "session created, token = " + session.getToken());
}
#Override
public void onError(List<String> errors) {
}
});
Source: Android developers documentation
I have worked with the Android SDK, and feel it still needs some work, esp to reach a stage equivalent to the iOS SDK and REST API.
Though looking at your code, you should use getToken() before creating the new QBUser and related video chat calls, if the token has expired, just create a new one.
I have implemented similar code, not a video chat application, but in a general manner, write the functions in onSuccess() of session creation if the session needs to be recreated.
Fyi, for the multiple ones, you can try checking the error with the summary that has been given, categorized into 4; ..developers/Errors

Android AccountManager API

I'm struggling to understand the Android AccountManager API. As far as I got thinks working I can use the blockingGetAuthToken method and specify whether Android should provide a notification for user to allow or deny the request. Another possibility is to use getAuthToken and check if KEY_INTENT is returned. If that's the case I could start a new Activity where the user can confirm my request.
My problem is that I would like to call one of these two methods from within a Service. Is there any chance to get a callback once the user has made a decision?
Thanks for your help
If you want a callback after the user has made a decision it's probably better to use the asynchronous version:
AccountManager mgr = AccountManager.get(getApplicationContext());
Account[] accounts = mgr.getAccountsByType("com.mydomain");
// assert that accounts is not empty
You'll want to use an AccountManagerFuture<Bundle> to hold results of the authentication token. This has to be async since the Android device may ask the user to login in the meantime:
private AccountManagerFuture<Bundle> myFuture = null;
private AccountManagerCallback<Bundle> myCallback = new AccountManagerCallback<Bundle>() {
#Override public void run(final AccountManagerFuture<Bundle> arg0) {
try {
myFuture.getResult().get(AccountManager.KEY_AUTHTOKEN); // this is your auth token
} catch (Exception e) {
// handle error
}
}
}
Now you can ask for the auth token asynchronously:
myFuture = mgr.getAuthToken(accounts[0], AUTH_TOKEN_TYPE, true, myCallback, null);

Categories

Resources