25

How can you uniquely identify a user who has installed your app so that:

  1. You will know it is them if they delete and reinstall your app;
  2. You will know it is them if they install your app on a second device they intend to use simultaneously?

Just as an example, I see that the Netflix app will automatically link to your desktop account without any user interaction. I'm guessing that they use accountManager.getAccounts() or similar method, because they also require the GET_ACCOUNTS permission. But of course that permission is marked as Protection level: dangerous. Is there any technique to do this that is less invasive or potentially alarming?


The key to answering this is to be both simple (for the user) and minimally invasive. Android provides heaps of ways to identify users and many of those ways involve piercing a user's privacy, and if that is the only way, I will do what I do now (optional email registration). I just want a way for my app to know if a user already is registered in my system across installs without having to interview the user (username/password, email address, third-party OAuth, etc).

My main reasons are:

  1. I don't want support requests from users who orphaned their content after a reinstall; and
  2. I don't want to host lots of orphaned content.
Andrew
  • 14,204
  • 15
  • 60
  • 104
  • I would think that this is one reason why `GET_ACCOUNTS` hast the `dangerous` level. You would somewhat be collecting personal information if you keep track of who has the app installed based on accounts. And as there is basically no connection between simultaneously used devices despite the linked accounts I can not think of any way. – chris-pollux Feb 07 '17 at 05:49
  • I completely agree. Here though, the use case is very common -- think of a photo app. Users install the app, I create an account, they create content, I associate it with their account -- some of which is private, some of which is shared. I want the account to persist across devices without frightening users with "scary permissions". What I wish Android had was a "GUID" that was unique to a user for each publisher, so that `com.mycompany.app1` and `com.mycompany.app2` saw the same value or something like that. But I can see why that currently wouldn't work. – Andrew Feb 07 '17 at 06:14
  • In that context it would be useful, you are right. On the other hand it might also scare users having their partly private content appear "magically" on another device. Maybe if you explain the permission thoroughly during the permission request it would help to take away the scariness. This would also allow the user to choose if he wants his content confined to one device. – chris-pollux Feb 07 '17 at 11:47
  • Maybe SMS auth using Digits will be more acceptable than OAuth over social networks? Just a suggestion – Dmytro Rostopira Feb 19 '17 at 19:36
  • Thanks. I appreciate your comment. SMS means user must expose their phone number to you, which is pretty invasive. It also has the same "Protection level: dangerous" as `GET_ACCOUNTS`. And, phone numbers are increasingly leaky -- if you ever get a new phone number and received heaps of calls/SMS from complete strangers, you quickly discover that. – Andrew Feb 20 '17 at 04:41
  • @Andrew This question has been asked so many times and has been beaten to death for years, what makes it worth asking again? – tar Feb 21 '17 at 14:50
  • 1
    @tar, with respect, there are variations of this question, yes, but my exact question has to do with a) minimal permissions (don't alarm the user) and b) respecting user privacy. If you can find this exact question on SO, please post it below or flag it as duplicate. If you disagree that this is the question I am asking, I don't know what to suggest. I'Ve tried to make this question as clear as possible. – Andrew Feb 22 '17 at 05:18

6 Answers6

10

Have a look at Firebase Authentication. It's quite seamless and does not require much effort to incorporate. Also it does not feel intrusive or cumbersome to the end user.

Here is a video tutorial by Google.

EDIT: In case your users are sure to have a cellular device with a phone number, you can use AccountKit. It is also what they call OTA (One Time Authentication). AccountKit uses just the users phone number to verify and validate users.

EDIT: Firebase Authentication now features 'Phone Verification' which is similar to AccountKit mentioned above. Both are good services. However, Firebase phone verification lets you make your own UI from scratch (which means a lot better control than AccountKit). Also, if you don't want to make your UI, you can always use FirebaseUI

Abdul Wasae
  • 3,614
  • 4
  • 34
  • 56
  • 2
    I am going to accept this answer because I think it brings me closest to my goal, though there are some really interesting ideas below. The cross-device notification system suggested by @assem-mahrous is most interesting (though I'm not sure it is persistent/deterministic across installs). What decides it for me is Firebase permits "anonymous login", so users can get into the app immediately, explore, build up their own content, and then when they feel invested and trust it, they can upgrade their identity to their Google account (or FB or email/password). And they can enforce single-account. – Andrew Feb 23 '17 at 08:57
  • 2
    @Andrew when you send push to user you can obtain an array of users how uninstall your app by silent push and handle it in your app that no notification appears to the user you just get the response of success and failure – Assem Mahrous Feb 23 '17 at 10:06
4

i have implemented something that seems little similar to your thing by push notification , i can get error if user uninstalled my app(and from the registration id i get the user) , and if he re installed he obtain a new registration id , and try to get the user UUID for different devices

Assem Mahrous
  • 411
  • 3
  • 15
  • thanks for that. Are you using Firebase or still GCM to receive the UID? Also, do you know if an unauthenticated user will receive the same UID on two devices she owns (e.g. phone and tablet)? I am pretty sure that this would not be possible with the public API, but if Google uses the private API, it might be possible. – Andrew Feb 23 '17 at 10:25
  • firebase but implemented on my backend by laravel , every user get a unique Token , and if he uninstalled and reinstalled the app he will get a new Token , the only unique thing is UUID i even get it before registering to my app to be able to track him – Assem Mahrous Feb 23 '17 at 10:52
3

I think the simplest way would be using UUID and storing the hash on sharedPreferences. You should generate the UUID as earlier as possible in your app.

sharedPrefs = context.getSharedPreferences(APP_SHARED_PREFS,Activity.MODE_PRIVATE);
if (sharedPrefs.getString("YOUR-KEY-TO-THE-UUID") == null || "".equals(sharedPrefs.getString("YOUR-KEY-TO-THE-UUID"))){
    prefsEditor = sharedPrefs.edit();
    prefsEditor.putString("YOUR-KEY-TO-THE-UUID", UUID.randomUUID().toString());
    prefsEditor.commit();
}
  • This will not work if user uninstall application from device. SharedPreference data will be deleted if user uninstall application – dev_kj Feb 22 '17 at 17:57
1

I think that the best way would be implementing login with Google or Facebook. This is quite seamless for users, safe enough (as Google and Facebook considered trusted), you do not need to implement your email registration and you will have identity across devices.

Alex Radzishevsky
  • 3,416
  • 2
  • 14
  • 25
  • Thanks for the answer. If they don't have Google / Facebook? I guess I can use Twitter. No Twitter? I guess maybe Foursquare. No Foursquare? Then Weibo. Then Etsy. Then Fitbit, Mixi and Netflix. It is a path of infinite regression. Then there is the question of whether users are comfortable with connecting you with their OAuth identity -- and if you are comfortable connecting your application & user base to that provider. I personally never feel comfortable clicking that button (SO is the only thing I currently have connected anywhere). I think it gives up too much with too little coming back. – Andrew Feb 19 '17 at 11:07
  • this is for you to decide, I just suggested option that is saferst for users and works in most cases – Alex Radzishevsky Feb 19 '17 at 13:50
  • 1
    @Andrew, the way you think, nothing's ever going to work for you. If you own an Android device you have to setup it with Google account anyway. If someone has some weird rooted Android images running and no facebook or twitter account then we are talking about a needle in the haystack already anyway. And it's totally not an infinite regression. You provide a couple easy options for users if they don't have neither of these accounts they'll just have to register using their email. Oh and what if they don't have an email? :) You know what I'm saying? – Andrej Jurkin Feb 22 '17 at 15:10
  • @AndrejJurkin, I get people's annoyance with this and I get what you are saying. I'm just trying to *find the best balance* of functionality, ease of use, ease of support and privacy, and I've seen some good suggestions here. I'm always disappointed but not surprised so many devs never consider the privacy costs of social sign-in. And internationally, it def. is infinite regress: FB is blocked in China; Weibo is unheard of in Africa; Twitter is frequently blocked in the ME. Plus, most social sites allow you to delete linked apps which is great for user privacy but a footgun for support. – Andrew Feb 22 '17 at 16:23
  • 1
    @Andrew, what exactly is a privacy cost of using Google sign in ? :) You always know what you will be sharing and if you don't like the requirements, then the app is not probably suitable for you. By default you share just very basic info. I'm always happy when I see google or fb sign in button in the app. So i can quickly set up the account instead of wasting time filling up some registration forms. – Andrej Jurkin Feb 22 '17 at 16:28
  • @AndrejJurkin Google sign-in is less of a prob, since they own the ecosystem, but I hope FB & Twitter is more obvious. But, even with Google, a legit question some people (including me sometimes) ask is, Why does your app need to know **a single thing** about me? In this case, I have a technical motivation. And I could explain it to users in an FAQ, but every paragraph costs me extra cash in translations. I would prefer it to "just work" and raise no troubling questions in **anyone's** mind. Give people no reason to distrust you. And a registration form is not the only alternative. – Andrew Feb 22 '17 at 17:00
1

If your app is Android only and you'd like to provide identity without any account creation for the user, I believe using Google Account name/id is the best choice (Accessing Google Account Id /username via Android) since you have to use Google Account on Android phone (unless you root it, delete Google Play Services etc).

If you'd like to only address the first point of your question (identify after reinstall) there's a Device Id -Secure.getString(getContext().getContentResolver(), Secure.ANDROID_ID); though it's not 100% reliable (f.e Factory Reset resets this value)

Community
  • 1
  • 1
krp
  • 2,247
  • 17
  • 14
  • Thanks for the answer. I think `Secure.ANDROID_ID`, is getting closer to an answer that meets my concerns in reinstalling the app or clearing the device's data, though the factory-reset problem is a fly in the ointment for sure. I assume that a single user's `ADROID_ID` will vary from device to device. – Andrew Feb 22 '17 at 04:58
0

The standard for achieving this sort of functionality is through the use of JSON web tokens (JWT) in conjunction with standard restful api traffic.

Assuming your android application interacts with a RESTful api for all crudlike operations and business logic, then using a JWT as an authentication identifier to your api can work quite well. You can embed information in each JWT allowing you to identify whatever you like (the user id in the database, the device id of whereve the user logged in from, etc). A JWT is essentially a datastructure allowing you to store information to be used by the API.

Some basics for how this works:

  1. Getting the JWT into the app: A user logs in to the application using their username/password. The api then returns an encrypted JWT to be used by the client for all future requests. Don't try to do the encryption yourself. Any language that can handle serving an api will have libraries for this.
  2. Using information in the JWT: The JWT is itself a datastructure. For example, it might look like this:

    { user_id: 1, device_id: 44215, device_os: android, }

    Your api will decrypt the JWT when it is supplied for authentication via the request header, and then have that information available in the context of the session.

If you provide the language used by your api then I might be able to recommend a library.

I will conclude by referring to the final requirement you submitted which states essentially that you do not want to have to interview the user across installs. If I understand your meaning, that you want a user to be able to simply install the application and begin using it without supplying authentication credentials, then there is no way to achieve that securely. You might be able to come up with a hackish way to get it to work, but it will be fundamentally insecure.

melchoir55
  • 6,842
  • 7
  • 60
  • 106
  • JWT are fine for maintaining state across sessions (though as many have explained elsewhere, JWT are inferior for maintaining state persistence.) My question though is specifically about maintaining identity across fresh instances. This exactly is my question and I've already provided a way in which it is done, securely and non-hackishly, by NetFlix. – Andrew Feb 22 '17 at 04:48
  • When netflix detects you have the desktop version installed (or on an android app detects the android app is installed), then uses the credentials found in that application to grant access via a browser, it is almost certainly simply retrieving a JWT from the local app and using that to authenticate. It may request a new unique one to differentiate itself from the desktop app for analytics, but the point is that you need to interview the user at least once on each individual device for the sake of security. – melchoir55 Feb 22 '17 at 04:59
  • If your concern is just persisting the JWT between installs of the application, then you must store the data outside of the "normal" application. For example, you could create an encrypted file on the SD card or filesystem. – melchoir55 Feb 22 '17 at 05:03
  • FS storage is certainly one solution to this. I would say that it offers the same advantages and disadvantages as `Secure.ANDROID_ID`, except that additionally, the required permissions are marked as `Protection level: dangerous`. Personally, I feel somewhat safer with `WRITE_EXTERNAL_STORAGE` than `GET_ACCOUNTS ` because so many apps use it that I almost expect to see it when I install something. – Andrew Feb 22 '17 at 05:30
  • I wouldn't use Secure.ANDROID_ID for this. Firstly, in my opinion it would be insecure. Secondly, as you suggest, I think it seems much less intrusive from a permissions standpoint to be writing to the filesystem. Thirdly, even if you ignore the security implications, it isn't as reliable. That value can change in uncommon circumstances and is generally not as reliable as your own filesystem-style implementation would be. – melchoir55 Feb 22 '17 at 05:38