I've got an odd behaviour when using Android's AccountManager to get an auth token for a Google account.
When the app launches, the first call to getAuthToken returns a bundle with an empty string as token. The next time, I call the very same method, it returns a valid token.
Here's my code:
public String updateToken(final boolean invalidateToken, final Context c) {
String authToken = "";
try {
final AccountManager am = AccountManager.get(c);
final Account[] accounts = am.getAccountsByType("com.google");
final Bundle bundle = am.getAuthToken(accounts[0], "android", true,
null, null).getResult();
authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN)
.toString();
if (invalidateToken) {
am.invalidateAuthToken("com.google", authToken);
authToken = updateToken(false, c);
}
} catch (final Exception e) {
//Just for debugging issues.
e.printStackTrace();
}
return authToken;
}
It looks like the empty token is returned, when this method is called in the onCreate method of my activity, although it's not always the case.
Thanks in advance.
Also I don't really know when to invalidate the token. Once a day? On every start up? Or is the empty token the indicator, that the token has to be invalidated, although it returns a valid token on the very next call.
You need to invalidate the token before requesting one.
See AuthToken from AccountManager in Android Client No Longer Working
Related
i'm new with android and i need to do a connection with server with oauth 2.0 i looked in internet and found just example how to dowit with google or github but my need is to connect with my own server i have the clientId clientSecret and the scope all i need is to get the token correctly
i hope my question is clear
thank you
this what i have donne
AccountManager am = AccountManager.get(Authentification.this);
Bundle options = new Bundle();
options.putSerializable("numero", numero);
am.getAuthToken(
null,
"whrite",
options,
this,
new OnTokenAcquired(),
null);
private class OnTokenAcquired implements AccountManagerCallback<Bundle> {
#Override
public void run(AccountManagerFuture<Bundle> result) {
// Get the result of the operation from the AccountManagerFuture.
try{
Bundle bundle = result.getResult();
// The token is a named value in the bundle. The name of the value
// is stored in the constant AccountManager.KEY_AUTHTOKEN.
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
System.out.println("================>>>>"+token);
}catch(Exception e){
}
}
}
I would start with the AppAuth code sample. My blog post
has step by step instructions on how to run it.
Once it is working reconfigure it to point to your own Authorization Server.
I'm trying to add a new account (after a Facebook login + server
validation) in AccountManager. The flow for this case is like this:
User login with Facebook
I got the details after the login is done and I validate them against the data I have on my server
If everything is ok, the server send back an auth_token (JWT token)
Having user's details and the auth_token I'm creating an account via AccountManager and once it is created, I set the authToken for
it.
On next login when the user will re-open the app I call getAuthToken which first try to get the cached authToken by calling peekAuthToken().
The problem
At point 5, peekAuthToken returns null but it shouldn't because I already set the autToken for that account.
Code
public static Bundle handleUserLogin(Context context, User user) {
SharedPreferences mPrefs = context.getSharedPreferences(Constants.PREFS_NAME, Context.MODE_PRIVATE);
AccountManager am = AccountManager.get(context);
Account account = new Account(user.getEmail(), ACCOUNT_TYPE);
Account[] accounts = am.getAccountsByType(ACCOUNT_TYPE);
boolean isNewAccount = true;
for (int i = 0; i < accounts.length; i++) {
if (user.getEmail().equalsIgnoreCase(accounts[i].name) && ACCOUNT_TYPE.equalsIgnoreCase(accounts[i].type)) {
isNewAccount = false;
account = accounts[i];
break;
}
}
if (isNewAccount) {
am.addAccountExplicitly(account, user.getPassword(), null);
accounts = am.getAccountsByType(ACCOUNT_TYPE);
for (int i = 0; i < accounts.length; i++) {
if (user.getEmail().equalsIgnoreCase(accounts[i].name) && ACCOUNT_TYPE.equalsIgnoreCase(accounts[i].type)) {
account = accounts[i];
break;
}
}
}
if (null != user.getPassword()) {
am.setPassword(account, user.getPassword());
}
Cs.error(TAG, "account " + account + " token " + user.getToken());
am.setAuthToken(account, user.getToken(), Authenticator.AUTHTOKEN_TYPE_FULL_ACCESS);
setUserData(user, account, am);
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, user.getToken());
mPrefs.edit().putString(Constants.KEY_CURRENT_USER, account.name).commit();
return result;
}
First I thought that maybe the reference to my new account is not the correct one (ex the one from AccountManager) so I search for account again.
Could you give me some indications about what I'm doing wrong or how should I make sure the authToken will be set for an account?
Thank you
I would not rely on explicit setting authToken in place different than in AbstractThreadedAccountAuthenticator's getAuthToken() method.
My guess is that the authToken is not cached yet when you request it.
I suggest just creating account via AccountManager.addAccountExplicitly() and deferring accessing authToken once you really need it.
In AbstractThreadedAccountAuthenticator's getAuthToken() I suggest applying logic like in the project I created a while ago.
Request authToken via AccountManager.peekAuthToken()
If authToken is set in cache then return it.
If not then request it from your server then cache via AccountManager.setAuthToken() and finally return.
Link: https://github.com/dawidgdanski/AccountAuthenticatorExample/blob/master/app/src/main/java/com/authenticator/account/auth/SimpleAuthenticator.java
Hope this will help you out:
https://github.com/dawidgdanski/AccountAuthenticatorExample
I am working with authenticating via Google+ according to the following:
https://developers.google.com/+/mobile/android/sign-in
Most of this process seems fine. The problem I'm having is that we need to get a "one-time authorization code" so that our backend servers can perform certain requests on behalf of the user, with their permission. This is covered in the section "Enable server-side api access for your app". However, for a number of reasons, our servers can cause the login to fail, even if the authorization code is valid (e.g. the user doesn't have an account corresponding to the google+ account on our servers yet, in which case they can make one).
If this happens, we might need them to login again at a later time. What I'm finding, though, is that when I perform the second login with google+, it gives me the same authorization code, even if it's already been used by our servers. I've tried disconnecting and reconnecting to the google client api, and calling GoogleApiClient.clearDefaultAccountAndReconnect(), but no matter what I do, I seem to end up with the same authorization code. This, of course, is rejected by the server when it tries to use it, since it's already been used.
I'm wondering what I'm doing wrong here. I have the following method, which is called during the initial authentication process, and then again if a response status of 500 is detected from our server (indicating the previous call failed, presumably because the code has already been used):
private void dispatchGooglePlusAuthCodeAcquisition() {
AsyncTask<Void, Void, String> authAcquisition = new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
Bundle authPreferences = new Bundle();
mUserPermissionNeededForAuthCode = false;
authPreferences.putString(GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES,
"");
String scopesString = Scopes.PROFILE;
WhenIWorkApplication app = (WhenIWorkApplication)WhenIWorkApplication.getInstance();
String serverClientID = app.getGoogleOAuthClientIDForPersonalServer();
String scope = "oauth2:server:client_id:" + serverClientID + ":api_scope:" + scopesString;
String code = null;
authPreferences.putBoolean(GoogleAuthUtil.KEY_SUPPRESS_PROGRESS_SCREEN, true);
try {
code = GoogleAuthUtil.getToken(
mActivity,
Plus.AccountApi.getAccountName(mGoogleApiClient),
scope,
authPreferences
);
} 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.
Log.d(LOGTAG, "Encountered an IOException while trying to login to Google+."
+ " We'll need to try again at a later time.");
} catch (UserRecoverableAuthException e) {
mUserPermissionNeededForAuthCode = true;
// 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.
if (!mGooglePlusPermissionActivityStarted) {
mGooglePlusPermissionActivityStarted = true;
mActivity.startActivityForResult(e.getIntent(), RESULT_CODE_AUTH_CODE);
}
} catch (GoogleAuthException authEx) {
// Failure. The call is not expected to ever succeed so it should not be
// retried.
Log.e(LOGTAG, "Unable to authenticate to Google+. Call will likely never"
+ " succeed, so bailing.", authEx);
}
return code;
}
#Override
protected void onPostExecute(String aResult) {
if (aResult != null) {
// We retrieved an authorization code successfully.
if (mAPIAccessListener != null) {
mAPIAccessListener.onAuthorizationCodeGranted(aResult);
}
} else if (!mUserPermissionNeededForAuthCode) {
// If this is the case, then we didn't get authorization from the user, or something
// else happened.
if (mAPIAccessListener != null) {
mAPIAccessListener.onAuthorizationFailed();
}
Log.d(LOGTAG, "Unable to login because authorization code retrieved was null");
}
}
};
authAcquisition.execute();
So, the answer to this was a lot simpler than I imagined. Apparently, there is aclearToken() method on the GoogleAuthUtil class:
http://developer.android.com/reference/com/google/android/gms/auth/GoogleAuthUtil.html#clearToken%28android.content.Context,%20java.lang.String%29
public static void clearToken (Context context, String token)
Clear the specified token in local cache with respect to the Context. Note that the context must be the same as that used to initialize the token in a previous call to getToken(Context, String, String) or getToken(Context, String, String, Bundle).
Parameters
context Context of the token.
token The token to clear.
Throws
GooglePlayServicesAvailabilityException
GoogleAuthException
IOException
Calling this method before attempting to re-authenticate causes Google to generate a new one-time authorization token.
Well i need to authorize Google Calendar's access for a user, first google Id works fine when i use
blockingGetAuthToken
and it gets a token, i usually log on this token.
So when i tried to use other accounts i got a null token.
I searched a lot and found out that using getAuthToken is preferred as it uses a context from the activity calling it.. then i converted the whole process to use it
private static final String AUTH_TOKEN_TYPE = "oauth2:https://www.googleapis.com/auth/calendar";
public static String authorize(AndroidtestActivity parent, Account account) {
AccountManager accountManager = AccountManager.get(parent);
Bundle options= new Bundle();
Log.d("MyAPP", "Get Authorization");
try {
AccountManagerFuture<Bundle> acc=accountManager.getAuthToken ( account, AUTH_TOKEN_TYPE, options, true, null, null);
Bundle authTokenBundle = acc.getResult();
String authToken = authTokenBundle.get(AccountManager.KEY_AUTHTOKEN).toString();
Log.d("MyAPP","Token= "+authToken);
return authToken;
} catch (Exception ex) {
Logger.getLogger(GoogleAuthorize.class.getName()).log(Level.SEVERE,
null, ex);
}
return null;
}
}
but still no accounts could get a valid token, they all get a null one
then i saw this answer https://stackoverflow.com/a/2021337/1280902 and followed using invalidateAuthToken
private static final String AUTH_TOKEN_TYPE = "oauth2:https://www.googleapis.com/auth/calendar";
public static String authorize(AndroidtestActivity parent, Account account) {
AccountManager accountManager = AccountManager.get(parent);
Bundle options= new Bundle();
Log.d("MyAPP", "Get Authorization");
try {
AccountManagerFuture<Bundle> acc=accountManager.getAuthToken ( account, AUTH_TOKEN_TYPE, options, true, null, null);
Bundle authTokenBundle = acc.getResult();
String authToken = authTokenBundle.get(AccountManager.KEY_AUTHTOKEN).toString();
accountManager.invalidateAuthToken("com.google",authToken);
acc=accountManager.getAuthToken ( account, AUTH_TOKEN_TYPE, options, true, null, null);
authTokenBundle = acc.getResult();
authToken = authTokenBundle.get(AccountManager.KEY_AUTHTOKEN).toString();
Log.d("MyAPP","Token= "+authToken);
return authToken;
} catch (Exception ex) {
Logger.getLogger(GoogleAuthorize.class.getName()).log(Level.SEVERE,
null, ex);
}
return null;
}
}
but i had the same problem on every account i use, even the one that used to work at the beginning with blockingGetAuthToken
So am i missing something?
Ok it works fine when i use
getAuthToken (Account account, String authTokenType, Bundle options, Activity activity, AccountManagerCallback<Bundle> callback, Handler handler)
The activity parameter solved the problem..
I'm trying to retrieve the AuthToken for Facebook (saved by Facebook for Android) by using the following piece of code.
AccountManager am = AccountManager.get(this);
Account[] accounts = am.getAccountsByType("com.facebook.auth.login");
if (accounts.length > 0) {
for(int j = 0; j < accounts.length; j++) {
Account account = accounts[j];
if(account.type != null && account.type.equals("com.facebook.auth.login")) {
Log.e(RuntimeVars.MY_NAME, "FACEBOOK-TYPE FOUND");
am.getAuthToken(account, "com.facebook.auth.login", null, ConversationList.this,
new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> arg0) {
try {
Bundle b = arg0.getResult();
Log.e(RuntimeVars.MY_NAME, "THIS AUTHTOKEN: " + b.getString(AccountManager.KEY_AUTHTOKEN));
}
catch (Exception e) {
Log.e(RuntimeVars.MY_NAME, "EXCEPTION#AUTHTOKEN");
}
}
}, null);
}
}
}
The login credentials are found and FACEBOOK-TYPE FOUND is written into LogCat, but neither THIS AUTHTOKEN: [...] nor EXCEPTION#AUTHTOKEN is logged. So I suppose am.getAuthToken is never called.
What am I missing?
In general, if there is a better (and at least working) approach to retrieve the Facebook authtoken from the Android accounts please let me know.
Thanks a lot for your help!
Best regards
S.
Why not use the Facebook SDK?
The Facebook class in it has a member to get the OAuth 2.0 access token (if that is what you need), getAccessToken().
To explain the fact that neither of your logging statements are being reached, consider:
Line ~8:
am.getAuthToken(account, "com.facebook.auth.login", null, ConversationList.this,
... can return a token if it's immediately available. Maybe that's the answer you're looking for? Quoting the AccountManager documentation:
If a previously generated auth token
is cached for this account and type,
then it is returned. Otherwise, if a
saved password is available, it is
sent to the server to generate a new
auth token. Otherwise, the user is
prompted to enter a password.
Try calling AccountManager.blockingGetAuthToken instead. If that works, then there's something more interesting at fault here...
Also, make sure your manifest has the USE_CREDENTIALS permission set correctly.
add try { before am.getAuthToken and catch Exception where this method declaration ends.This will give you why and where excepption is happening