Sharing oauth token between my apps on Android - Shared User ID, Keychain, AccountManager or something else?
Asked Answered
C

2

6

I'm trying to work out the best solution for a particular situation I'm in, and am having trouble working out the best option. It's a tricky setup, so might be a fun challenge for you Android experts! Here's my situation:

  • We have two Android apps already on the Play Store, and are working on another right now.
  • The two released apps are signed with the same keystore, but do not currently have a sharedUserId set in their manifest files.
  • The two released apps store a user's oAuth token in SharedPrefs, some product/content data in an SQLite DB, and some audio/video content in external storage (using getExternalFilesDir).
  • The apps are all separate oAuth clients/applications of our server (i.e. they all use different client ID and secret keys).
  • Our server is setup to only allow one oAuth token per oAuth application (i.e. Android app). e.g. if a user logs in to app A on one device, then logs into app A on another device, the first device's token will be invalidated and the app will receive a 401.
  • We've successfully implemented a single sign-on system on iOS by enabling shared keychain between the apps. If an app detects that another app in the group has a valid oAuth token, it can send that to our server and exchange it for a valid token for that app.
  • In the iOS version, we wanted to ensure that the apps didn't need to know about the existence of the other apps using hardcoded values per-app (e.g. if we release a new app in the future, other apps don't need to be updated to share/receive tokens with it), so we created an entry in the keychain containing an array of bundle IDs that had valid tokens, that all apps could access. When an app successfully logs in or exchanges a token, they add their own bundle ID to that array. Any newly installed app could find a bundle ID from that array, and use that it as a key for loading the token details for that app, which it would then exchange for its own fresh token.
  • We want the token exchange to be automatic and not require input from the user.

I hope that all makes sense!!! Please let me know if not.

I'm now trying to work out the best method for storing oAuth tokens (plus some additional data such as email address to go with it) on Android so that other apps owned by our account can access them in order to exchange for a fresh token.

I've looked into using the following, but am unsure of the best route:

  1. SharedPreferences along with sharedUserId
  2. AccountManager (https://developer.android.com/reference/android/accounts/AccountManager.html)
  3. Android Keychain (https://developer.android.com/reference/android/security/KeyChain.html)
  4. ContentProviders

The problem with option 1. seems to be that setting the sharedUserId after first release will lose access to all of the data (see http://java-hamster.blogspot.jp/2010/05/androids-shareduserid.html). This is not a nice thing for our users.

Option 2. (AccountManager) could be a good option, but if we want to store the tokens separately (per-app), but want any other apps we make to access their tokens, I'm not sure how we'd do that.

Option 3... is it possible to do what we need with Keychain?

If I understand correctly, option 4 would need each app to have its own ContentProvider? I'm not sure how that would work for our requirements.

If anyone has gone through this kind of situation and could share some insights and recommendations, I'd really appreciate it!

Cyprus answered 23/2, 2017 at 9:6 Comment(0)
K
5

ContentProvider is probably your best best. I won't be able to provide the whole code for you to do this, but this is generally how I see it working:

  1. Each app creates a content provider that exposes their own API token;
  2. Each app tries to acquire and query (sequentially until successful) all the other content providers before requesting a login;
  3. If app X is able to acquire and retrieve the token from app Y (via Y's content provider), then store it in app X and use it;

Side notes:

  1. This is very sensitive information, so you should enforce security. Your content providers should only be accessed by other apps signed with the same key, they should have only read permissions and you should create your own custom permission as well.
  2. The content provider does not need to access an sqlite database. It can access whatever your using to store the token (which I hope is stored in a secure way, but I'll leave that to your own judgment)

Useful links:

  1. How to create a custom content provider

  2. How to secure your content provider

  3. Example of content provider that retrieves data from shared preferences;

Karee answered 23/2, 2017 at 10:42 Comment(3)
Thanks for your answer, @joao. I'll look into this in the next few days and will let you know how it goes. :)Cyprus
@Cyprus How did you solve the problem in the end? Did you use ContentProvider or solved it using any other method? I am having the exact same problem.Yul
@KashishMalhotra I'm afraid I don't remember now! I just checked our codebase and it seems we ended up not implementing this for some reason. Sorry!Cyprus
S
3

AccountManager is meant to solve the exact problem that your are commenting. Here is a good tutorial to work with it: http://blog.udinic.com/2013/04/24/write-your-own-android-authenticator/

Just be sure that you sign all your apps with the same keystore, because this is the only thing that might complicate things (a lot).

Using preferences will lead you into a problem as all that you store in the main thread is not guaranteed to be there in a Service (like a SyncAdapter). There used to be a trick for this in the form of flag (MULTI_SERVICE) but was deprecated in api 23.

ContentProvider is of course possible (its too generic) but AccountManager will help you to cover the corner cases related from refreshing tokens and other interesting stuff.

Santonin answered 14/3, 2017 at 13:31 Comment(1)
If an app is already on PlayStore and most of the users will already be logged into their apps, how would this solution work?Yul

© 2022 - 2024 — McMap. All rights reserved.