I have an activity where I want the user to wait until email account is verified, but it is not taking the user to mainActivity once account is verified, how should I address this?
See my code below;
user.sendEmailVerification();
final Handler handler = new Handler();
final int delay = 10000; //milliseconds
handler.postDelayed(new Runnable() {
public void run() {
if (user.isEmailVerified()) {
startActivity(new Intent(VerificationEmailActivity.this, MainActivity.class));
Toast.makeText(VerificationEmailActivity.this, R.string.spend_wisely, Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(VerificationEmailActivity.this, "Check your email!", Toast.LENGTH_SHORT).show();
}
handler.postDelayed(this, delay);
}
}, delay);
}
Since the actual verification of the user happens outside of your Android app (in your browser), the app is not aware that it happens. This means that the app only detects the user's updated state once it auto-refreshes the token, which can take up to an hour.
To detect it earlier, you can reload the profile by calling reload(), or forcing the refresh of the ID token by calling getIdToken(true).
This has been covered quite a few times already, so also check out:
Firebase email verification behavior
On every call of isEmailVerified() returning the false condition, Even after email verified
Firebase: Observe email verification status in real time
Flutter: How to listen to the FirebaseUser is Email verified boolean?
Related
I have a firebase project which allows users to login only if they are already exists in identifier in authentication .
I already added few users using my web app with mobile numbers.
Now, in android I have used the signInWithPhoneAuthCredential method to get the users login.
But in this method, it allows any users to login even if the user is first time entering the mobile number.
Is there any method to restrict this ?
Sample Code :
private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success");
FirebaseUser user = task.getResult().getUser();
// ...
} else {
// Sign in failed, display a message and update the UI
Log.w(TAG, "signInWithCredential:failure", task.getException());
if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
// The verification code entered was invalid
}
}
}
});
}
According to the docs:
signInWithPhoneNumber:
Asynchronously signs in using a phone number. This method sends a code via SMS to the given phone number, and returns a firebase.auth.ConfirmationResult. After the user provides the code sent to their phone, call firebase.auth.ConfirmationResult.confirm with the code to sign the user in.
This is the default behavior in all applications that uses phone number as login. There is no method in the firebase docs that can restrict this.
you have to use firebase database also with this code to save user info, that way you can achieve your use case
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'm trying to make a error checking log in activity, where it will trigger an intent if it detects the following issues :
1) if user hasn't signed up (the email he used isn't authenticated with firebase) which is working out well for me
2) if user has signed up but didn't give me any information into the firebase database
My issue is that, for some reason the code i use to check for information in database, works for users even though they have information in the database attached to their UID.
meaning that the intent to tell them to give information will trigger when they already have given information.
if(task.isSuccessful()){
// Checks if user has submitted information in the Essential Information activity
//Takes the Unique ID(if it is present if not it will tell him to sign up or invalid email) asks the firebase database if he has given information to the database
reference.child("Users").child(UserUID).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// User exists
if (dataSnapshot.exists()) {
//Displays Toast telling user that their information is saved in the database
Toast.makeText(LogInActivity.this, "You have data in our database ", Toast.LENGTH_SHORT).show();
}
//User doesn't have information in the database
else {
// Displays Toast telling user he/she needs to sign in into the firebase database
// User goes to UserInformationActivity to give his/her information
Toast.makeText(LogInActivity.this, "You need to give Essential Information", Toast.LENGTH_SHORT).show();
// 3 second delay
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Goes to UserInformationActivity
Intent GoToUserInformation = new Intent(LogInActivity.this, UserInformationActivity.class);
LogInActivity.this.startActivity(GoToUserInformation);
}
}, 3000);
}
}
// if the checking got cancelled, likability of that happening is small
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Don't you want to check if the user is signed in?
Directly from firebase...
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
// User is signed in
// check a snapshot to see if there is information....if not, error out.
} else {
// No user is signed in
}
In an android app, which uses Parse, the login flow is like this...
We have our own logic to see if the user has entered the correct credentials. Once we verify that, signUpOrLoginOnParse() is called. Parse is used just to store data and handle sessions locally. Users can not access the api without the token.
private void signUpOrLogin(final String username, final String token) {
ParseUser user = new ParseUser();
user.setUsername(username);
user.setPassword(username);
user.signUpInBackground(new SignUpCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
// sign up success. ParseUser.getCurrentUser() populated
saveTokenToCloud(token);
} else if ("condition to check if the user already exists") {
// existing user, login.
ParseUser.logInInBackground(username, username, new LogInCallback() {
#Override
public void done(ParseUser parseUser, ParseException e) {
// login was successful, ParseUser.getCurrentUser() populated
saveTokenToCloud(token);
}
});
} else {
showProgress(false);
e.printStackTrace();
}
}
});
}
private void saveTokenToCloud(String token) {
// saving token to cloud
ParseUser user = ParseUser.getCurrentUser();
user.put("token", token); // THIS IS WHERE I GET NULL POINTER EXCEPTION
user.saveEventually();
// link installation to user.
ParseInstallation parseInstallation = ParseInstallation.getCurrentInstallation();
parseInstallation.put("user", user);
parseInstallation.saveEventually();
// Starting next activity
Intent i = new Intent(this, NextActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
finish();
}
All good when I first run the app. Once, logout button is pressed (contains - Parse.logoutInBackground()), it shows the LoginActivity (current one). When trying to log in, everything succeeds but I get a NullPointerException at line 3 of saveTokenToCloud().
It says - trying to invoke virtual method .put() over a null object reference.
But, isn't Parse.currentUser() already populated since this method is called from callback of methods that do that ?
It works after restarting the app. But then the same continues if logout is pressed.
After calling logoutInBackground , future calls to getCurrentUser() will return null.
You will need to initialize the user again.
signUpInBackground will create a new ParseUser on the server, and also persist the session on disk so that you can access the user using ParseUser.getCurrentUser().
However i am not sure you should be calling it every single time you log in.
Instead of calling the getCurrentuser inside the saveToken method you can pass the user to the saveTokenMethod from the done callback parameter.
Separate you logic in distinct methods for sign up and logIn. I suggest you check before calling signUp, and not abusing it every time you want to login