How can I get any unique id flutter android and ios - android

I need to get a unique information of my device. This may be the mac address, it may be the imei address, or something different, ios and android return a constant value after a certain version and do not give the unique id.
The unique id as currently used changes when the application is deleted and reinstalled. I have a chat system and I don't want some members to come back.
Is there a way to access a device's unique information by obtaining a permission if necessary?

you can use a package called firebase_messaging and get device token
String deviceToken = await FirebaseMessaging.instance.getToken();
To get the IMEI of user phone device_information
String imeiNo = await DeviceInformation.deviceIMEINumber;

The token is generated per the client app instance.
On initial startup of your app, the FCM SDK generates a registration token for the client app instance.
See Best practices for FCM registration token management for details.
Only one user at the time is logged in on the app instance. Delete the token when a user logs out and then you get a new token when a new user logs in.

Related

Handling refresh tokens for FCM device groups

I am trying to implement Firebase cloud messaging in my Android app through a Node.js server and I have got stuck at a usecase.
I saw the Firebase tutorial of creating a device group using registration tokens to send messages/notifications to all devices with the same user logged in, what I don't understand is what happens when one of the registration tokens get refreshed by onTokenRefresh() method.
How will I distinguish which token to change as all will be belonging to the same user?
Update:
Ok, so now I have got stucked on another blocking use case. I am creating a user group identified by the user id from my server. If user uninstalls and reinstalls the app immediately and another user logs in on the device, if I call a gcm message on the previous user group this device still receives it.
Is there any way for the gcm to identify is the device it is sending the notification to is logged in or not and if it is, is it logged in with the same user as for the group?
There is another way to solve this problem using Cloud Firebase Functions.
How will I distinguish which token to change as all will be belonging
to the same user?
Using Firebase Functions, you don't have to. Within onTokenRefresh(), you send the new token to the server.
For Example:
The user has 3 devices, each of which have a token that has been sent to server.
*** deviceTokenA/B/C represent UIDs of the token ... we do not know what they are, or which device they belong to.
UserId:
Device Tokens:
deviceTokenA: true,
deviceTokenB: true,
deviceTokenC: true,
Now, the User is on the device that triggered deviceTokenA. The token is refreshed, and onTokenRefresh() is called, sending the token to that collection.
onTokenRefresh() {
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
sendTokenToServer(refreshedToken);
}
sendTokenToServer(String refreshedToken) {
// send to Firebase or Firestore Database, put in the Device_Tokens collection. }
Now, you will have 4 tokens in the collection.
UserId:
Device Tokens:
deviceTokenA: true, // this one has been "unregistered"
deviceTokenB: true,
deviceTokenC: true,
deviceTokenD: true, // this one has been registered.
The deviceTokenA no longer applies, because it was refreshed, and is not attached to an actual device anymore.
When looking at the device Tokens, we still don't know which ones are good, which are bad, and which tokens belong to which device. That's ok!
So, then create a forEach loop, getting each Token, and then send an FCM to each of these Tokens, FCM can let us know which tokens were sent successfully. One of them will return an error. If it returns an error saying the token was bad, we can then catch the error and delete that token, so it will not be called again.
// outside for Each loop
var promiseHolder = [];
// create a forEach loop, iterating through the collection of deviceTokens
// within that loop, put:
let innerPromise = admin.messaging().send(message)
.then(response => {
console.log('notification sent success: ' + response);
})
.catch((error) => {
console.log('Error sending notification: ' + error);
// if the error == bad token message, then Delete the Token.
if (error == 'Error: Requested entity was not found.') {
console.log('you matched the error, token doesn't work, handle here.');
//delete the old token
return admin.firestore()doc(`users/${userID}/device_tokens/${token_id}`).delete();
}
}
// still within forEach loop
promiseHolder.push(innerPromise);
// end the forEach Loop, and outside forEachLoop put:
return Promise.all(promiseHolder);
So I've been thinking about how to go with this scenario. First off, let's put in the instances when onRefreshToken() is called:
This will not be called very frequently, it is needed for key rotation and to handle Instance ID changes due to:
App deletes Instance ID
App is restored on a new device
User uninstalls/reinstall the app
User clears app data
Guess with that, you can say that 'onTokenRefresh()` will be called after one the above happens and if the device is online (of course it has to be online on order to get a new token). So I guess here's how I'd go on the scenario:
First off, upon registration, I would save the registration token and pair it along another identifier, let's say a deviceId (since we're in a scenario for a user with multiple devices) in my App Server.
So assume I add in 3 registration tokens, those are also paired with their deviceIds. I add them all to a device group.
Now say one of the devices triggers the onTokenRefresh(), I would immediately send a delete request to my App Server for the registration token that is currently paired to that deviceId (you should also delete it in any device group(s) it's connected to), replacing it with the new one, then re-add it to the corresponding device group(s).
That's the simplest way I can think of. The key here is for you to pair the registration tokens with another identifier and use it to find which registration token you need to replace.
at moment i use this method. in my database i create a node with devices id
deviceId: {
uid1: deviceId,
uid2: deviceId,
uid3: deviceId
}
another node with the users that are subscribed to receive a notifications
newsSubscriber: {
uid1: [array of subscribers],
uid2: [array of subscribers],
uid3: [array of subscribers]
}
when i can send a notification by my nodejs script i get all users that are saved in the newsSubscriber node, and for any user i get his deviceId and i put it into an array of devices to send a notification.
There are only one problem, i read now that in this mode there are a limit of only 20 devices!.
but i think that this is a good easy method to have a corresponding deviceId for any user because when i use the logout or the login in my app i can change the correspondent deviceId for any user, so as to have consistency between user and device.
what happen if to bypass the limit of 20 devices i send the same notification to different groups of 20 devices?

Android unique device identifier to use with google's firebase token generator

I am creating an application that uses goolg's firebase as its push notification service. I am just using the push notifications server and DON'T WANT TO use its other services (such as database, authentication and etc).
In my application, a user that signs up can have multiple devices and thus multiple firebase tokens. The problem occurs when onRefreshToken is called. I should know which device the user is using and update that specific device's token in my database. So I should know a unique identifier for each device that does not change in device's lifetime.
I was wondering if there are any better ways to tackle this problem and if not, I am so confused about an android unique ID. The two options are Settings.Secure.ANDROID_ID which may have some issues (so I am not sure if I should use it) and TelephonyManger.getDeviceId() which return null sometimes and for devices that have no sim slots (such as tablets).
So what do you suggest me to do?
This answer has 2 solutions:
1) Using ANDROID_ID as a Device Identifier.
2) Using cloud functions and FCM to determine a bad token, after 1st message sent to it.
1)
Try Settings.Secure.ANDROID_ID to get the Device ID.
String deviceAppUID = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
if (deviceAppUID!=null) {
setFirebaseTokenInServer(deviceAppUID, FirebaseDeviceToken);}
Android is changing ANDROID_ID in 8.0 (API 26), so that it will apply only to the combination of specific app, user, and deviceId. When that happens, you aren't really getting a Device ID that is ALWAYS true for every app, but rather, you are getting a Device ID for your specific App, with its specific signing key, on your specific devise. You can uninstall the app, and reinstall the app, and the ANDROID_ID will be the same as it was before.
That makes it safer for the user, but would still allow you to persist the Firebase Device Tokens in case the registration token changes for these following reasons:
App deletes Instance ID
App is restored on a new device User
uninstalls/reinstall the app
User clears app data
Then in Firebase, you can save these as:
DeviceTokens:
deviceID: firebaseDeviceToken
deviceID: firebaseDeviceToken
2) Using Firebase Cloud Messaging and Firebase Google Cloud Functions to determine a bad token.
If you also use Cloud Functions to send the FCM to each listed deviceToken (as is necessary if multiple devices), you can check for a response after the first notification is sent. If there is an error, and the error returns a message stating the token is bad, you can handle the error and then delete it from the Server.
No device ID is necessary, much safer for user.
Sends 1 FCM to a bad token, but not again after that
If Firebase Team changes its errors or error messages, you will have to change the function or this will no longer work.
See this answer for more details:
Handling refresh tokens for FCM device groups
In class you are extending "FirebaseInstanceIdService" in method "onTokenRefresh" you can get it like :
FirebaseInstanceId.getInstance().getId()
You can get the device token by extending your custom class with FirebaseInstanceIdService Class
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String TAG = "MyFirebaseInsIDService";
#Override
public void onTokenRefresh() {
String refreshedToken = FirebaseInstanceId.getInstance().getToken();
Log.d(TAG, "Device Token: " + refreshedToken);
}
}
Add service in your AndroidManifest.xml file
<service
android:name=".MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
</intent-filter>
</service>
Now, you can use the device token to send notifications on the user mobile.
I strongly recommend retrieving the latest updated registration token.
The registration token may change when:
The app deletes Instance ID
The app is restored on a new device
The user uninstalls/reinstall the app The user clears app data.
When you need to retrieve the current token, call FirebaseInstanceId.getInstance().getToken(). This method returns null if the token has not yet been generated.

AWS Cognito- get user information with ID

Is there any way to get information about the user in AWS Cognito pool (on android) who is not logged in, knowing his ID? I tried that code:
AppHelper.getPool().getUser(username).getDetailsInBackground(detailsHandler);
However it works only for username who is currently logged in.
No, there is not.
You get get user information given the user name by calling AdminGetUser from you backend with your developer credentials but not from an Android client.
Use ListUsers() Api , with filters ie
client = boto3.client('cognito-idp', region_name=USER_POOL_REGION)
user = client.list_users(
UserPoolId = USER_POOL_ID,
AttributesToGet=attributes, # ie ['email', 'sub',]
Filter='sub = "<user-id-here>"',
#Limit=limit
)
for complex filters and examples see https://docs.aws.amazon.com/cognito/latest/developerguide/how-to-manage-user-accounts.html#cognito-user-pools-searching-for-users-listusers-api-examples

GCM , Registration ID and handling user login/logout

I have app that allows user to login with credentials issued by the server, lets say simple login with password and usename.
So user starts the app, enter username and password, server authentificates the user, save this information. (details are really don't play any role in this case).
What I need to implement ?
There some events when my server need to notify user. Lets consider order status changed. If so I need to notify user about this, with notification in the status bar.
I have decided to use GCM for this task.
I read about this, and got some questions.
When user gets registration ID from GCM service in android, is it constant or it can be changed. As I understand there is no guarantee that it be always the same. So how can I handle refreshes of this ID.
Or if I will not call GoogleCloudMessaging.getInstance(applicationContext).registerit will stay the same until I register new or even if I call register method it can return the same ID. May I save this ID in SharedPreferences for example to use it for a long time ? Or it can be become invalid after some period of time ?
The main problem is that I need to handle multiple users on the same device. As far as I know, registration id issued by gcm service it tied to the device and app. So if new user logged out, and logged in with new credentials (new user account) how can I handle this ? I can for example store this ID in my database on the server and use it until user logout from the account inside my app, and after that just remove it from the database, and if user on this device will login with another account registration ID can be used to notify another user ? Will it work ? Or I have missed something.
How can I handle user multiple device. My group notifaction key will be some unique data from user account (email,username..). If I understand correctly, I need to get registration ID on all devices where user logins with its my server account and map this to one notification key. Should I handle notification dismiss by myself and than send upstream message to the GCM server in order to notify other user devices that notification has been dismissed ?
Please help with understanding of this service.
Thanks in advance.
I have implemented this in one of our app. In this we send the notification to users whenever new listing get added.
User can login from multiple devices same time. Device may be android or iOS anything.
Also multiple users can login from same device (After logging out previous users).
Whenever user log in from Android device will get the registration ID from GCM. This GCM id is not constant for one device, it gets changed whenever you again request it from GCM server.
We send the notifications to multiple devices using notification key, it contains GCM register ids of all devices. If you lose a notification key, you can’t get it back. To make it worse, You can’t regenerate a new notification key with a previously-used notification key name.
Never use unique data from user account (email, username..) as notification key name. Always generate some random string as notification key name.
To overcome this follow following steps:
Create one table to store the following details
Columns :
id
userId
deviceGCMId
deviceId
notificationKey
notificationKeyName
whenever user logs in sent the following parameters to the server.
GCMId, deviceId, userId
First check that userId and deviceId exists or not.
If not exists then go to step 5 else go to step 6
From given userId get the notificationKey and notificationKeyName from table. If not found then generate new notificationKeyName and from that key generate new notificationKey. Use current GCMRegId to generate the key. Store the keys in variables.
Add new entry in table with notificationKey and keyname generated in step 4. Also add the GCM id under notification key.
Check the deviceId is same and GCM id is different then add update the GCM id in table and add that GCM id under notification key. If device id and GCM id same then no need to do anything
Sometimes notification key get discarded from GCM server and it shows the key not found error. In this case create the new notificationKey from new key name and add GCM ids against that key.
For more info go through following useful links
https://medium.com/appunite-edu-collection/notifications-d7df385b0ff4
https://medium.com/#Bicx/adventures-in-android-user-notifications-e6568871d9be
Hope this will help

What is the best way to auth, identify and store delicate information about users?

With the purpose of learning about endpoints I'm building an app called "Where Are You?". The app lets users request the location of other users. The idea is that it does so by letting the user select a contact, lookup the contact by phone number in my endpoint. If found it means that the contact have the app and GCM is sent requesting the location. If the contact isn't found a SMS is sent with an url which will request the location through a browser, perform a http post of said location and have the server GCM it back to the person requesting the location.
I need to authenticate users of the app and store phone numbers of several contacts as well as users of the app. As of now I'll mainly focus on the server side of things.
How can I archive the above in a safe manner?
So far I'm enabling OAuth 2.0 for my API and passing the user_required=True as argument to my Model.method decorator. Resulting in the following code:
from google.appengine.ext import endpoints
from google.appengine.ext import ndb
from protorpc import remote
from endpoints_proto_datastore.ndb import EndpointsModel
class User(EndpointsModel):
owner = ndb.UserProperty()
phone_number = ndb.StringProperty()
reg_id = ndb.StringProperty()
created = ndb.DateTimeProperty(auto_now_add=True)
#endpoints.api(name='whereareyoudroid', version='v1',
description='Where Are You Users')
class UserApi(remote.Service):
#User.method(user_required=True,
response_fields=('id',),
path='user')
def insert(self, user):
user.owner = endpoints.get_current_user()
user.put()
return user
#User.method(user_required=True,
request_fields=('id',),
path='user/{id}',
http_method='GET')
def get(self, user):
if not user.from_datastore:
raise endpoints.NotFoundException('User not found.')
return user
But the above code only requires a valid gmail account. I'm thinking something where you only can access a user if you already have the phone number? Which would be the case if I limit the response fields on the get method to exclude the phone number... unless someone decides to brute force the service. Suggestions? Comments?
From this I gather that I can configure my endpoint to only accept requests from my apps. Is that right? And if so, couldn't someone extract the need information from the apk or modify it to perform... evil? :)
Update:
A makes a request for B's location
Querys endpoint by phone number
if not found simply send a request per SMS
if found... go to 2.
Request is forwarded to GAE app
This is done by inserting a Location endpoint whoes id is a UUID and sends a GCM to B about the request
GAE app verifies that A's secret ID is on B's whitelist
There is no whitelist and the secret id is the UUID so this step is eliminated
The app then queries for B's location
If B decides to grant access to it's location B simply updates the Location endpoint based on the UUID
Once the app retrieves this information, it then verifies that it is only sending this information to the user identified by A's secret ID
When B updates it's location GAE sends a GCM to A informing about the update
The info is then sent securely to A's client
Done! GCM and SMS (and HTTPS) can be considered secure... right?
Update: GCM isn't secure... but does it really matter that much?
You are wanting to acquire a rather sensitive combination of information - user email (as identity), phone number, and location.
So firstly, you'll have to be very considerate about privacy issues for users, having a policy on how this information is stored and distributed and make it clear to users what they are allowing. I would suggest not storing any aggregate of this type of dataset in the client, in as much as possible.
For this purpose I would recommend using GAE storage as much as possible.
Your app querying a user's location shouldn't be too worrying, provided things are done over secure channels kept well.
What is worrysome is that that information only reaches an allowed source. i.e that only a mutual contact can request this information.
I'd suggest that for each user, a whitelist of allowed contacts would be best. For each user, have a secret random uuid associated (that never goes out to any client) on GAE. A user registering for your service would create this ID. The whitelist would then consist of a list of secret IDs.
A process could then be-
A makes request for B's location
request is forwarded to GAE app.
GAE app verifies that A's secret ID is on B's whitelist
The app then queries for B's location
Once the app retrieves this information, it then verifies that it is only sending this information to the user identified by A's secret ID
The info is then sent securely to A's client

Categories

Resources