I am trying to get the device token once the person has logged into the app and send back the device token to our server. The login process is done in async.
According to this post , you can get the token in activities but is it possible to get in async?
I have tried to put the code in async but does not work. (I changed the MainActivity.this to (Activity)mycontext, where I have defined mycontext as a Context)
FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener( (Activitiy) mycontext, new OnSuccessListener<InstanceIdResult>() {
#Override
public void onSuccess(InstanceIdResult instanceIdResult) {
String newToken = instanceIdResult.getToken();
Log.e("newToken",newToken);
}
});
Thank you very much
thanks to Shashanth , the solution is just to get the token in one activity and pass that token value to async
Related
I am working in an application in which I need to refresh my token after every 60 minutes. I have created a service which runs in the background. However, the problem I am facing is, when the phone goes to sleep mode, my service gets stopped and hence I am unable to refresh my token. I am getting this problem mostly in OREO & PIE.
public class InActivityTimer extends Service {
#Override
public void onCreate() {
super.onCreate();
mService = APIUtils_AI.getSOService();
mService2 = APIUtils_AI.getSOServiceAI();
utils = new Utils();
receiver = new BroadcastTokenCheck();
filter = new IntentFilter(COUNTDOWN_BR);
registerReceiver(receiver, filter);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
cdt = new CountDownTimer(15000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
Log.i(TAG, "Countdown seconds remaining: " + millisUntilFinished / 1000);
if(millisUntilFinished<8000&&millisUntilFinished>5000){
if(!isSecond){
Log.i("", "Running: ");
refreshToken();//Refresh Token API
cdt.cancel();
cdt.start();
}
}
}
#Override
public void onFinish() {
Intent intent = new Intent(COUNTDOWN_BR);
intent.putExtra("VALUE","startService");
sendBroadcast(intent);
//stopSelf();
Log.i("", "Timer finished");
}
}.start();
return START_STICKY;
}
#Override
public void onDestroy() {
unregisterReceiver(receiver);
Log.e("Service Status: ","Stopped");
cdt.cancel();
super.onDestroy();
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
}
My suggestion is to refresh the token when necessary. From my understanding, the token is required to authenticate an API call in a server application. When the API returns unauthorized or 401 error status, you might consider refreshing the token in that case.
Android 8.0 put some limitations to background services which are described briefly in their developer's documentation. If you really need to refresh your token after every 60 minutes, then you might consider using JobScheduler which is suggested in their documentation.
However, I want to recommend to refresh your token in the onResume function of your launcher activity if 60 min has elapsed after the last refresh. The situation may vary based on your server-side implementation though.
Hope that helps!
Here is my 2 cents.
I assume you are calling refreshToken() to fetch a new token from the server, so that when you make the next REST call, you can use this valid token.
Maybe instead of continuously running a service in background, refreshing tokens and consuming the resources, even though user might not be using your app actively, you can do the following:
Keep a local flag in SharedPreference, where you keep record of how long it has been since you made the last call to refresh token. Or you can save the timestamp till the current token is valid.
Before making the REST call, you can check if your current token is valid or not by checking your shared preference.
If its not valid, fetch a new token and then when you receive the response, use that new token to make the initial call. Also persist the token that you received and update the SharedPreference flag.
Note: in case you are using Retrofit for REST calls, you can use something like Interceptor where you can do the above check. Something like this answer: Answer on "Refreshing OAuth token using Retrofit without modifying all calls"
I want to generate and store token everytime users login. I tried for my code below, but it always generate same token when I login with another account.
mAuth.signInWithEmailAndPassword(email, password).addOnCompleteListener(LoginActivity.this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
AlertDialog.dismiss();
if (!task.isSuccessful()) {
if (password.length() < 6) {
new SweetAlertDialog(LoginActivity.this, SweetAlertDialog.ERROR_TYPE)
.setTitleText("Oops...")
.setContentText("Enter minimum 6 charachters !! ")
.show();
} else {
passwordInput.setText("");
new SweetAlertDialog(LoginActivity.this, SweetAlertDialog.ERROR_TYPE)
.setTitleText("Oops...")
.setContentText("Authentication failed !!")
.show();
}
} else {
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
FirebaseUser users = FirebaseAuth.getInstance().getCurrentUser();
DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference("users/"+ users.getUid());
String token = FirebaseInstanceId.getInstance().getToken();
Log.e("tokenid",""+token);
mDatabase.child("token_id").setValue(token);
finish();
}
}
});
}
});
please help, thanks..
When using the following line of code:
String token = FirebaseInstanceId.getInstance().getToken();
You are getting the Firebase Instance ID token, which is also known as FCM token, on the client side. You need to know that in Firebase there two different tokens:
A FCM Token/Instance ID token that identifies an installation of a specific application on a specific user device. It doesn't identify in any way a particular user.
An Auth ID token identifies a specific user of a specific application. It doesn't identify in any way a particular device.
The two tokens are quite different and serve different purposes. Please see the official documentation for Android regarding on how to retrieve ID tokens on clients.
The identity of the user is remembered indefinitely (or until you sign out). But their credentials (or rather, whether their token is still valid) are re-checked every hour.
FCM generates a registration token for the client app instance, hence It may happen that you'll get the same token for different users in your app. You can use forceRefresh to generate a new token every time. Register new token everytime user logins to any device and save it in DB and update with a new token on new login this way you will have a new token for each user on every login (If this fits your requirement)
Here is a good answer to understand how it works Firebase FCM force onTokenRefresh() to be called
Use UUID.randomUUID().toString()
You can read more here.
java docs - and here =)
I have an Android and iOS application that uses Firebase cloud functions with Stripe to process payments.
On the client side I handle the token operation and then write to the realtime database. When done writing the addPaymentSource cloud function triggers which stores that payment source for future transactions.
Interesting enough the process of creating a token on iOS and then saving that output to my server works as expected. My problem comes along when trying to duplicate my iOS implementation into my Android application. The Firebase cloud function triggers as expected but it is outputting an error to my server.
Error found in server:
"The source hash must include an 'object' key indicating what type of source to create."
Client code:
public void tokenizePaymentFields(){
Stripe stripe = new Stripe(getApplicationContext(), stripePublishableKey);
final Card stripeCard = new Card(validCard.getCardNumber()
,Integer.valueOf(validCard.getExpiredDate().substring(0,2)),Integer.valueOf(validCard.getExpiredDate().substring(3,5)),validCard.getCvvCode());
if (!stripeCard.validateCard()) {
Toast.makeText(getApplicationContext(),
"There was an error validating your card.",
Toast.LENGTH_LONG
).show();
return;
}
stripe.createToken(
stripeCard,
new TokenCallback() {
public void onSuccess(Token token) {
// Send token to your server
pushToServer(token);
}
public void onError(Exception error) {
// Show localized error message
activitySubmitCreditCardBinding.progressCircle.setVisibility(View.INVISIBLE);
Toast.makeText(getApplicationContext(),
error.getLocalizedMessage(),
Toast.LENGTH_LONG
).show();
}
}
);
}
Stripe(Firebase Cloud Functions):
https://github.com/firebase/functions-samples/tree/master/stripe
Rather than sending the entire token object to your server, you should simply send the token's id, like this:
public void onSuccess(Token token) {
// Send token to your server
pushToServer(token.getId());
}
In your server-side (Firebase) code, the charge creation request expects only a token ID in the source parameter, not a full token object.
since today I encountered the following issue with GCM "subscribe to topics".
Nexus 6, Android 6.0.1, Google Play Services 9.0.83
Using google-play-services:8.3.0 in app.
Step 1
I follow the documentation from Google for getting the token through the instance id.
After getting the token I successfully subscribe to the "topics/global" topic and store the token in the shared preferences.
protected void register() {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
try {
// [START register_for_gcm]
// Initially this call goes out to the network to retrieve the token, subsequent calls
// are local.
// R.string.gcm_defaultSenderId (the Sender ID) is typically derived from google-services.json.
// See https://developers.google.com/cloud-messaging/android/start for details on this file.
// [START get_token]
InstanceID instanceID = InstanceID.getInstance(this);
String token = instanceID.getToken(getString(R.string.gcm_defaultSenderId),
GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
// [END get_token]
Log.i(TAG, "GCM Registration Token: " + token);
// TODO: Implement this method to send any registration to your app's servers.
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
sharedPreferences.edit().putString("token", token).apply();
// You should store a boolean that indicates whether the generated token has been
// sent to your server. If the boolean is false, send the token to your server,
// otherwise your server should have already received the token.
sharedPreferences.edit().putBoolean(QuickstartPreferences.SENT_TOKEN_TO_SERVER, true).apply();
// [END register_for_gcm]
} catch (Exception e) {
Log.d(TAG, "Failed to complete token refresh", e);
// If an exception happens while fetching the new token or updating our registration data
// on a third-party server, this ensures that we'll attempt the update at a later time.
sharedPreferences.edit().putBoolean(QuickstartPreferences.SENT_TOKEN_TO_SERVER, false).apply();
}
// Notify UI that registration has completed, so the progress indicator can be hidden.
Intent registrationComplete = new Intent(QuickstartPreferences.REGISTRATION_COMPLETE);
LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
}
Step 2
After some time / on user interaction I want to subscribe to additional topics.
I fetch the token from the shared preferences and try to subscribe like before, but this time it fails with the "java.io.IOException: InternalServerError".
The exception is catched of course, but I do not know how to proceed now.
private void subscribeTopics() throws IOException {
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
String token = sharedPreferences.getString("token", null);
if(token == null) {
Log.e(TAG, "No token");
return;
}
GcmPubSub pubSub = GcmPubSub.getInstance(this);
for (String topic : TOPICS) {
pubSub.subscribe(token, "/topics/" + topic, null); // <--- FAILS HERE
}
Log.d(TAG, "Subscribed to topics.");
}
This process worked for the last 5 months without issues. Suddenly, since this morning, the subscription to additional topics (step 2) fails.
Any idea if the switch to Firebase Cloud Messaging (FCM) brought breaking changes?
Currently all my client apps are not usable.
Fast help is really appreciated.
I am part of the Google Cloud Messaging team.
We identified an issue in our backed that affected a small percentage of the topic subscriptions during the last 24 hours.
The issue has already been fixed, and the subscriptions should work correctly on all devices.
Please let us know if you are still experiencing this error.
Thanks Steffen for reporting the issue.
I created my own Android account authenticator by extending AbstractAccountAuthenticator and implementing addAccount() and getAuthToken(). Some of the methods in it are called by AccountManager, but others are not.
This works great:
AccountManager#addAccount()
AccountManager accountManager = AccountManager.get(activity);
accountManager.addAccount(MyAccountAuthenticator.ACCOUNT_TYPE,
MyAccountAuthenticator.AUTHTOKEN_TYPE_FULL_ACCESS, null, null,
activity, callback, null);
The problem happens when I call AccountManager#getAuthToken() in my Activity. The AccountManager does not call the getAuthToken() method I define in my AccountAuthenticator. It calls some other default method that only checks for existence of an authToken before starting the AuthenticatorActivity.
This does not work. It does not call my getAuthToken() method:
AccountManager#getAuthToken()
AccountManager accountManager = AccountManager.get(activity);
accountManager.getAuthToken(
mAccount, MyAccountAuthenticator.AUTHTOKEN_TYPE_FULL_ACCESS, null,
activity, callback, handler);
AuthenticatorService
I created my service and defined onBind(). addAccount() should not work otherwise.
public IBinder onBind(Intent intent) {
return intent.getAction().equals(ACTION_AUTHENTICATOR_INTENT) ? new MyAccountAuthenticator(this).getIBinder() : null;
}
EDIT: I call addAccountExplicitly in MyAuthenticatorActivity after the app gets an auth token back for the user.
Snippet from class MyAuthenticatorActivity extends AccountAuthenticatorActivity:
if (getIntent().getBooleanExtra(KEY_IS_ADDING_NEW_ACCOUNT, false)) {
// Creating the account on the device and setting the auth token we recieved
accountManager.addAccountExplicitly(account, null, null);
}
Your comment cleared things up immensely -- if you set the auth token for the account, then your getAuthToken method will not be called until the token is invalidated. You generally do this by calling invalidateAuthToken upon receiving a 401 or 403 or what have you from the web service.
From the Javadoc for the getAuthToken methods:
If a previously generated auth token is cached for this account and type, then it is returned. Otherwise, if a saved password is available, it is sent to the server to generate a new auth token. Otherwise, the user is prompted to enter a password.
Since your token is in the cache, it is returned directly and your authenticator is not consulted.
for calling AuthenticatorActivity in method AccountManager#getAuthToken, you must send intent to the activity by parcelable, method for example :
final Intent intent = new Intent(mContext, LoginActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, accountAuthenticatorResponse);
intent.putExtra(LoginActivity.ARG_ACCOUNT_TYPE, account.type);
intent.putExtra(LoginActivity.ARG_AUTH_TYPE, authTokenType);
intent.putExtra(LoginActivity.ARG_ACCOUNT_NAME, account.name);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);