This is what I've come up with to handle auth in my Android app backed by anonymous authentication.
public class StartupActivity extends AppCompatActivity {
FirebaseAuth.AuthStateListener mAuthListener;
#Override
protected void onStart() {
super.onStart();
FirebaseAuth.getInstance().addAuthStateListener(mAuthListener = firebaseAuth -> {
if (firebaseAuth.getCurrentUser() != null) {
LoggedInActivity.newInstance(this);
} else {
IntroActivity.newInstance(this);
}
});
}
#Override
protected void onStop() {
FirebaseAuth.getInstance().removeAuthStateListener(mAuthListener);
super.onStop();
}
}
This StartupActivity is the one defined in my AndroidManifest.xml to be my app's main launcher Activity.
The pattern works well: at the end of IntroActivity, my code authenticates the user anonymously and sends them to the LoggedInActivity. Every launch after, the anonymous authentication persists through and the user goes straight to LoggedInActivity.
However, some users report losing their anonymous authentication and effectively losing their data since my other inner-app screens are driven off Firebase nodes that correspond to the user's UID.
This is pretty bad, but it only happens to a few users it seems. And only for anonymous auth - if it happened for email auth, it wouldn't even be a big deal, since users could log back in. But for anonymous, it's a pretty big issue. The user loses everything.
The issue could be tied to either Firebase SDK updates or app updates - that's when it seems to happen most / be reported most by my users.
Why is this happening? Is this a bad pattern for auth? I love the concept of using anonymous authentication to allow users to simply use your app without login, and I believe this is Firebase's intention as well. It's almost like I need to give them the option of backing their account with an actual login though, since this bug has such bad effects.
I believe I was able to reproduce this issue myself.
After getting an angry mail from a user, I tested what would happen if I updated the app (raising the version code for good measure) and tried to open it while my device was offline. Turns out Firebase logs me out, as I believe it might consider the internal copy of the Database outdated and wipes it completely, so it's basically the equivalent of a reinstall with no credentials. This doesn't happen after updating and opening the app while online.
This would explain why this only happens to a very small percentage of users, as they need to be Anonymous users who update the app and then try to open it with no internet connection in their devices, which I don't think would happen often.
The system was designed so that a user can start out in anonymous mode, in order to keep signup friction low. But you can write code to convert an anonymous account to a permanent account. This is what you should do for users who would like to retain their privileges if they're willing to provide some sign-in credentials.
Anonynmous auth credentials can be lost if you sign them out, or the user clears the app storage, or the user uninstalls the app.
Related
I'm not going to paste any code because I receive the desired behavior when a user creates account with a phone number in firebase auth.
My problem is after an app update, firebaseUser.getCurrentUser is null despite the fact that the user is already signed up.
My question:
How do I mimic a behavior like WhatsApp which doesn't require the user to always go through OTP after every app update?
Iv tried using authState:
auth.addAuthStateListener(firebaseAuth -> {
user = firebaseAuth.getCurrentUser();
if(user == null)
signUp() });
Hoping after update user won't be null. But it's always null after updating the app.
Actually what I didn't realize is that I was trying to mimic an update behavior by installing and uninstalling the app. Turns out token credentials are lost in the process. I just assumed Firebase attaches to the device identity some how, some where. I admit I need some instruction
My app is not automatically logging in when I restart the Android emulator. I believe previously it was doing so - though this might have been a bug caused by some bad code I have since ironed out. So to troubleshoot this problem I first need to discover whether or not this is simply a feature of the emulator.
Here is my code. I've confirmed that it successfully logs into FirebaseAuth and creates a user. According to documentation, automatically logging in on reboot should be as easy as this:
#Override
public void onStart() {
super.onStart();
//Get Firebase auth instance
auth = FirebaseAuth.getInstance();
// Check if user is signed in (non-null)
firebaseUser = auth.getCurrentUser();
}
The emulator has no bearing on the way Firebase Auth actually works. The problem is almost certainly that you're asking the SDK if the user is signed in before the SDK is certain about that. Instead of calling auth.getCurrentUser() you should use an auth state listener to get a callback when the final authentication state of the user is known. It might not be known immediately at launch, as the user's token might have expired and need to be refreshed at the server. This takes time.
Your app should wait until this auth state listener indicates that the user is actually signed. This means that your listener will actually be the thing to move your UI along to do things like make queries and present data to the user.
I have a DispatchActivity as my Launcher Activity, which is meant to check if there is a user currently signed in. If the user is signed in, I send them to their ProfileActivity. Otherwise, I send them to a LogInActivity. Here is my code in DispatchActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dispatch);
//.....................
auth = FirebaseAuth.getInstance();
authListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
launchProfileActivity();
} else {
// User is signed out
launchLoginActivity();
}
}
};
}
#Override
public void onStart() {
super.onStart();
auth.addAuthStateListener(authListener);
}
#Override
public void onStop() {
super.onStop();
if (authListener != null) {
auth.removeAuthStateListener(authListener);
}
}
No matter what I do, firebaseAuth.getCurrentUser(), never returns null on my primary testing device. According to the Docs, getCurrentUser() will be null unless there is a User Currently Signed in. When I Log some of this User's details, they are consistent with a "Test" user I created previously, (i.e. calling user.getEmail() returns "email#example.com".
This is problematic, as I most certainly don't have any users in my Console's User Database (In Authentication/Users). I deleted this test User from the database some time ago, and the database is empty.
I tried running the App on another device, and it executed properly. However, performing a fresh install on my primary testing device does not fix the problem.
Question:
As far as I can see, the issue is related to some kind of persistent user state with my device; since running a different device works fine. Since I haven't deliberately configured this to occur, I would like to know what is causing this inconsistency between my Auth User Database and the Device.
If the Database is empty, where is auth.getCurrentUser() getting this previously deleted user object from?
Thank you.
Firebase Authentication will cache an authentication token for the user when they're signed in. This prevents having to authenticate every little interaction the user makes with other services provided by Firebase. This token gets automatically refreshed periodically, but until then, the SDK assumes that the token represents the user. You'll find that the token will expire after some time, or you can force the issue by uninstalling and reinstalling the app, or clearing its data.
Authentication works regardless of the contents of your Realtime Database. They are independent from each other, except where security rules limit access to the currently authenticated user.
In Your Manifest File Add The Following
In the <manifest> tag (at the end)
xmlns:tools="http://schemas.android.com/tools"
Then in the <application> tag (at the begining)
tools:replace="android:allowBackup"
android:allowBackup="false"
android:fullBackupContent="false"
just as #Doug Stevenson mentioned above firebase saves some data on the phone adding these tags will ensure that that does not happen... so when you uninstall the app and install it again it will clear all the data associated with the app.
I had the same exact question and could not find the answer to this problem anywhere. I tried everything. I don't know why this works but I restarted my phone and it happened to fix everything. Super late but hope it helps for anyone else with the same problem.
I followed the below link to implement a "sign out" button in my android app, which uses a Google API client. However, upon connecting the google api again, the user is not presented with an account picker. It looks like the value of her/his original choice is somehow still cached perhaps. I've been trying to figure this out for a few hours.
Any and all ideas very welcome. Thank you.
https://developers.google.com/+/mobile/android/sign-in
if (mGoogleApiClient.isConnected()) {
Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
mGoogleApiClient.disconnect();
}
I've had many problems using clearDefaultAccount and trying to reconnect as well. Finally I've decided to separate the account selection process by using the AccountPicker class (which, by the way, doesn't require global permissions in manifest).
So, when the user wants to connect, always show the AccountPicker and then use the selected account to build your GoogleApiClient (see .setAccountName in GoogleApiClient.Builder).
Everything works smoothly now.
This works for me - use revoke to remove all data in the google client:
public void logout()
{
if (mPlusClient.isConnected())
{
Plus.AccountApi.clearDefaultAccount(mPlusClient);
Plus.AccountApi.revokeAccessAndDisconnect(mPlusClient);
}
}
Afterwards, if you try to login again, you'll be presented an account selector again
You are not being presented with an account picker because you didn't call
mGoogleApiClient.connect() after reconnecting.
I am trying to submit a status from my application to Window's live, the user starts the app, gets asked to give my app permissions to do this, and once granted I have a live session object and I can update their status. This works perfectly.
However, if the user closes the application and then opens it again, they are again asked to approve my app for this action. Every time.
Now the live documentation says you can obtain a refresh token (which I do) to prevent this, problem is the access token and the refresh token are all baked in the LiveConnectSession, so when my application is closed this object is destroyed and the user is asked to give the app permissions again.
So what I'd like to know is if anyone knows a way of recreating that object when the application starts (if I stored the token and refresh token) or a way of saving the object onDestroy()..
Iterable<String> scopes = Arrays.asList("wl.signin", "wl.share", "wl.offline_access" );
this.auth.login(this, scopes, this);
public void onAuthComplete(LiveStatus status, LiveConnectSession session, Object userState) {
if(status == LiveStatus.CONNECTED) {
Log.d("", "Signed in.");
client = new LiveConnectClient(session);
stuck with the same issue using Windows Phone..
I have tried serializing the session, which does not work because the session class has no default constructor.
EDIT:
after two full days searching for the mistake I was making, I finally found out what I was doing wrong: I have to use the wl.offline_access scope to make this work!
Now everything is fun again. Can't believe that this was the problem. Tested & working. Nice!
As I can see, you are using the offline scope, so that's not the problem for you.
But I have found out more:
there are two ways to connect to Live (in C#, I don't know how the methods are called in Java):
use LiveConnectClient.LoginAsync (which comes with GUI)
use LiveConnectClient.InitializeAsync (which is UI less and connects in background)
So if your application is already connected, use the second one to gain access to a new session object.
AFAIK, this object is valid for one year, after that, the user has to sign in again. But don't quote me on that.
Please let me know if this works for you.