When I wrote Any.DO’s sync system, about 18 months ago, I wanted to use the built in AccountManager API to authenticate and store the users’ credentials. When I used it to access Google accounts it seems a pretty simple experience, so I thought I should do that for Any.DO as well. It also goes very well with a sync mechanism (using a SyncAdapter), making it the perfect solution. The problem – no good documentation was out there. The developers community didn’t have much experience with that back then, and since we didn’t have much time to figure out issues that could arise from this “No man’s land”, a decision was made to use other methods.
But times have changed..
I recently studied that feature again for a project I’m working on, and saw there’s a huge improvement with the knowledge about it. Besides the better documentation on the Android.com site, more and more tutorials went out to the wild, feeding us with knowledge about the mysteries of the notorious Account Manager. Some tales were told about the road to create your own account. I read pretty much all of them.
But..they all still miss something.
I didn’t feel that I actually know everything I wanted to know about the process, and some parts weren’t clear enough. So I did what I usually do when I want to know everything about something – investigate it “Jack Bauer style”! This post is the in-depth conclusion of my journey, with all the quirks and features that this service provides and I thought was important enough to find out. There will be a followup post about Sync Adapters as well, so I recommend RSS/Twitter subscribing to be notified…if you’re into this kind of stuff. I’ve been pretty thorough learning all those features, not just the basic stuff as all other tutorials did, but if I forgot something – please let me know by commenting this post.
Why Account Manager?
Why really?
Why not just write a simple sign-in form, implement a submit button that post the info to the server and return an auth token? The reason for that is the extra features you get and in the small details that you don’t always cover. All those “corners” that developers often miss when they need their users to sign-in, or dismiss by saying “This will happen to 1 out of 100000 users! It’s nothing!”. What happens if the user changes the password on another client? Has an expired auth-token? Runs a background service that doesn’t have a UI the user can interact with? Wants the convenience of logging-in once and get automatically authenticated on all account-related app (like all Google’s apps do)?
Reading this article will probably make you think it’s complicated stuff, but it’s not! Using the Account Manager actually simplifies the authentication process for most cases, and since I’m already giving you a working code sample – why not use it 🙂
So, to recap the benefits:
Pros: Standard way to authenticate users. Simplifies the process for the developer. Handles access-denied scenarios for you. Can handle multiple token types for a single account (e.g. Read-only vs. Full-access). Easily shares auth-tokens between apps. Great support for background processes such as SyncAdapters. Plus, you get a cool entry for your account type on the phone’s settings:
Cons: Requires learning it. But hey, that’s what you’re here for, isn’t it?
The steps we’ll performs to get this done:
- Creating our Authenticator – the brain behind this operation
- Creating the Activities – in those the user will enter his credentials
- Creating the Service – through it we can communicate with the authenticator
But first, some definitions.
Authenti..what?
Lets start with the basics – these are the main parts here:
Authentication Token (auth-token) – A temporary access token (or security-token) given by the server. The user needs to identify to get such token and attach it to every request he sends to the server. On this post I’ll use OAuth2 as the authentication standard, since it’s the most popular method there is.
First time logging-in
- The app asks the AccountManager for an auth-token.
- The AccountManager asks the relevant AccountAuthenticator if it has a token for us.
- Since it has none (there’s no logged-in user), it show us a AccountAuthenticatorActivity that will allow the user to log-in.
- The user logs-in and auth-token is returned from the server.
- The auth-token is stored for future use in the AccountManager.
- The app gets the auth-token it requested
- Everyone’s happy!
In case the user has already logged-in, we would get the auth-token back already on the second step. You can read more about authenticating using OAuth2 here.
Now that we know the basics, let’s see how to create our own account type authenticator.
Creating our Authenticator
As written earlier, the Account Authenticator is the one that gets addressed by the AccountManager to fulfill all account relevant tasks: Getting stored auth-token, presenting the account log-in screen and handling the user authentication against the server.
Creating our own Authenticator requires extending AbstractAccountAuthenticator and implementing some methods. Let’s focus for now on the 2 main methods:
addAccount
Called when the user wants to log-in and add a new account to the device.
We need to return a Bundle with the Intent to start our AccountAuthenticatorActivity (explained later). This method can be called by the app itself by calling AccountManager#addAccount() (requires a special permission for that) or from the phone’s settings screen, as seen here:
Example:
@Override public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException { final Intent intent = new Intent(mContext, AuthenticatorActivity.class); intent.putExtra(AuthenticatorActivity.ARG_ACCOUNT_TYPE, accountType); intent.putExtra(AuthenticatorActivity.ARG_AUTH_TYPE, authTokenType); intent.putExtra(AuthenticatorActivity.ARG_IS_ADDING_NEW_ACCOUNT, true); intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); final Bundle bundle = new Bundle(); bundle.putParcelable(AccountManager.KEY_INTENT, intent); return bundle; }
getAuthToken
Explained by the diagram above. Gets a stored auth-token for the account type from a previous successful log-in on this device. If there’s no such thing – the user will be prompted to log-in. After a successful sign-in, the requesting app will get the long-awaited auth-token. To do all that, we need to check the AccountManager if there’s an available auth-token by using AccountManager#peekAuthToken(). If there isn’t we return the same result as for addAccount().
@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) { authToken = sServerAuthenticate.userSignIn(account.name, password, authTokenType); } } // If we get an authToken - we return it if (!TextUtils.isEmpty(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, AuthenticatorActivity.class); intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response); intent.putExtra(AuthenticatorActivity.ARG_ACCOUNT_TYPE, account.type); intent.putExtra(AuthenticatorActivity.ARG_AUTH_TYPE, authTokenType); final Bundle bundle = new Bundle(); bundle.putParcelable(AccountManager.KEY_INTENT, intent); return bundle; }
If the auth-token we got from this method is not valid anymore, because of time expiration or changed password from a different client, you need to invalidate the current auth-token on the AccountManager and ask for a token once again. Invalidating the current token is done by calling AccountManager#invalidateAuthToken(). The next call to getAuthToken() will try to log-in with the stored password and if it fails – the user will have to enter his credentials again.
So..where will the user enter his credentials? That’ll be in our derivation for AccountAuthenticatorActivity
Creating the Activity
Our AccountAuthenticatorActivity is the only direct interaction that we have with the user.
This activity will show the user a log-in form, authenticate him with our server, and return the result to the calling authenticator. The reason we extend from AccountAuthenticatorActivity, and not just from the regular Activity, is the setAccountAuthenticatorResult() method. This method is in charge of taking back the result from the authentication process on the activity and return it to the Authenticator, who called this activity in the first place. It saves us the need to keep a response interface to communicate with the Authenticator ourselves.
I built a simple username/password form on my Activity. You can use the Login Activity Template suggested on the Android site. When submitting I call this method:
public void submit() { final String userName = ((TextView) findViewById(R.id.accountName)).getText().toString(); final String userPass = ((TextView) findViewById(R.id.accountPassword)).getText().toString(); new AsyncTask<Void, Void, Intent>() { @Override protected Intent doInBackground(Void... params) { String authtoken = sServerAuthenticate.userSignIn(userName, userPass, mAuthTokenType); final Intent res = new Intent(); res.putExtra(AccountManager.KEY_ACCOUNT_NAME, userName); res.putExtra(AccountManager.KEY_ACCOUNT_TYPE, ACCOUNT_TYPE); res.putExtra(AccountManager.KEY_AUTHTOKEN, authtoken); res.putExtra(PARAM_USER_PASS, userPass); return res; } @Override protected void onPostExecute(Intent intent) { finishLogin(intent); } }.execute(); }
sServerAuthenticate is the interface to our authenticating server. I implemented methods such as userSignIn and userSignUp that return the auth-token from the server, upon a successful log-in.
mAuthTokenType is the type of token that I request from the server. I can have the server give me different tokens for read-only or full access to an account, or even for different services within the same account. A good example is the Google account, which provides several auth-token types: “Manage your calendars”, “Manage your tasks”, “View your calendars” and more.. On this particular example I don’t do anything different for the various auth-token types.
When I finish, I call finishLogin():
private void finishLogin(Intent intent) { String accountName = intent.getStringExtra(AccountManager.KEY_ACCOUNT_NAME); String accountPassword = intent.getStringExtra(PARAM_USER_PASS); final Account account = new Account(accountName, intent.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE)); if (getIntent().getBooleanExtra(ARG_IS_ADDING_NEW_ACCOUNT, false)) { String authtoken = intent.getStringExtra(AccountManager.KEY_AUTHTOKEN); String authtokenType = mAuthTokenType; // Creating the account on the device and setting the auth token we got // (Not setting the auth token will cause another call to the server to authenticate the user) mAccountManager.addAccountExplicitly(account, accountPassword, null); mAccountManager.setAuthToken(account, authtokenType, authtoken); } else { mAccountManager.setPassword(account, accountPassword); } setAccountAuthenticatorResult(intent.getExtras()); setResult(RESULT_OK, intent); finish(); }
This method gets a fresh auth-token and do the following:
1. Existing account with an invalidated auth-token – in this case, we already have a record on the AccountManager. The new auth-token will replace the old one without any action by you, but if the user had changed his password for that, you need to update the AccountManager with the new password too. This can be seen in the code above.
2. You add a new account to the device – that’s a tricky part. When creating an account, the auth-token is NOT saved immediately to the AccountManager, it needs to be saved explicitly. That’s why I’m setting the auth-token explicitly after adding the new account to the AccountManager. Failing to do so, makes the AccountManager do another trip to the server, when the getAuthToken method is called, and authenticating the user again.
Note: The third argument to addAccountExplicitly() is a “user data” Bundle, which can be used to store custom data, such as API key to your service, right with the other authentication related data on the AccountManager. This can also be set by using setUserData().
After the log-in process done by this Activity, we have the AccountManager all set up with our account. The call to setAccountAuthenticatorResult() returns the information back to the Authenticator.
Now we have the process ready to go, but who will start it? How will it gain access to it? We need to make our Authenticator available for all the apps that want to use it, including the Android settings screen. Since we also want it to run in the background (The log-in screen is optional), using a Service is the obvious choice.
Creating the Service
Our service will be very simple.
All we want to do, is letting other processes bind with our service and communicate with our Authenticator. Luckily for us, the AbstractAccountAuthenticator, which our Authenticator extends, has a getIBinder() method that returns an implementation to IBinder. Our service needs to call it on its onBind() method and that it! The basic implementation takes care of calling the appropriate methods on the Authenticator by the request of an outside process. To see how it’s actually done, you can take a look on Transport, an inner class of AbstractAccountAuthenticator and read about AIDL for inter-process communication.
Here’s how our service will look like:
public class UdinicAuthenticatorService extends Service { @Override public IBinder onBind(Intent intent) { UdinicAuthenticator authenticator = new UdinicAuthenticator(this); return authenticator.getIBinder(); } }
..and on the manifest we need to add our service with the
<service android:name=".authentication.UdinicAuthenticatorService"> <intent-filter> <action android:name="android.accounts.AccountAuthenticator" /> </intent-filter> <meta-data android:name="android.accounts.AccountAuthenticator" android:resource="@xml/authenticator" /> </service>
Simple, huh?
The authenticator.xml, that we link as a resource, is used to define some attributes for our Authenticator. That’s how it looks:
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android" android:accountType="com.udinic.auth_example" android:icon="@drawable/ic_udinic" android:smallIcon="@drawable/ic_udinic" android:label="@string/label" android:accountPreferences="@xml/prefs"/>
Let’s explain some of them:
accountType is a unique name to identify our account type. Whenever some app wants to authenticate with us, it needs to use this name when approaching the AccountManager.
icon and smallIcon are icons for the account to be seen on the device’s Settings page and on the account approve page (more on that later).
label is the string that represent our account when listed on the device’s Setting’s page.
accountPreferences is a reference to a Preferences XML. This will be shown when accessing the account’s preferences from the device Settings screen, allowing the user more control over the account. You can check the stuff Google and Dropbox are letting you change about their account for some examples. Here’s an example of my own:
Random stuff you may want to know
During my investigation I ran into some interesting scenarios that I thought I’d share, keeping your hair intact while working with this API.
1. Check existing account validity – If you want to get an auth-token for an account name that you stored yourself, check that this account still exist first by using the AccountManager#getAccounts*() methods. I’ll quote the AccountManager’s documentation:
“Requesting an auth token for an account no longer on the device results in an undefined failure.”
For me, the “undefined failure” was to bring the sign-in page and then do nothing after I submitted my credentials, so there you have it.
2. First in, first served – Let’s say you copied your authenticator’s code to 2 of your apps, thus sharing its logic, and altering the sign-in pages design on each app to fit the app it belongs to. In that case, the first installed app’s authenticator will be called for both apps when an auth-token will be requested. If you uninstall the first app, the second app’s authenticator will be called from now on (since it’s the only one now). A trick to overcome this will be to put both sign-in pages on the same authenticator, then use the addAccountOptions argument on the addAccount() method to pass your design requirment.
3. Sharing is caring..for security – If you try to get an auth-token from an authenticator that was created by a different app, which was signed using a different signing key, the user will have to explicitly approve this action. This is what the user will see:
The “Full access to..” string is retrieved from the our Authenticator’s getAuthTokenLabel(). You can specify different labels for each auth-token type, being more user friendly on cases like this.
4. Storing the password – The AccountManager is not secured by any encryption method. The passwords there are stored in plain text. You can’t peekAuthToken() to other Authenticators (You’ll get a “caller uid X is different than the authenticator’s uid”), but a root access and some adb commands will do the trick. In the sample code I’m storing the password for the convenience of auto-login the user in case of token expiration. It’s the ultimate trade-off between security and convenience. In most cases I would take the secure road, but for some it’s not worth the inconvenience caused to the user. If someone has a root access and can run adb commands on your device – he can do much more damage than accessing your user’s “high scores” table..
Now what?
Now that you got familiar with this great service, you can download from Google Play the sample authenticator that I wrote. It will allow you to create an “Udinic account” on your device. The authentication will be against a Parse.com account that I created for this cause. These are the options you get on the sample app:
The getAuthToken button will query first all the Accounts from the type “Udinic” on the device. If there’s one, it’ll return its token by calling AccountManager#getAuthToken(). If there’s more than one, it’ll populate them on a dialog and let you choose which one you want.
The getAuthTokenByFeatures calls a cool convenient method on the AccountManager by the same name, that do all the work for you. It’ll query the AccountManager for accounts with the requested type, “Udinic”, and its behavior is as follows:
- There are no accounts: Starts addAccount() to allow the user to add a new account. After that, it will automatically call getAuthToken() on the created account to get the token for it.
- There’s one account: Get its auth token.
- There are 2 accounts or more: Create an account picker dialog and return the token of the account the user picked.
If you want to invalidate the token, you can use the invalidateAuthToken button. Note: The Udinic authenticator knows how to recover from invalidated tokens, as seen previously on the sample code for getAuthToken(). Meaning, after invalidating the token the getAuthToken button will still return a token, but it’ll be only after he asks for it again from the server. You can confirm that by looking at the LogCat and see network communication to the server in that case. Removing the account is possible only through the device’s settings screen.
You can download the source code from GitHub: https://github.com/Udinic/AccountAuthenticator. There are 2 sample apps in there, allowing to play around with them since they both share the same Authenticator. For example: Use different signing keys for them and see the different flow for the user when one is asking for auth-token created by the other sample app. You can also try to create an apklib for the authenticator and reuse it across different apps. If you have a fix or a suggestion – don’t hesitate posting it here or as a Pull Request on GitHub.
OMG! I was trying to do this and I could not find any good documentation about it. This is a great sample and documentation. Thank you.
I do not understand this. Everything I know on web is self taught. Call me illiterate. Please explain in layman’s terms
Hey,
Some of the stuff here requires prior knowledge.
Explaining every concept presented here will get this post be at least 3 times bigger.
I’d recommend reading about OAuth2, to understand authentication methods, and also the links I refer to on this post (they provide further information about the subject).
Good luck!
kindly paste complete source code of working sample
Hi!
First of all I have to say, that this piece of code is really really helpful. Thank you so much for that.
I only stumbled across one issue.
1. I added an account for my first app, which worked like a charm.
2. I reused the same Service for my second app: Getting and invalidating tokens works as expected, also the invalidating part. The only thing I am struggeling with is the “Add Account” – part.
If I press on the button nothing happens. I think there is something wrong with binding the service, but I have not figured out what the issue really is.
Maybe you can me point me in the right direction, why I am not able to open up the “Sign – In” – page in my second app.
Other than that, thanx for your code and the awesome explanation.
br MikeT
Hey
Try to look at the values on the authenticator.xml. These values should be different between different accounts.
Perfect!
What a fast reply. That little change did the trick! Thanx, you saved me from getting millions of grey hair 🙂
All the best. And keep up the good work.
Hey Mike, below it sounds like you were able to make this work by changing authenticator.xml. Could you explain what change you made?
You can 2-way encrypt the password you’re storing in the AccountManager with a device-specific key. It’s recommended that you do that with the auth_token as well, for the same reason that storing the plain text password is insecure. That way, the password and auth_token are never known by the AccountManager. There’s a Android dev blog post on encryption.
thanks for the amazing help
I’ve been searching for an explanation like this. I’ve a little question, if i want that the first time that i open the app, ask me to log in and if the log in is succesfull then open another activity, i need to put the authenticator activity like the launcher activity?
No, that’s what’s cool about this method.
You can start your main activity and try to get an auth token. If there isn’t one – the framework will automatically raise the authentication activity and return the response back to your main activity after the user has successfully logged in.
Hi, i trying to get an auth token inside my main activity, but nothings happen
mAccountManager.getAuthTokenByFeatures(SyncConstants.ACCOUNT_TYPE,
SyncConstants.AUTHTOKEN_TYPE, null, this, null, null, null, null);
the application never display the login activity automatically, what do you think?
Thanks for the great explanation! It’s by far the best i found on the net.
I do have a problem trying to use the code you provided: I get an ActivityNotFoundException for”:
com.udinic.accounts_authenticator_example/com.udinic.accounts_authenticator_example.authentication.LoginActivity”
– when trying to add a new account. I can’t find any place that LoginActivity is invoked, and I haven’t changed the code.
In a previous post you suggested looking at the values in the authenticator.xml. At what values specifically ?
The LoginActivity is probably invoked by the AccountAuthenticator itself. Check there. You need to declare that activity in your app to make it work.
“2. First in, first served ”
I created a library project and created the full suite, the authenticator, the activity and the service (almost like the good the bad the ugly :P).
I’m using the library (a.k.a authlib) in two different projects. I installed both apps in the device.
Almost ALWAYS, only the first app’s “addAccount” from the Authenticator class gets called. Anytime i open the second app and try to invoke the account adding flow, it doesn’t seem to bind properly. I’m not sure why. Any help would be greatly appreciated.
p.s: I’m trying to use 1 account for two apps. But the “adding of the user” should be possible from either of the apps and should not be dependant on the sequence of the installation.
The order of the selected authenticator is OS implementation and unfortunately cannot be changed.
HOWEVER,
I believe you can do what you need following these steps:
1. On the authenticator project, create 2 sign-up activities, having their own log-in flow according to what you need for each app using this library.
2. When calling the account manager to add an account, for any of the 2 apps, you can pass an “options” extra to the method. Pass “APP1” or “APP2” in the Bundle.
3. On the authenticator’s addAccount() method – read the “options” extra and start the appropriate sign-up flow.
I haven’t tried it, but I think it can work.
If you do try this – please report back for other people to learn.
It sounds like this can be fixed by modifying authenticator.xml as you mentioned here: https://udinic.wordpress.com/2013/04/24/write-your-own-android-authenticator/#comment-459. Any specifics on how to modify it?
Hi, udinic
I have tried the solution which you suggested and it doesn’t work. Do you have any other suggestions? Thank you very much in advance.
Hi, thanks for sharing this. It helps me a lot to understand the flow.
There is one thing I do not understand. I understand that in OAuth2.0 you have to provide an client_id to get an accesstoken. It seems that you are not using this. How can this be communicated via the account manager to the authenticator?
The algorithm to acquire the auth token from the server is up to you. If you need to pass a client_id, API key or a CAPTCHA – that’s your responsibility to do that on the authenticator when acquiring the auth token. After that – you just store the auth token in the account manager as any other authenticator will do.
Hi. When i tried both the source code project and the google play sample, the authentication is successful, but always when i access the Udinic account configuration my phone crashes and restarts… In the Logcat it tells about some ‘Error inflating class Switchpreference’. What i am doing wrong?
You probably running this example on an unsupported API level device. Switchpreference exists since API 14, you’re probably running this on device <ICS.
Thanks! Much better than the official docs 😀 You saved my day.
Is there a way to prevent my app from showing up in the Accounts & sync settings?
Why do you want this? I don’t think so.
Looks like that on Android 4.3’s restricted profiles – you can 🙂
http://developer.android.com/about/versions/android-4.3.html
We are waiting for the sync adapter example of yours. 🙂
Hey,
It’s coming real soon. Probably this week.
Fingers crossed. 🙂 Looking forward to it.
This is great – much more succinct and obvious than the main docs. Thanks! 🙂
Pingback: Write your own Android Sync Adapter | udinic
Nice Tutorial
Thanks a lot. Very nice Tutorial with a good example.
Hello,
i am trying to use AccountManager in an Activty under Application (not Service), but without sucess. I use an 4.3 emulator (Intel image). I can easly liist all accounts on the emulator (added via android interface) but when I want to create one an Error comes in:
09-14 13:21:21.863: E/AndroidRuntime(3291): FATAL EXCEPTION: main
09-14 13:21:21.863: E/AndroidRuntime(3291): java.lang.SecurityException: caller uid 10046 is different than the authenticator’s uid
09-14 13:21:21.863: E/AndroidRuntime(3291): at android.os.Parcel.readException(Parcel.java:1431)
09-14 13:21:21.863: E/AndroidRuntime(3291): at android.os.Parcel.readException(Parcel.java:1385)
09-14 13:21:21.863: E/AndroidRuntime(3291): at android.accounts.IAccountManager$Stub$Proxy.addAccountExplicitly(IAccountManager.java:719)
09-14 13:21:21.863: E/AndroidRuntime(3291): at android.accounts.AccountManager.addAccountExplicitly(AccountManager.java:612)
My code snippets are:
Login.java ->
AccountManager accountmanager = AccountManager.get(this);
Account userAccount = new Account(“Test”, “cg.activities”);
if (accountmanager.addAccountExplicitly(userAccount, “pass”, null))
System.out.println(“Added success”);
else System.out.println(“Added failed”);
res/authenticator.xml ->
manifest ->
…
…
I searched trouhg Google and StackOverflow, but without result. Tried it on 2.2 Android phone and the error is the same. Your app works on my phone. Drives me wild :S
To prevent any unnecessary complexity of comments, I opened a question on StackOverflow.
The link is:
http://stackoverflow.com/questions/18801516/accountmanager-doent-allow-adding-a-new-account-in-application-caller-uid-is-d
Author, please delete my other comments. I hope someone could give me a good advice 😉
Greetings.
Hey Thanks for your great tutorial here. can you please explain more about server part of your app. here you said you use parse.com servers. I want to implement my own server. what is the approach for returning auth token?
Hi bro, could you find something helpful regarding server side code. Actually I need some help!
Simply desire to say your article is as amazing. The clarity to your publish is simply great and that i could suppose you are a professional
on this subject. Well along with your permission let me to snatch your feed to keep updated with coming near
near post. Thank you a million and please continue the gratifying work.
If i want add Gmail,Facebook.Twitter etc accounts for that what shout i suppose to do ?
Hey there!
I am just curious if it is possible to use one and the same account for multiple apps. I am able to request account information from a different account, but is it possible to add Accounts to the same AccountManager from different apps?
Similar to what google does with their Gmail account which is used for the mail app, hangout, g+,…
I researched a while and am still not sure if this is possible and if its good practise. I would appreciate if you could give me some input.
best regards,
Mike
You can share the account type between apps if they all share the same signing signature.
If you install 2 apps with the same account type, but signed with different keys, you’ll get a “SecurityException: caller uid XXXX is different than the authenticator’s uid” when trying to add an account from the second app.
Thanks a million……. I was here to implement sync adapter and I get to know about this post. This post is really amazing. Now I am really interested to implement it in my application.
You’ve used the parse.com servers, the problem is I want to implement is on my own server.
Could you please upload some server side code? Some php code?
It would be really really big help for me, and I will be really thankful to you for this favor!
Well worked bro. Keep this stuff coming…. We love you!
Best Wishes!
Hey
I didn’t write any server code for this example, and a server code will be different from one implementation to another. It can also be done in many different languages/platforms.
I suggest you search the internet from some “inspirations” and then implement your own.
Pingback: CopyQuery | Question & Answer Tool for your Technical Queries
Pingback: Android AccountManager permission for a single custom account type | BlogoSfera
It is the first day I do anything related to Android,
The first thing to start with is authentication stuff,
Thanks for this full covered workflow!
always i used to read smaller articles or reviews which as well clear
their motive, and that is also happening with this paragraph which I am
reading now.
Thank you for the full explanation!
I would like to write my own authenticator to use between all my related apps.
I can’t get it to work with more that one app.
When I use your sample code, only the first installed app opens the authenticatorActivity (By clicking the “addAccount” button) and the second app does nothing when clicking this button.
I read others commenting on the same thing. Can you please explain why? I really need this to work for me 🙂
Thank you!
Using the same authenticator will require you to use a library project for the authenticator, and linking to it from any app that you wish using it.
Regarding your problem, have you checked the logs to see if there’s any error?
As I wrote on “Random stuff you may want to know” section 2, the first app to be installed will register its authenticator. Meaning, other apps needs to be signed with the same signing key in order for them to access the authentication data the first app saved.
Hi, This is what I did- The authenticator is in a library and the two apps (Signed the same) are using it.
When I am checking your example its the same- Only the first installed app response to addAccount().
This is what I get in the log:
10-22 11:39:35.437: D/udinic(31466): UdinicAuthenticator> addAccount
10-22 11:39:35.707: D/dalvikvm(31466): JDWP invocation returning with exceptObj=0x41d8f138 (Ljava/lang/NullPointerException;)10-22 11:39:35.737: D/dalvikvm(31466): JDWP invocation returning with exceptObj=0x41d8f6b8 (Ljava/lang/ClassNotFoundException;)
I understand that the first installed app is registering the authenticator but it looks like the second app can’t reach it. Any suggestions?….
Sorry but your uploaded project is slightly unclear. I cannot get the app to work on my device. Do I have to create an entire new project to test example 1 and 2. When I try and runt the main.java files in Eclipse the AuthenticatorActivity.apk is installed but nothing is launched. This is not surprising as the manifest does not include a launcher activity.
How are you supposed to test your example apps?
Hi,
Thanks for sharing your experience with the Android AccountManager. Could you please add a link to the Article in the Android documentation where you took the picture of the getAuthToken() method from? I couldn’t find it by myself.
Thanks!
Hi
First of all let me comment you for a very great tutorial you have put up it is very insightful and helpful. However I have a question and I think you could probably help. I want to do something that will allow the user to create account and the sign in once in an environment where there is a network communication and then stores and encrypt the user’s authentication details on the device for future sign in in the event that the user is in a place where there is no network communication. Can you please advice on how this can be done. Thanks
Hi,
great tutorial. its working like a charm.
now i want to know if you could give me a lead.
i will have a couple of applications, all using one type of authentication.
What i want to do is check if the account exists in any of the applications, and if there is no account created, call the authentication module for create one.
i almost accomplish that but i dont know why android are asking for modification permissions. in both applications. is this correct or im doing something wrong?
regards.
It’s not quite clear what you are trying to do.
When do you check that? When the app first launches? In that case just try to get an auth token and the authenticator will start the log-in activity for you in case there are no credentials stored already.
Please clarify your question.
thanks udinic, i have fix it with the suggestion of “Thanks” and the exported property.
regards!
Hi Udinic,
Its Very very great Tutorial. Really it simplified my understanding about AccountAuthenticator. But still I have one problem regarding following :
1. How to Check existing account validity if the user is already logged in and already that account exists. Your help will be appreciable .
Regards.
Well..you can use the account’s credentials and see that they still valid against the server. If they are not – just invalidate them using invalidateAuthToken, and next the app will try to use them – it’ll ask the user for new credentials.
Pingback: How to implement user login with google, facebook or twiter in android app?CopyQuery CopyQuery | Question & Answer Tool for your Technical Queries,CopyQuery, ejjuit, query, copyquery, copyquery.com, android doubt, ios question, sql query, sqlite query
Thanks for the article, it was very helpful. One thing that is worth noting: you’ll need to add android:exported=”true” to the login and register activities in order to access them from the second application. Without that, you’ll get a securityexception from the second app complaining that the activity is not exported.
Hi,
in your example users password is stored unencrypted in AccountManager.
How safe is that?
I have just implemented Android Authenticator using this blog post and I did not store the password at all. You do not have to put actual password there. I think it is optional.
In this example, when the user token is not valid, it uses that stored password and re-authenticate immediately. If you do not store the password, you just have to ask for the password again.
Then you have to set a long expiration time for the token if you do not want to disturb the user by asking for his password.
Actually I never expire them automatically. I only expire them if I detect bas usage and if user changes his password on the web.
Http request in ParseComServerAuthenticate::userSignUp() is running on the main thread?
Hi,
Thanks for the great tutorial. One thing I wanted to mention:
I think it would be better if you put the explanation of “sServerAuthenticate” and “authTokenType” before the creating activity part. Because in the getAuthToken() method, both of them are used and it was a little confusing (at least for me) not having any explanation about them right after that part.
Very true. I still cant figure out what to do.
Hi,
Thanks for the great tutorial, clears up all the things, but maybe you can help with a related question.
I need to create a client app for an OAuth2 API using a Bearer token for authentication. At the time of obtaining the token, I receive the expiry timestamp for it, but I am unclear about where to store and how to utilize it. Problem is, if I don’t want to have unnecessary trips to the server, the app would realize that the Bearer had become invalid only after it receives a HTTP 401 error from the server.
– Should every network request in my code have a retry mechanism in case the bearer token has become invalid in meantime?
– Can SyncAdapter somehow help with this?
– As I am new to Android development, is there maybe anything else?
Thanks!
The correct way to handle invalid auth tokens is using the invalidateAuthToken() whenever you get a 401 from server.
Doing that, will cause the authenticator to authenticate the user again, instead of returning the auth token again.
Hi,
I want to a Switch as you have prefs.xml. I should be able to read the value from the application, who is responsible for account and not from the settings.
Hi,
I got a “bind failed” error everytime I’m trying to add an account ! The service is declared in the manifest, the right permissions too…Can anyone help ?
Problem solved, I didn’t know that account type must be the package name ! I have two questions (for now) to ask : When an auth token is no longer valid (time elapsed) do I need to “refresh” it “manually” ? How to automatically detect password change ?
Thanks Udinic for this great tutorial. Browsing the code on Github i saw a comment on the AccountAuthenticatorActivity where you said that ” There can only be one AuthenticatorActivity ” from witch you started a second activity (sign up activity) and i was wondering why. Can’t you just start another AuthenticatorActivity from the main activity just for the sign up?
Activities who are extending from “AuthenticatorActivity” has special properties. Starting a new activity like this has implications of information retrieval and status codes code returned to the Authenticator itself.
Every AuthenticatorActivity is linked to the account authenticator and returns information it needs once the user has submitted data in it (such as username/password). You can take a look at the AuthenticatorActivity source code and learn more about its properties and the things it does.
Because it’s a special type of activity, you cannot just start a new AuthenticatorActivity.
Where do you check if the token has expired? I have a server which gives me the timestamp and expiry time. I want to store both these fields into account manager and check if the token has expired. I am not sure – how are you handling the token expiry check?
authenticator.xml goes into the /res/xml directory if you dont have a xml dir you need to make it, that caused me a headache
Hi
Nice tutorial. But i dont want to use oAuth. I have an own login screen and authenticate with ldap. Can you tell me how to create oauth and session id for this case
Pingback: Android AccountManager no account after restarting applicationCopyQuery CopyQuery | Question & Answer Tool for your Technical Queries,CopyQuery, ejjuit, query, copyquery, copyquery.com, android doubt, ios question, sql query, sqlite query, nodejsquery
Has anyone tried accessing the Authenticator from an app which is signed by a 3rd party ? Using the sample code, out of the box, I keep getting “W/Binder(461): java.lang.SecurityException: Activity to be started with KEY_INTENT must share Authenticator’s signatures”
Hi Guss199,
I am also struggling with this issue on KitKat. The same code works with Android versions before KitKat.
These guys on SO have the same problem:
http://stackoverflow.com/questions/20527528/show-fullscreen-access-request-dialog-instead-of-notification-when-using-getau
http://stackoverflow.com/questions/20763010/securityexception-in-authenticator-getauthtoken
If it helps, I tracked down the code generating this error(line 2206): https://android.googlesource.com/platform/frameworks/base/+/master/services/java/com/android/server/accounts/AccountManagerService.java
Help us Udinic, you’re our only hope!
Its a bug!
Someone wanted Authenticators to not send ‘Settings’ type activities in KEY_INTENT ( per the comment) w/o realizing this would also kill the launch of ‘Request Access’ activity, which gets stuffed
as part of the newGrantCredentialsPermissionIntent method.
someone skipped the regression!
If that’s a bug on the newer versions of Android, this could be reproduced by trying to request a Google auth token for services like Gtask or Google Drive.
Can you confirm that?
It works for the Gtask case. The only way I can get it to work is by using ‘customTokens = true’ in my authenticator. With Gtask case, not sure how Google’s Authenticator gets hold of their ‘Grant permission’ activity. If you use the default one ( hard coded in the AccountManagerService GrantCredentialsPermissionActivity.class) you would get the error. Not sure, how is Google is able to supply its own Activity.
Thanks Guss,
It seems as if Google AccountManager/Account work differently from everyone else’s.
On ICS they have this bug, which only affected 3rd-party custom AccountManager’s (from what I can tell, not Google’s) : http://stackoverflow.com/questions/9798163/getauthtoken-not-working-is-ics
I am also happy to implement my own version of GrantCredentialsPermissionActivity….if only I can find out how to trigger it – who knows, this might be a workaround for the ICS bug as well?
Guss199: Can you please share some code how you did it with ‘customTokens = true’? I’m currently struggling with it.
i whould like to sync contact like what’sapp .but i confuse in server side concept because i download whatsapp at first time and its show all contact which have already what’s app user please help me how to apply this concept in my application. do you have any idea and reference pls suggest me.
I take pleasure in, result in I found just what I was taking a look for.
You have ended my 4 day long hunt! God Bless you man.
Have a great day. Bye
Hey Udinic can you please tell me how you are getting the API key and Application Id in the Syncadapter example and why is it fix for all the users……
Hi Udinic,
I have two applications which are signed by the same signature. In order to get/set user data the AccountManager documentation specify that both apps should have the same sharedUserID in the manifest file but when i do that i cannot seem to update the applications due to different sharedUserID. the other weird thing is that when i’m not using sharedUserID at all everything works fine as opposed to the documentation.
Will it be safe to do it without the sharedUserID?
You need to have the same shared user id for that to work. Try to uninstall and reinstall your apps again after you set their sharedUserId to be the same.
Thanks for the reply.
I’ll try to explain more.
i have two applications that share the same authenticator code.
Both apps are signed with the same signature.
UID in the manifest file is not set for any of the apps.
I cannot uninstall and reinstall the apps or change their UID in the manifest file as they are both already published to google play.
AccountManager.getUserData() and AccountManager.setUserData() documentation state that in order to call them the caller UID should be the same as the authenticator UID in addition to the same signature.
If i try to call AccountManager.getUserData() from any of the apps i don’t get any exception even though they don’t share the same UID(it contradicts the documentation).
Am I missing something here?
Thanks.
There is a library Android AtLeap which has helper classes for Account Authenticator. Here is its URL https://github.com/blandware/android-atleap Take a look at it.
Please tell me how to set Auto authenticate for facebook in my app activity settings????Please mail me msaiaditya29@gmail.com
After I initially left a comment I appear to have clicked on the -Notify me when new comments are added- checkbox
and now every time a comment is added I
get four emails with the same comment. Perhaps there is an easy
method you are able to remove me from that service? Thanks a lot!
How does this approach work when using account types for 3rd parties? For example, lets say that I want to authenticate against LinkedIn and the LinkedIn app isn’t installed, so there is currently no LinkedIn account available. I can create the authentication logic the way you have shown in this tutorial, but then wouldn’t I have to set the icon, title, description, etc. for the LinkedIn account? Then if the user eventually does install the LinkedIn app, there would be 2 LinkedIn accounts – one official, one unofficial. Any thoughts on this situation? Thanks.
Well..if you want to use the other app’s logged-in account, you need it to be installed first. If you want to create *your own* account that connects to their server and retrieve an auth-token, you can do that too, but it’ll need to be a different account type than the one they are using, otherwise – installing their app will lead to a security exception.
Linkedin is not a good example, since I know they don’t use an authenticator (at least they didn’t 6 months ago), but let’s take Dropbox as an example. If you will create an account type with exactly the same name as they did, there will be a problem installing Dropbox after your app, since they both need to share the same account type and both have different signing keys, which is a security problem.
You can try that and see for yourself what happens in such situation.
Thanks. I was trying to figure it out from the android docs but this was far more elucidative!
Pingback: Android: How to implement an account login system? | questions android
Well done for the great work and the comprehensive tutorial. Simple, relevant and functional; as all things should be.
hello udinic i cant sync my application with contact…i required to syncr contac with my apps…like whatsup install in my phone i get in all contact which one use ….so do this type of synchronization pls help me its a very important for me…
Hi Udinic,
Really thanks for this nice tutorial.
I am able to run it.
But I see my app in accounts only when I add it manually.
How can I achieve it without adding manually, as other app comes.
Thanks in advance.
Grea site. A lot of ueful information here. I’m sending iit to a few buddies ans additionally sharing in delicious.
And of course, thasnks on your effort!
U deserve a beer!
hy thanks for your awesome pose..
how can I call the addAccount method as blocking, thanks
What’s Taking place i am new to this, I stumbled upon this I’ve found It positively
helpful and it has aided me out loads. I’m hoping too
give a contribution & aaid other customers lile its aided me.
Good job.
Hey I know this is off topic but I was wondering if you knew of any widgets I could add to my
blog that automatically tweet my newest twitter updates.
I’ve been looking for a plug-in like this for quite some time and was hoping maybe
you would have some experience with something like this.
Please let me know if you run into anything. I truly enjoy reading your blog and I look forward to your
new updates.
Hey, thanks for this great tutorial. I hope you’re still looking at comments because I’ve been fighting with this for a couple days now.
I’m using getAuthTokenByFeatures() as it’s supposed to encapsulate all the logic of using an account if it’s already there (and there’s only 1), or showing a picker to the user if there’s more than 1, or creating the account if there are none. I’ve found that last statement a little misleading as I still had to do the addAccountExplicitly() in my AccountAuthenticatorActivity. However, once I did that, it worked… mostly.
If AccountManager has a token cached (and thus doesn’t even need to call my authenticator), then my callback I specify on the getAuthTokenByFeatures() gets called with the AccountManagerFuture. However, if it DOESN’T have a token cached (or I need to create a new account, both behave the same), my callback NEVER gets called. The account is added in AccountManager, and next time I come in it’ll work because the token is cached and it doesn’t call my authenticator. But any time my authenticator is in the picture, the callback isn’t called.
This is the code I have at the end of my authenticator’s getAuthToken():
// Didn’t find a valid token. Return a response that’ll show the login screen.
Intent intent = new Intent(mContext, LoginActivity.class);
intent.putExtra(Constants.KEY_AUTH_TOKEN_TYPE, authTokenType);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
Bundle result = new Bundle();
result.putParcelable(AccountManager.KEY_INTENT, intent);
return result;
And this is the code I have in my AccountAuthenticatorActivity that should return the result:
// Done. Tell the AccountManager what we got/created.
Intent intent = new Intent();
intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, mEmail);
intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, accountType);
intent.putExtra(AccountManager.KEY_AUTHTOKEN, loginResult.getAuthToken());
intent.putExtra(KEY_LOGIN_RESULT, loginResult);
setAccountAuthenticatorResult(intent.getExtras());
setResult(RESULT_OK, intent);
finish();
Hy bro !
i am new in android. How i can do drive api authorization for android application.?
I saw google drive authorize tutorials but no thing find help full.
plz help me I will be very thank full to you.
Hi, I would know where does your variable mAccountManager come from in the login activity ?
I tried your code by using this in finishLogin method :
AccountManager mAccountManager = AccountManager.get(this);
But I have a SecurityException error (caller uid different thant authenticator’s one)…
I think I musn’t declare the variable here, but where ?
I like the helpful info you provide on your articles.
I will bookmark your blog and check once more here
frequently. I’m fairly certain I’ll learn a lot of new stuff right right here!
Best of luck for the following!
Hi, Udinic,
I am getting false from “getIntent().getBooleanExtra(ARG_IS_ADDING_NEW_ACCOUNT”
when login complete succesfully .
But the method ::
private void finishLogin(Intent intent) {
if (getIntent().getBooleanExtra(ARG_IS_ADDING_NEW_ACCOUNT, false)) {
}else {
Log.d(“Rufaida Medical”, TAG + “> finishLogin > setPassword”);
mAccountManager.setPassword(account, accountPassword);
}
}
my code always execute else block. please describe me how can I get value true of “getIntent().getBooleanExtra(ARG_IS_ADDING_NEW_ACCOUNT”
I am waiting for your response.
Thanks
Hossain
It’s inn fact very complex in this full of activity lofe to listen news on TV,
so I just use internet for that purpose, and take thhe moset up-to-date news.
I’ve learn some good stuff here. Definitely price bookmarking for revisiting.
I wonder how much attempt you set to create
this type of fantastic informative website.
Hi Udinic,
Thank you for this great sample! I would love to have latest version of this code. I download the apk which works fine but the code in github doesnt let me create new user
hi!,I love your writing very much! share we communicate extra approximately your article on AOL?
I require an expert on this area to solve my problem.
Maybe that is you! Looking ahead to look you.
Hi Udinic,
Thanks for a great tutorial. I have a query. I need to know what configurations do we need to do on Parse.com side. Actually, I have to present a demonstration to someone with sync adapter and account authentication.
This is BEST article so far for the introduction of android Account Management!
THANK YOU!
What’s up Dear, are you actually visiting this web page daily,
if so then you will absolutely obtain pleasant knowledge.
Pingback: [转]launchAnyWhere: Activity组件权限绕过漏洞解析(Google Bug 7699048 ) | 陈影中心
I’m implementing my own authenticator and I found a strange possibility and I was curious how you handle it.
Let’s say I invalidate an auth token for account “gator”, then I show an account selector to select an account and I choose “gator”. Then I try to get gator’s auth token. The auth token doesn’t exist so Android launches my registered AccountAuthenticatorActivity.
The thing is, once I’m in AccountAuthenticatorActivity, I can enter any username, not just “gator”. So I could end up logging in as “crocodile” and then crocodile’s auth token gets saved under gator’s account. How would you deal with this? Should the AccountAuthenticatorActivity auto-populate the username and prevent the user from entering a different username when its being used to renew a token on an existing account?
You don’t even need to show anything to the user. If you’re not expecting for any input from the user, just do your thing and call finish() to close the activity.
hi while running this source code in android,it shows an error “Android library projects cannot be called launched “.how can i resolve this???pls kindly help me out
HI ,
I’ve try to convert it for android studio, but I can’t build it 😦
Anybody can help me ?
Pingback: Android OAuth2 Bearer token best practices | Zourkos Answers
Reblogged this on Laaptu.
Hi Udinic,
Thanks for a great tutorial !
could we implement authenticator same account type but diffirent auth token type for each application. I tried but as you said, first in, first serve, so i can’t get token with diffirent auth token type for second app.
What I would recommend is creating one authenticator to support both auth tokens, then add it as a library to both of you apps. That way, it doesn’t matter which app was installed first, they will both support the 2 auth token types.
Pingback: Accountmanager vs sharedpreference for authentication and server communication | Alysa
Pingback: Authenticator | Pursuing Software Ideas
Pingback: Contacts | Pursuing Software Ideas
great job ,
can u have any video tutorial…because account manager in anadoid have no video on youtube…
i hope you upload and discribe all things of Accountmanager….
Thnks alot
A very confusing tutorial. Putting the pieces together is as hard as in the original documentation.
Hi, thanks first of all for this amazing tutorial.
I am trying to develop a secure account authenticator which stores the users password, but i am having the following troubles.
If two applications installing an account authenticator for the same account type, only the account authenticator of the first app is installed.
However, if the first app is uninstalled, the account authenticator of the 2nd app takes its place. (I just tried that and even if the signature of 2nd app is different, calls like ‘AccountManager.getPassword’ which would usually crash as the UID is different, are working)
How can i prevent that – given my app is uninstalled – no other already installed malicious app can take over my account and potentially read out my users credentials?
They can’t take over your account.
They may be able to create an authenticator with the same look and feel and same account type, but it won’t be signed with the same signature as your app, so they won’t have access to the important stuff.
Feel free to try it:
1) Install an app that creates an account, like whatsapp. Create an account.
2) Write an app that installs an authenticator for the same account type as whatsapp and tries to access UID-protected methods like AccountManager#getPassword() -> your app will crash
3) Uninstall the original app
After 3) the account created in 1) is still around, but now your application can access that account, e.g. execute getPassword() without crashing. It seems that the ownership of the account-type was automatically transferred to your app installed in 2)
Whatsapp does not store any password in the account, but I am sure there are plenty other apps that do.
Pingback: Android Account Management – How to create different types of accounts? | 我爱源码网
Awesome tutorial…..really helped me
Pingback: how to make multiple Account And where Account Data Stored in Database? how? | 我爱源码网
HI Udinic!
Thank`s for you posts!
Please tell me I am, in my situation:
I start activity for sending message (ActivitySending), but before do it, i test if User has AuthToken.
I do it with call method “getTokenForAccountCreateIfNeeded”.
And that is great!::
– if User has AuthToken, in “public void run(AccountManagerFuture future)” I get AuthToken and send User`s message to server
– if User has not Account (so no AuthToken) will show AuthenticatorActivity for authenticating. And when user has made the authentication and get AuthToken, On idea, to be executed Method “public void run(AccountManagerFuture future)” and sending message. BUT in AuthenticatorActivity, when user authentication ended , is call method “finishLogin(Intent intent)”, that contains the “finish()”.
And, So, after authentication, AuthenticatorActivity is close and for some reason close ActivitySending without sending the message!
Please, tell me how can I avoid closing the parent ActivitySending and avoid interruption send a message.
Thanks this was definitely helpful, however it still barely scratches the surface. I really wish the documentation had more info about using the confirmCredentials, updateCredentials, etc.
Agencja PR opiekuje się więc całym otoczeniem informacyjnym wokół podmiotu – jest to
opieka, która jest w dzisiejszych czasach rzeczą niezbędną.
hi, can you provide the complete code at one place for newbies like me?
Hi, awesome tutorial.
One question. I tried the mAccountManager.invalidateAuthToken() method and was expecting that it removes the token from the manager. After executing this, I executed getAUthToken expecting that there is no returned authToken or at laest a different (newly generated) one. Am I incorrectly understanding the function of invalidateAuthToken?
Background: I tried to integrate this into my app which has multiple users. My logout method calls invaliadteAuthToken but nothing happened. Ideally I want to invalidate the current token and return to the login activity so that another user can login. How would that be possible?
Thanks a lot
It depends how you implemented the getAuthToken method. In my implementation, I save the user/password internally and if there’s no valid token – I authenticate using these credentials. This means, that unless the password has changed, you won’t be prompted to enter your credentials again.
The value of the token itself depends on the server. Sometimes, the same token will be returned by the server, given the same credentials. The invalidation in the account manager will only invalidate the use of the current token in the account manager. It won’t invalidate it on the server.
Thanks for the quick reply.
I studied the different getAuthToken() methods and most of them would indeed use the saved password to request the token again from the server.
In your example I deleted the places where you store the password (i.e. wherever you add the “PARAM_USER_PASS, userPass” to a bundle) expecting the app to prompt for a new login. But it did not happen.
Even without taking out those lines I would have thought that the app would run through the userSignIn() method in the ComServer class using the stored password to request a new token since the getAuthToken() api says:
“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. ”
What am I not getting here?
Thanks again for you help!
If I remember correctly, it’s not automatic. Either you need to start the authentication activity by yourself or need to call a method to do that for you. Not sure which one will do the trick though..
If you find a solution for this – please post here for us to learn.
Pingback: Download aplikasi Authenticator Sample App gratis untuk android
Pingback: (翻译) Android Accounts Api使用指南 - 有Bug
Hi, thanks for the great tutorial.
I’m developing an app which has one token type and one account type.
Can you tell me what is the right way to make sure that there is only one account connected to the app anytime.
Thanks a lot
What about the abstract methods confirmCredentials, getAuthTokenLabel, updateCredentials, hasFeatures, and editProperties? You just throw UnsupportedOperationException when they are called? See http://pastebin.com/ABXLeEVL
And thank you a lot for this marvelous post. After years, this is the best reference to account authenticator.
Thanks for the great tutorial!
There is a recommended way to add the option to skip the authentication.
I mean that the user can log in and will get all the features.
But if he decides not to, he will be able to use the app in restricted areas.
Thanks for the tutorial. The only problem I’ve got is that I can’t find the declaration for sServerAuthenticate.
never mind, didn’t see the static import :p