Firebase Auth getIdToken gets stuck after update - android

So the situation is as follows:
1) A user is logged in. the last token generated was a while ago and has expired. even if it hasn't, the issue doesn't hit.
2) An app update is installed. (Not android studio instant run thing, Actual signed apk with a higher app version and appversioncode)
3) User opens app and the regular code below which i use to refresh the id token runs.
4) None of the listeners get hit and there's never a time out
Is this a bug or am i doing something wrong here?
Clearing app data and restarting clears all problems. The code runs fine in this case. It only gets stuck after an update.
NOTE: i have set the forceRefresh boolean false as this code runs on every app launch and i don't want unnecessary id token creations getting called in case there's a limit on the generations. the false boolean does refresh the token if and only if it has expired.
FirebaseAuth.getInstance().getCurrentUser().getIdToken(false).addOnCompleteListener(new OnCompleteListener<GetTokenResult>() {
#Override
public void onComplete(#NonNull Task<GetTokenResult> task) {
if (task.isSuccessful()) {
//logic to proceed further
}
}).addOnFailureListener(new OnFailureListener() {
#SuppressLint("SetTextI18n")
#Override
public void onFailure(#NonNull Exception e) {
if(e instanceof FirebaseAuthInvalidUserException){
//Do stuff to log out the user session
}else{
//Some other exception occurred, let the user retry
}
}
});
P.S i do have a nullity check before all this to see if there indeed is a user.

For anyone wondering about this, I raised a ticket with Firebase support and it turns out that it's a bug in Firebase Auth 15.1.0
According to release notes, they had modified the token refresh logic and probably this is where the bug was introduced.
The system works fine up to 12.0.1 version according to my tests. Please do not update to 15.1.0 if you are planning to use a similar feature.

Related

FirebaseAuthUserCollisionException vs FirebaseAuthInvalidUserException when user account is disabled?

I am currently developing a android app which uses FirebaseAuth to control users.
The users can sign-in either using email or Google.
Considering this, i am catching some exceptions in order to handle any problems with the authentication system.
Most of these work fine. This is my code:
when(task.exception!!){
is FirebaseAuthEmailException -> {
errorDialogBuilder.setTitle(R.string.exception_email_title)
errorDialogBuilder.setMessage(getString(R.string.exception_email_msg, task.exception!!.localizedMessage))
}
is FirebaseAuthInvalidCredentialsException -> {
errorDialogBuilder.setTitle(R.string.exception_invalid_credentials_title)
errorDialogBuilder.setMessage(getString(R.string.exception_invalid_credentials_msg, task.exception!!.localizedMessage))
}
is FirebaseAuthInvalidUserException -> {
val invalidUserException = task.exception!! as FirebaseAuthInvalidUserException
when (invalidUserException.errorCode) {
"ERROR_USER_DISABLED" -> {
errorDialogBuilder.setTitle(R.string.exception_user_disabled_title)
errorDialogBuilder.setMessage(R.string.exception_user_disabled_msg)
errorDialogBuilder.setNeutralButton(R.string.more, null)
}
"ERROR_USER_NOT_FOUND" -> {
errorDialogBuilder.setTitle(R.string.exception_user_not_found_title)
errorDialogBuilder.setMessage(getString(R.string.exception_user_not_found_msg, fragmentView.email_input_signin.text.toString()))
errorDialogBuilder.setNeutralButton(R.string.action_createnew_account) { _, _ ->
viewModel.createUser(fragmentView.email_input_signin.text.toString(), fragmentView.email_password.text.toString())
}
}
else -> {
errorDialogBuilder.setTitle(invalidUserException.errorCode)
errorDialogBuilder.setMessage(invalidUserException.localizedMessage)
}
}
}
}
I had no problems when using this code for development purposes. But I found out something very weird:
I am playing around with disabling users; This issue ocurred: Whenever I disable a google account, not a FirebaseAuthInvalidUserException is thrown with code ERROR_USER_DISABLED as when trying to sign in with a disabled email-based account, but a FirebaseUserCollissionException which has the exact same message as a FirebaseAuthInvalidUserException with code ERROR_USER_DISABLED:
com.google.firebase.auth.FirebaseAuthUserCollisionException: The user account has been disabled by an administrator.
at com.google.firebase.auth.api.internal.zzdx.zza(com.google.firebase:firebase-auth##19.1.0:42)
at com.google.firebase.auth.api.internal.zzfa.zza(com.google.firebase:firebase-auth##19.1.0:19)
at com.google.firebase.auth.api.internal.zzet.zza(com.google.firebase:firebase-auth##19.1.0:34)
at com.google.firebase.auth.api.internal.zzev.zza(com.google.firebase:firebase-auth##19.1.0:98)
at com.google.firebase.auth.api.internal.zzev.zza(com.google.firebase:firebase-auth##19.1.0:85)
at com.google.firebase.auth.api.internal.zzed.zza(com.google.firebase:firebase-auth##19.1.0:43)
at com.google.android.gms.internal.firebase_auth.zza.onTransact(com.google.firebase:firebase-auth##19.1.0:13)
at android.os.Binder.execTransactInternal(Binder.java:1021)
at android.os.Binder.execTransact(Binder.java:994)
This is how it looks normally, when trying to use an disabled email-based account(this is handled&this works):
com.google.firebase.auth.FirebaseAuthInvalidUserException: The user account has been disabled by an administrator.
at com.google.firebase.auth.api.internal.zzdx.zza(com.google.firebase:firebase-auth##19.1.0:6)
at com.google.firebase.auth.api.internal.zzfa.zza(com.google.firebase:firebase-auth##19.1.0:21)
at com.google.firebase.auth.api.internal.zzet.zza(com.google.firebase:firebase-auth##19.1.0:34)
at com.google.firebase.auth.api.internal.zzev.zza(com.google.firebase:firebase-auth##19.1.0:74)
at com.google.firebase.auth.api.internal.zzed.zza(com.google.firebase:firebase-auth##19.1.0:18)
at com.google.android.gms.internal.firebase_auth.zza.onTransact(com.google.firebase:firebase-auth##19.1.0:13)
at android.os.Binder.execTransactInternal(Binder.java:1021)
at android.os.Binder.execTransact(Binder.java:994)
Why are there two different exceptions being used here? And why would firebase throw a FirebaseAuthUserCollisionException when the users account is disabled, as, from what i know from the docs, the FirebaseAuthUserCollisionException is only thrown when there are user accounts conflicting?
In addition:
Brief extract from the firebase docs:
FirebaseAuthUserCollisionException
public final class FirebaseAuthUserCollisionException extends FirebaseAuthException
Thrown when an operation on a FirebaseUser instance couldn't be
completed due to a conflict with another existing user.
That's weird. I read the Google API for Android docs - and what you said was correct - FirebaseAuthUserCollisionException should only be thrown when there is a conflict between Firebase users, particularly if both share the same credential accidentally, but I think Firebase Auth does not allow that and it can detect if the credential between two users or more are similar right from the start.
I think that Firebase Auth tries to re-register/re-add the disabled Google account into the system when you use it to sign in. Since the Google account is disabled, Firebase Auth may consider the account invalid - yet the account still remain and recorded in the auth system - hence if you are trying to sign in with the previously disabled Google account, Firebase Auth tries to re-add that and collide with the previous record of disabled Google account.
There is also a chance that this may be a bug/glitch/error in Firebase Auth side.
If you are asking about difference, I think it's more to the circumstances/conditions that caused either of those exceptions to occur; most exceptions are similar, it is the purpose and cause of error that makes the difference.
Hope this helps.

Why Google authentication token refresh keeps failing recently in Azure Mobile Apps SDK for Android

I have an android app implementing the azure mobile SDK (com. microsoft. azure: azure-mobile-android:3.5.1#aar)
In my app I use the Google authentication method with refresh tokens (as described in Azure Mobile Apps documentation).
The authentication had been working fine for nearly 2 years. For the last 10 days(no changes have been made), my users can no more refresh their auth tokens if the last refresh was made 60 minutes or more ago. The refresh succeeds only if it is called sooner than 1 hour. If the refresh fails, as it frequently does, I must force the user to sign-in again using the Google consent screen in order to get a new token. This was not happening before, as the tokens could be refreshed even days after the last refresh. What could be wrong? My Azure Service Plan is type D1: Shared.
The users see this error message in their android device, if the refreshUser() method is called more than 1 hour after the last refresh:
Google authentication refresh failed.Refresh failed with a 401 Unauthorized error. Credentials are no longer valid.
In the Log Stream (in portal.azure) I see the following message:
HTTP Error 401.83 - Unauthorized You do not have permission to view this directory or page.Most likely causes:The authenticated user does not have access to a resource needed to process the request.Things you can try:Create a tracing rule to track failed requests for this HTTP status code.
This is my authenticate method:
private void authenticate() {
// Sign in using the Google provider.
HashMap parameters = new HashMap<>();
parameters.put("access_type", "offline");
parameters.put("prompt", "consent");
mClient.login(MobileServiceAuthenticationProvider.Google, url_scheme_of_your_app, GOOGLE_LOGIN_REQUEST_CODE, parameters);
}
This is my refresh token method following the Mobile Apps documentation:
private void refreshToken(){mClient.refreshUser(new UserAuthenticationCallback() {
#Override
public void onCompleted(MobileServiceUser user, Exception exception, ServiceFilterResponse response) {
if (user != null && exception == null) {
/*refresh succeeded*/
} else {
/*refresh failed*/
String message = "+"%s authentication refresh failed.Error: %s", "Google",exception.getMessage())";}}}); }
Authentication settings in Azure portal:
It looks like the Azure App Service Authentication/Authorization feature had a regression in the default value for token refresh (it used to be 72 hours, now it is not being set, so it is defaulting to 1 hour). We will be fixing this in an upcoming release (next few weeks).
In the meantime, you can manually configure this value with the below steps:
Navigate to https://resources.azure.com
Navigate to subscriptions > (subName) > resourceGroups > (resourceGroupName) > providers > Microsoft.Web > sites > (siteName) > config > authSettings.
Click edit
Set "tokenRefreshExtensionHours" to 72.
Click PUT
This should restore the old behavior, and will still work even once we change the default back to 72 hours.

App uninstall and re-install shows "firebase authentication not allowed" despite User is Null. Can we signOut or delete user or Login?

After the User has uninstalled the app, it doesn't un-authenticate the user. Even if the user is null, it calls onVerificationFailure(). Can I have it re-authenticate the new user or get the credentials of previous one despite it being null?
Firebase - Deleting and reinstalling app does not un-authenticate a user
Firebase FAuthData saved even after deletion of app
Log user out after app has been uninstalled - Firebase
I have found these cases for iOS and I do understand that the issue is similar to Android. However, neither signout() nor currentUser.delete() works in the case. At times currentUser is even null but I can't sign in again with this or newer user.
Also, android:allowBackup="false" doesn't solve the problem as suggested here with Android.
Firebase Auth saved after uninstall. How can I delete it?
My current code:
FirebaseAuth mAuth = FirebaseAuth.getInstance();
FirebaseUser mUser = mAuth.getCurrentUser();
if (mUser != null) {
FirebaseAuth.getInstance().signOut();
mUser.delete().addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful())
Log.e(TAG, "Delete Success");
else
Log.e(TAG, String.valueOf(task.getException()));
}
});
} else
Log.e(TAG, "User Null");
The code is in onStart() of activity. It either returns User Null or moves to failure function. SHA-1 is verified and uploaded. Firebase authentication is enabled. Currently, only authentication by Phone Number is available to user end; no database or firestore is in use.
The other approach I tried (unsuccessfully) instead of deleting/signing out the user:
if (mUser.getPhoneNumber() != null)
//Log In Logic
else
//sign Up Logic
I expect the user to be able to log in or sign up, and not entirely be failed in verification process. Documents do not mention more than deletion or removal. Firebase docs even allow signout to be able to sign in with a different user. But it doesn't work here either.

Facebook access token is null on 2nd attempt of log in with facebook using ParseFacebookUtils

I am using parse server as a backend in the app. I've the requirement to login with Facebook as a sign up option. For that I am using
compile 'com.parse:parsefacebookutils-v4-android:1.10.3#aar'
Using this latest Facebook sdk for Android
compile('com.facebook.android:facebook-android-sdk:4.26.0') {
exclude group: 'com.parse.bolts',
module: 'bolts-android'
}
When login first time after installing the app with facebook it all works fine
private void loginWithFacebook(){
ParseFacebookUtils.logInWithReadPermissionsInBackground(this, Arrays.asList("public_profile", "email", "user_friends"), new LogInCallback() {
#Override
public void done(ParseUser user, ParseException err) {
//....
}
});
}
I can have the access token with other information which i can use later on in the app for different purpose, but the problem starts when i logout using:
LoginManager.getInstance().logOut();
this function logs me out properly but after logging again with same Facebook account using same logic for login when i debug and tries to get the Access Token it's null which I am not able to understand why as it should work just fine.
The account that I am using to login is already a test account which works fine with the iOS version of the application with the same flow but not in Android.
Let me know if I am doing anything wrong.
Try to log out using this method:
ParseUser.logOut()
public static void logOut()
Logs out the currently logged in user session. This will remove the session from disk, log out of linked services, and future calls to ParseUser.getCurrentUser() will return null.
Typically, you should use ParseUser.logOutInBackground() instead of this, unless you are managing your own threading.
Note:: Any errors in the log out flow will be swallowed due to backward-compatibility reasons. Please use ParseUser.logOutInBackground() if you'd wish to handle them.
I think your issue occurs because you are directly using LoginManager.getInstance().logOut() method of Facebook SDK, but ParseFacebookUtils caches some info about previous login. Try to call:
ParseUser.getCurrentUser().logOut();
BTW, if you look into the code of LoginManager.getInstance().logOut(), you will see next:
/**
* Logs out the user.
*/
public void logOut() {
AccessToken.setCurrentAccessToken(null);
Profile.setCurrentProfile(null);
setExpressLoginStatus(false);
}
It sets Access Token to NULL.
Also people advise to remove next call from your app's Application class:
ParseUser.enableAutomaticUser();

com.google.firebase.firestore.FirebaseFirestoreException: Failed to get document because the client is offline. Android

I get this run time error that I do not understand the reason behind it.
com.google.firebase.firestore.FirebaseFirestoreException: Failed to get document because the client is offline.
Below is the code in my Activity that tries to fetch data from the cloud Firestore
DocumentReference docRef = db.collection("room-id-1").document("participan-name-1");
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document != null) {
Log.d(TAG, "DocumentSnapshot data: " + task.getResult().getData());
userData.registerUserToHotspot(roomId_str, participantName_str);
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
Is there something I can do about this?
This happens because OnCompleteListener is triggered when the task is completed, either it fails or succeeds. To avoid the error, you can use separate OnSuccessListener and OnFailureListener.
OnSuccessListener is called when the task is succeeded, but if the above error occurs, OnFailureListener will be triggered and you can handle the error in onFailure() method of the listener.
Happy Coding :)
If nothing helped and you sure that all good to go, do next steps:
Clean project (Build -> Clean Project). You can also "Invalidate and Caches" with "Clean project" for more safety.
Delete google-services.json from your project and sync.
Build project. Android studio will throw build exception about missing google-services.json, It's okey.
Download again google-services.json from Firebase console, put it in project and sync.
Build and run.
Hoping I'm helped to some one
I had the same exception when I used wrong path to the document. When I fixed the path it started to work.
Anyway, the error message is misleading.
In my case the solution was to create a new emulator in Android Studio and run the app there
if you are using rxjava then it wiil be simple to catch throwable this way:
RxFirestore.getDocument(FirebaseFirestore.getInstance()
.collection("info")
.document("app"))
.subscribeOn(Schedulers.io())
.subscribe(snapshot -> {
}, throwable -> Timber.e(throwable))
Otherwise try somehow catch it in promise or/and try-catch block
For me, I faced this issue because i forgot to initialize my firestore database from the Firebase console. Hope this helps someone else.
I faced this problem recently I tried deleting and re-installing the emulator still no dice. So I decided to catch the error and retry the query if the particular error is present. If the user was really offline I included a time out, if not it will retrieve the data after retries.
fun makeFirestoreQuery(){
fireStoreDb
.collection(Constants.USER_DATA)
.document(currentUserEmail)
.get()
.addOnSuccessListener{documentSnapshot->
//perform opertion with the data
}.addOnFailureListener{exception->
if(exception.message?.contains("Failed to get document because
the client is offline")){
makeFirestoreQuery() //recursively call itself
//if the user is really offline, set a time out
}}
}
I faced with this problem on emulators below API 19 and asked Firebase support team.
They responded to me that is a secure connection problem and they suggested to me enabling TLS on that emulators.
As far as I understand, Firebase requires up-to-date security provider for connections.
If you think all of your Firebase configurations are correct and still facing with this problem on emulators below API 19, be sure emulator has up-to-date security provider.
Please check "TLS on Android" section at this link: https://github.com/grpc/grpc-java/blob/master/SECURITY.md#tls-on-android
Google says it provides a up-to-date provider via Google Play Services. If you want to use that, there is a document.
https://developer.android.com/training/articles/security-gms-provider.html

Categories

Resources