I have completed all operation related to syncAdapter but now i m stuck on one minor issue
Auth Token
After 2hr my token just expired and then i need to show user a dialog to enter his password again so that he can renew his token.
AccountManager.get(getContext()).getAuthToken(account, LoginActivity.ACCOUNT_TYPE, null, false, new AccountManagerCallback<Bundle>() {
#Override
public void run(AccountManagerFuture<Bundle> arg0) {
try {
arg0.getResult();
} catch (OperationCanceledException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AuthenticatorException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}, null);
I m running this on onPerformSync but this is not opening an activity.
There are two parts to doing this
1) in your AbstractThreadedSyncAdapter implementation of overridden onPerformSync method you need to
Get the authcode from the AccountManager using method blockingGetAuthToken()
Try to use the authcode to perform your sync processes (i.e. web service call or whatever you use it for)
If the previous step failed because authcode has expired (e.g. your web serivce returns some kind of authcode expired message) then you need to invalidate the authcode via the AccountManager using method invalidateAuthToken()
2) in your AbstractAccountAuthenticator implementation of overridden getAuthToken() method
Use the AccountManager to retrieve the password that the user last provided and try to get a new authcode from your web service using those credentials.
If the previous step failed then add an intent to open your login activity to the bundle that is returned from the getAuthToken() method. This will cause the login screen to display
Example
#Override
public Bundle getAuthToken(AccountAuthenticatorResponse oResponse, Account oAccount, String strAuthTokenType, Bundle options)
throws NetworkErrorException {
// Validate the authentication type
if (!strAuthTokenType.equals("TODO: your auth token type URL here"))
{
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType");
return result;
}
// Try to get the password already stored in account manger, if there is one
final AccountManager oAccountManager = AccountManager.get(moContext);
final String strPassword = oAccountManager.getPassword(oAccount);
if (strPassword != null)
{
// TODO: Call the authentication web service method to get a fresh authcode
// Pass the strPassword and oAccount.name
Boolean blnVerified = //TODO: were the username + password authenticated?
String strNewAuthCode = //TODO: the new authcode returned by your authentication web service
// If it worked then return the result
if (blnVerified)
{
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, oAccount.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, "TODO: your account type URI here");
result.putString(AccountManager.KEY_AUTHTOKEN, strNewAuthCode);
return result;
}
}
// Password is missing or incorrect. Start the activity to ask user to provide the missing credentials.
// Open a UI form to get the user to input their username and password again
final Intent oIntent = new Intent(moContext, frmAccount_Auth.class);
oIntent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, oResponse);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, oIntent);
return bundle;
}
Related
I use a SyncAdapter and an AccountAuthenticator in my app. When doing the sync stuff, I call AccountManager.blockingGetAuthToken to get an access token. I understand this method that way, that it starts my Log-in activity when it can not get a token (or in other words, when the getAuthToken methods returns an Intent to start the Activity).
But it just returns null, without launching the Activity.
This is the getAuthToken method from my authenticator.
#Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
// Extract the username and password from the Account Manager, and ask
// the server for an appropriate AuthToken.
final AccountManager am = AccountManager.get(mContext);
String authToken = am.peekAuthToken(account, authTokenType);
// Lets give another try to authenticate the user
if (TextUtils.isEmpty(authToken)) {
final String password = am.getPassword(account);
if (password != null) {
try {
authToken = APIHelper.getInstance().logIn(account.name, password);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// If we get an authToken - we return it
if (!TextUtils.isEmpty(authToken)) {
// cache
am.setAuthToken(account, authTokenType, authToken);
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
return result;
}
// If we get here, then we couldn't access the user's password - so we
// need to re-prompt them for their credentials. We do that by creating
// an intent to display our AuthenticatorActivity.
final Intent intent = new Intent(mContext, AuthActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
intent.putExtra(AuthActivity.ARG_ACCOUNT_TYPE, account.type);
intent.putExtra(AuthActivity.ARG_AUTH_TYPE, authTokenType);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
By the way, most of the code is from
this blog.
It seems the asker figured it out. For posterity, it works if you use this instead:
AccountManagerFuture<Bundle> resultFuture = accountManager.getAuthToken(
account,
AUTH_TOKEN_TYPE,
null,
activity,
null,
null
);
Bundle bundle = resultFuture.getResult();
return bundle.getString(AccountManager.KEY_AUTHTOKEN);
I guess blockingGetAuthToken() is unable to do this automatically because it lacks the activity parameter. And the documentation is incorrect.
I want to enable server-side Calendar API access for my android app.
I have followed the steps given here .
I am getting a null value for the authorization code.
I think I am giving wrong values for 'scope' field and the 'server_client_id' field.
Please give me an example showing correct fields values for 'scope' and 'server_client_id' in the getToken() method.
Thanks for any help.
PS- I have used google+ sign in for android given here for connecting to a google account.
EDIT- Here is my code. I have given the OAuth 2.0 scope for the Google Calendar API in the scope field.
I have taken Client ID for Android application from Developers Console and put in 'serverClientID' field. This is probably where I am wrong. I don't know how to get Server's Client ID which is required by the
public class AsyncGetAuthToken extends AsyncTask<String, Void, String>{
#Override
protected String doInBackground(String... params) {
Bundle appActivities = new Bundle();
appActivities.putString(GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES,
"MainActivity");
String scopeString = "https://www.googleapis.com/auth/calendar.readonly";
String serverClientID = CLIENT_ID;
String scopes = "oauth2:server:client_id:" + serverClientID + ":api_scope:" + scopeString;
String code = null;
try {
code = GoogleAuthUtil.getToken(
MainActivity.this, // Context context
Plus.AccountApi.getAccountName(mGoogleApiClient), // String accountName
scopes, // String scope
appActivities // Bundle bundle
);
} catch (IOException transientEx) {
// network or server error, the call is expected to succeed if you try again later.
// Don't attempt to call again immediately - the request is likely to
// fail, you'll hit quotas or back-off.
return null;
} catch (UserRecoverableAuthException e) {
// Requesting an authorization code will always throw
// UserRecoverableAuthException on the first call to GoogleAuthUtil.getToken
// because the user must consent to offline access to their data. After
// consent is granted control is returned to your activity in onActivityResult
// and the second call to GoogleAuthUtil.getToken will succeed.
startActivityForResult(e.getIntent(), AUTH_CODE_REQUEST_CODE);
return null;
} catch (GoogleAuthException authEx) {
// Failure. The call is not expected to ever succeed so it should not be
// retried.
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
}
And in my onActivityResult, I look for the Auth Code
if (requestCode == AUTH_CODE_REQUEST_CODE) {
if (responseCode == RESULT_OK){
Bundle extra = intent.getExtras();
String oneTimeToken = extra.getString("authtoken");
Log.d("LOG", "one time token" + oneTimeToken);
}
}
I have a problem with kitkat api while tringy to get access token of google account services, google music in my case. So, if user trying get token at first by using next method:
public String getAuthToken(Account account)
throws AuthenticatorException, IOException {
String s1;
if (account == null) {
Log.e("MusicAuthInfo", "Given null account to MusicAuthInfo.getAuthToken()", new Throwable());
throw new AuthenticatorException("Given null account to MusicAuthInfo.getAuthToken()");
}
String s = getAuthTokenType(mContext);
try {
s1 = AccountManager.get(mContext).blockingGetAuthToken(account, s, true);
} catch (OperationCanceledException operationcanceledexception) {
throw new AuthenticatorException(operationcanceledexception);
}
if (s1 == null) {
throw new AuthenticatorException("Received null auth token.");
}
return s1;
}
here i get s1 == null and the system push notification:
When user tap on notification, next dialog appear:
When user click "ok", all next iterations getting token get success.
Question: How to circumvent this confirmation or show just dialog, without click to notification ?
It's not a direct answer to your question, but you can use Google Play Services instead.
String token = GoogleAuthUtil.getToken(context, userEmail, "oauth2:https://mail.google.com/");
You just have to specify the oauth2 scope you need. For instance for Google+ you would need "https://www.googleapis.com/auth/plus.login" instead of what I post in the snippet for Gmail. You can also specify multiple scopes in one token request. The permission request pops up right away.
You can read all about it here: Authorizing with Google for REST APIs, Login scopes
Solved. Need use this method:
Bundle result = AccountManager.get(activity).getAuthToken(account, s, new Bundle(), activity, new AccountManagerCallback<Bundle>() {
#Override
public void run(AccountManagerFuture<Bundle> future) {
try {
Log.e("xxx", future.getResult().toString());
} catch (OperationCanceledException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (AuthenticatorException e) {
e.printStackTrace();
}
}
}, null).getResult();
I am creating an box android app that allows user to upload media files on their account.
I have set up my client id and client secret,it is authenticating my app too.
Uploading part is also done,but the problem i am facing is to save the auth data [which is obviously needed so user is not needed to login again and again]
Load, save and use of authentication data in Box Android API
the solution given above is not working [may b they have removed 'Utils.parseJSONStringIntoObject' method]
i can store the access token and refresh token but whats the point of saving when i cant use them to re authenticate a user
switch (requestCode)
{
case AUTHENTICATE_REQUEST:
if (resultCode == Activity.RESULT_CANCELED)
{
String failMessage = data.getStringExtra(OAuthActivity.ERROR_MESSAGE);
Toast.makeText(this, "Auth fail:" + failMessage, Toast.LENGTH_LONG).show();
// finish();
}
else
{
BoxAndroidOAuthData oauth = data.getParcelableExtra(OAuthActivity.BOX_CLIENT_OAUTH);
BoxAndroidClient client = new BoxAndroidClient(BoxSDKSampleApplication.CLIENT_ID, BoxSDKSampleApplication.CLIENT_SECRET, null, null);
client.authenticate(oauth);
String ACCESS_TOKEN=oauth.getAccessToken();
String REFRESH_TOKEN=oauth.getRefreshToken();
Editor editor = prefs.edit();
editor.putString("ACCESS_TOKEN", ACCESS_TOKEN);
editor.putString("REFRESH_TOKEN", REFRESH_TOKEN);
editor.commit();
BoxSDKSampleApplication app = (BoxSDKSampleApplication) getApplication();
client.addOAuthRefreshListener(new OAuthRefreshListener()
{
#Override
public void onRefresh(IAuthData newAuthData)
{
Log.d("OAuth", "oauth refreshed, new oauth access token is:" + newAuthData.getAccessToken());
//---------------------------------
BoxOAuthToken oauthObj=null;
try
{
oauthObj=getClient().getAuthData();
}
catch (AuthFatalFailureException e)
{
e.printStackTrace();
}
//saving refreshed oauth object in client
BoxAndroidOAuthData newAuthDataObj=new BoxAndroidOAuthData(oauthObj);
getClient().authenticate(newAuthDataObj);
}
});
app.setClient(client);
}
i have referred https://github.com/box/box-android-sdk-v2/tree/master/BoxSDKSample example
can any one tell me what i am doing wrong or any alternative to authenticate user using authdata,access token,refresh token?
UPDATE
refreshing token as they have said
'Our sdk auto refreshes OAuth access token when it expires. You will want to listen to the refresh events and update your stored token after refreshing.'
mClient.addOAuthRefreshListener(new OAuthRefreshListener()
{
#Override
public void onRefresh(IAuthData newAuthData)
{
Log.d("OAuth", "oauth refreshed, new oauth access token is:" + newAuthData.getAccessToken());
try
{
oauthObj=mClient.getAuthData();
mClient.authenticate(newAuthData);
String authToken=null;
//Storing oauth object in json string format
try
{
authToken = new BoxJSONParser(new AndroidBoxResourceHub()).convertBoxObjectToJSONString(newAuthData);
prefs.edit().putString("BOX_TOKEN", authToken).commit();
//saving authToken in shared Preferences
mClient.authenticate(newAuthData);
String ACCESS_TOKEN=newAuthData.getAccessToken();
String REFRESH_TOKEN=newAuthData.getRefreshToken();
Log.v("New Access token ", oauthObj.getAccessToken());
Log.v("New Refresh token ", oauthObj.getRefreshToken());
editor.putString("ACCESS_TOKEN", ACCESS_TOKEN);
editor.putString("REFRESH_TOKEN", REFRESH_TOKEN);
prefs.edit().putString("BOX_TOKEN", authToken).commit();
editor.commit();
}
catch (BoxJSONException e1)
{
e1.printStackTrace();
}
Log.v("Token Refreshed", " ");
}
catch (AuthFatalFailureException e)
{
e.printStackTrace();
}
}
});
app.setClient(mClient);
}
onClientAuthenticated();
In main activity,fetching stored token
try
{
stored_oauth_token=prefs.getString("BOX_TOKEN", null);
authData = new BoxJSONParser(new AndroidBoxResourceHub()).parseIntoBoxObject(stored_oauth_token, BoxAndroidOAuthData.class);
}
catch (BoxJSONException e)
{
e.printStackTrace();
}
mClient = new BoxAndroidClient(BoxSDKSampleApplication.CLIENT_ID, BoxSDKSampleApplication.CLIENT_SECRET, null, null);
mClient.authenticate(authData);
BoxSDKSampleApplication app = (BoxSDKSampleApplication) getApplication();
app.setClient(mClient);
i tried this app to upload a file after existing ,it did work
but after 60-70 odd minutes i couldn't upload file.
is there anything wrong in my code ?
This is how I initialize my Box client:
mClient = new BoxClient(BOX_CLIENT_ID, BOX_CLIENT_SECRET, null, null);
mClient.addOAuthRefreshListener(new OAuthRefreshListener() {
#Override
public void onRefresh(IAuthData newAuthData) {
try {
String authToken = new BoxJSONParser(new AndroidBoxResourceHub()).convertBoxObjectToJSONString(newAuthData);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit().putString("box_token", authToken).commit();
} catch (BoxJSONException e) { }
}
});
mAuthToken = prefs.getString("box_token", null);
if (mAuthToken != null) {
BoxAndroidOAuthData authData = new BoxJSONParser(
new AndroidBoxResourceHub()
).parseIntoBoxObject(mAuthToken, BoxAndroidOAuthData.class);
mClient.authenticate(authData);
}
if (!mClient.isAuthenticated()) {
Intent intent = OAuthActivity.createOAuthActivityIntent(context, BOX_CLIENT_ID, BOX_CLIENT_SECRET, false, "https://yoururl.com/");
((Activity) context).startActivityForResult(intent, BOX_AUTH_REQUEST_CODE);
}
So for the auth refresh there are a couple of things to be considered:
box client automatically refreshes OAuth tokens, you'll want to attach a OAuthRefreshListener to listen to the refresh, if you want to persist, persist the oauth data passed into the refresh listener. The listener only update your persisted oauth data, you don't need to re-authenticate in the refresh listener, sdk does the re-authenticate automatically.
When you first initiate box client, you need to authenticate either by persisted auth, or the OAuth UI. The logic should be:
check client.isAuthenticated();
2.1 If authenticated, do nothing.
2.2 if not authenticated, try to check whether there's persisted auth data. If so, authenticate by client.authenticate(oauthdata);
2.3 if 2.2 failed, start OAuth UI flow.
2.4 at last, in case of OAuthFatalFailureException, start OAuth UI flow.
I'm doing authentication of android app against an app engine server, basically following this post: http://blog.notdot.net/2010/05/Authenticating-against-App-Engine-from-an-Android-app.
It appears that the cookie that I get at the end of the process is no good - I'm getting 401, so I tried copying the cookie and testing it in the browser, and still getting 401. When copying the browser cookie to the android app, the request works.
How could I be getting an invalid cookie? I've even tried invalidating the tokens, but still getting the same result...
Don't bother doing this yourself. Use this: http://loopj.com/android-async-http/
For some reason this worked: I changed the URL for the initial cookie request from https to http, then changed it back.
But in the end I decided to change my implementation and go with loopj as alistair suggested. The result is far more elegant. This is my login activity (note that I'm connecting the client to a persistent cookie storage given by loopj api):
public class AccountList extends ListActivity {
protected AccountManager accountManager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
accountManager = AccountManager.get(getApplicationContext());
Account[] accounts = accountManager.getAccountsByType("com.google");
this.setListAdapter(new ArrayAdapter<Account>(this, R.layout.list_item, accounts));
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
Account account = (Account)getListView().getItemAtPosition(position);
accountManager.invalidateAuthToken("com.google", null);
accountManager.getAuthToken(account, "ah", null, this, new GetAuthTokenCallback(), null);
}
private class GetAuthTokenCallback implements AccountManagerCallback<Bundle> {
public void run(AccountManagerFuture<Bundle> result) {
AsyncHttpClient client = new AsyncHttpClient();
PersistentCookieStore myCookieStore = new PersistentCookieStore(getBaseContext());
client.setCookieStore(myCookieStore);
try {
Bundle bundle;
bundle = result.getResult();
Intent intent = (Intent)bundle.get(AccountManager.KEY_INTENT);
if(intent != null) {
// User input required
startActivity(intent);
} else {
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
String url = "http://myapp.appspot.com/_ah/login?continue=http://localhost/&auth=" + token;
client.post(url, new AsyncHttpResponseHandler());
Intent backToMainActivity = new Intent(getApplicationContext(), MainActivity.class);
startActivity(backToMainActivity);
}
} catch (OperationCanceledException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (AuthenticatorException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Because loopj gives persistent cookie storage, all I have to do in another activity is to initialize a client and connect it with the persistent cookie storage. This gives the new client all of the cookies I got from the login activity. Initialization looks something like this:
AsyncHttpClient client = new AsyncHttpClient();
client.setCookieStore(new PersistentCookieStore(this));
BTW & FYI: The loopj library uses the SharedPreferences API in order to store the cookies, and wraps it nicely as PersistentCookieStore.
dont know what httpclient you are using.
maybe it will help to read the apache docs on cookies...