Detect Firebase Auth Provider for Loged in User - android

How can i check that my user Loged in to my app Using which Auth Provider ? I wanted to detect that did my user loged in to my app using Facebook auth provider or using Email Provider or by using Google auth provider . I have searched this in Firebase Docs but i couldnt find any proper answer ,

FirebaseUser firebaseUser = FirebaseAuth.getInstance().getCurrentUser();
if (firebaseUser.getProviderData().size() > 0) {
//Prints Out google.com for Google Sign In, prints facebook.com for Facebook
e("TOM", "Provider: " + firebaseUser.getProviderData().get(firebaseUser.getProviderData().size() - 1).getProviderId());
}

You can always check the list of providers as Malik pointed out. However, as you can have multiple providers linked to the same user, to get the sign in method of the current User with multiple providers, you have to check the ID token. You need to check firebase.sign_in_provider claim in the token. That will give you the sign in method used to get the ID token. To get it on the client, you need to getIdToken and then parse the returned JWT with some JWT parser.

You can use method getIdTokenResult() of your user object (firebase.User) to get IdTokenResult object, whose signInProvider property you can use to detect signin method of your logged in user.

Reached here for same issue, unable to find the provider that user used to login.
I am working on react-js app using react-firebaseui lib for readymade UI.
After a lil struggle, I simply analysed the "user" object returned by auth and in that we receive an array "providerData".
Without further analysis I decided to use:
const signinProvider = user.providerData[0].providerId;
In my case, we use only 2 providers google & password.
I get the user from "onAuthStateChanged" function as below:
import { fbAuth } from "./firebase";
fbAuth.onAuthStateChanged(function (user) {
if (user) {
console.log("authStateChanged:=====", user);
useItFurther(user);
} else {
console.log("authStateChanged: no user logged in.");
cleanUpAuthDetailsIfApplicable();
}
});
CAUTION: I haven't researched why is providerData an array and what more can be there in that array, what could be the sequence when there are more than 1 objects in that array, etc.
In my case, we had to add a condition for email validation based on provider. Like, for a particular domain email address, force user to use a specific provider.

I have been puzzled over this problem and couldnt find an appropriate solution for a long time as well. The solution turns out to be short:
String strProvider = FirebaseAuth.getInstance().
getAccessToken(false).getResult().getSignInProvider();
So, if (strProvider.equals("password")) then the authentication is by Email + Password,
if (strProvider.equals("google.com")) then the authentication is via Google,
if (strProvider.equals("facebook.com")) then the authentication is via Facebook.
Addition
However, with this one-liner you can get an exception wchich can be prevented by adding OnSuccessListener like so:
mAuth = FirebaseAuth.getInstance();
mAuth.getAccessToken(false).addOnSuccessListener(new OnSuccessListener<GetTokenResult>() {
#Override
public void onSuccess(GetTokenResult getTokenResult) {
strProvider = getTokenResult.getSignInProvider();
}
});

Alternative
The getProviders() method list is sorted by the most recent provider
used to sign in. So the first element in getProviderData() is the
method that the user used to sign in.
Also the reason why
FirebaseAuth.getInstance().getCurrentUser().getProviderId() returns
firebase is because a backing Firebase Account is always created
irrespective of how the user signs in. This helps with linking and
unlinking of accounts where users may want to attach more than one
credential (however your view of the FirebaseUser has not changed).
as mention this post.

Related

Firebase Authentication returns NULL for getCurrentUser() even though there's a user in Firebase Console

When a user in a registration fragment enters a username, email, and password he is forwarded to a verification fragment where I sent him email verification and he stays on that screen until he clicks the link from the email, after that he's forwarded to profile fragment.
While the user is on the verification fragment, he can go back to the registration fragment before clicking the link from the email I sent. In that case, I call a function from onStart() to delete his data from the Firebase:
private fun deleteUnverifiedAccount() {
val firebaseAuthentication = FirebaseAuth.getInstance()
if (firebaseAuthentication.currentUser == null) {
shortToast("empty")
}
if (firebaseAuthentication.currentUser != null) {
if (!firebaseAuthentication.currentUser.isEmailVerified) {
val user = firebaseAuthentication.currentUser
user.delete()
.addOnCompleteListener {
shortToast("deleted")
}
}
}
}
Here's the problem:
This function successfully deletes the user from Firebase Authentication
But when the user tries to register the second time, his data is again being saved to Firebase Authentication.
And if at the moment he goes back again to the registration fragment without verifying his email then his data is not deleted from Firebase Authentication.
And when the deleteUnverifiedAccount() function is called it says that currentUser == null even though he is clearly not null in Firebase Authentication.
Firstly i did not try to delete any user from firebase but i have just searched something and i find this link https://www.javatpoint.com/firebase-realtime-database-update-and-delete this link will help you because i think problem of your code you try to delete your user but not user's data.
your syntax will be like this
myFirebase.child(current user's id).remove value
can you try this before user.delete() i hope it works

How to update Firebase user profile on sign in Android?

Currently, I update FirebaseUser profile after sign in, can I do it as part of sign-in parameters? i.e as an atomic operation?
The reason is that I want to avoid handling error of profile not updated correctly e.g. not a stable internet connection or app crash
I tried to update the profile name before the actual sign-in, hoping it will take it from the previous but it did not work
Please note the user before sign-in is anonymous and its uid might be different from the final signed-in user
This is my current code
private fun signInWithPhoneAuthCredential(credential: PhoneAuthCredential) {
FirebaseAuth.getInstance().signInWithCredential(credential)
.addOnCompleteListener(requireActivity()) { task ->
if (task.isSuccessful) {
updateProfile()
} else {
codeError()
}
}
}
private fun updateProfile() {
val profile = UserProfileChangeRequest.Builder()
.setDisplayName("user name")
.build()
FirebaseAuth.getInstance().currentUser!!.updateProfile(profile)
goBackToMain()
}
Currently I update Firebase user profile after sign in
That's the right thing to do. When you call signInWithPhoneAuthCredential(), a FirebaseUser object is created. Once this object is created, it can be updated.
I tried to update the profile name before the actual sign-in, hoping it will take it from the previous but it did not work
Indeed it's the expected behavior because, before the sign-in, the FirebaseUser object:
FirebaseAuth.getInstance().currentUser
Will return null, meaning that the user is not signed in. There is no way you can update an object that that is not initialized and holds the value on null.
Please note the user before sign-in is anonymous and its uid might be different from the final signed-in user.
It won't unless the user signs-out or uninstalls the app. When you link an anonymous account with a Provider, for example with Google, the UID of the user remains the same, meaning that:
FirebaseAuth.getInstance().currentUser.uid
Is the same as before you linked the account.

Firestore rules not working using displayName, Android

I have firestore rules like this:
match /{document=**} {
allow read, write: if request.auth.token.name == "dummyUser";
}
Now when I createUserWithEmailAndPassword, after I created the user, I set the displayName to "dummyUser" like this, with method setUsersSecureName() that I made:
fun setUsersSecureName(myCallback: (Boolean?) -> Unit) {
val user = FirebaseAuth.getInstance().currentUser
val profileUpdates = UserProfileChangeRequest.Builder()
.setDisplayName("dummyUser")
.build()
user?.updateProfile(profileUpdates)
?.addOnCompleteListener { task ->
if (task.isSuccessful) {
Log.d(TAG_HELPER_METHODS, "Secure user profile updated.")
myCallback(true)
}
}
}
I do the same for anonymous logins also. So I create anonymous log in like this:
auth.signInAnonymously()
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG_MAIN, "signInAnonymously:success")
val user = auth.currentUser
setUsersSecureName(){
makeRequest()
}
As you see, after login is successful with anonymous user, I change the users name. Now it seems to be changed, when I check it, the users displayName is "dummyUser". However, the firestore request doesn't work, and I get the message in the log:
PERMISSION_DENIED: Missing or insufficient permissions.
This is also not working when users are signed in with email and password. Why would this be?
Thanks
Security rules work by receiving a user ID token from Firebase Authentication at the time of the request. The Firestore SDK does this automatically. You normally don't have to do anything special.
However, in this specific case, after you update the user's profile, Firebase Authentication is still holding on to a user ID token that doesn't know about the change of name. You will probably also have to tell Firebase Authentication to fetch a new one by calling user.getIdToken(true) after the profile is successfully updated, in order to force a refresh of the ID token. After the refresh succeeds, then you can try the Firestore query to see if it works. user.reload() might work as well. Both of those methods are asynchronous and return a Task that you should use to track the completion of the request.

Dealing with FirebaseUI sign-in and offline Firestore data?

I am having difficulty figuring out how user authentication/sign-in via FirebaseUI works in relation to the offline persistence of data from a Firestore database in Android.
I understand a user must be signed in in order to retrieve their documents from the database but what happens when the user is offline? How do I set up the flow of user and data checks in my app before displaying the user's list of documents, if any?
Please correct me if my understanding of the documentation (FirebaseUI and Firestore offline data) below is wrong.
So there are I think basically 3 pages a user would see:
A sign-up/sign-in page
An empty page when the user has no data in the database
A list of their documents
The first sign-up/sign-in page should be displayed for first time users and signed out users (whether the user has signed-out themselves or their sign-in token has expired). This is where FirebaseUI comes to the rescue. Can I check for both cases with just the getCurrentUser method? What does this method return when the user is offline? Have I missed this somewhere in the documentation on managing users?
The second empty page should be displayed for signed in users who don't have any data in the database (whether because they have just signed in for the first time or they have deleted all of their data). Do I use a get call to check for data? What does it return when there is no data or what listener do I have to use? Have I missed this somewhere in the documentation on getting data?
The third list page should be displayed for signed in users who have existing data or who have just created data/documents. This can be obtained with a query on a collection via a get call on that collection.
Finally, would you tie all this together from within one activity/fragment in the following way and order in onCreate/onCreateView?
First - Check for first time and signed out users: If yes then display (inflate) the first page (i.e. launch the FirebaseUI sign in intent activity). What happens after the user has signed up/signed in? Is the user brought back to the originating activity/fragment? How do I handle this?
Second - Check for data in the database: If there is no data then display an 'empty' page. If there is data then display the list of documents instead. It seems this can be handled by switching visibility between say a TextView with the text "Empty" and the RecyclerView in the same layout (see this SO post).
Please help!
Can I check for both cases with just the getCurrentUser method? What does this method return when the user is offline?
When calling getCurrentUser() method on a FirebaseAuth object it returns an object of type FirebaseUser if the authentication process is successful.
FirebaseUser firebaseUser = firebaseAuth.getCurrentUser();
But before that you need to instantiate the firebaseAuth object by calling the static FirebaseAuth.getInstance() method like this:
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance();
So if the authentication process is successful and you are getting offline, it doesn't matter, calling getCurrentUser() will always return the FirebaseUser object.
Do I use a get call to check for data?
Yes, you should use a get() call and first time check if (task.isSuccessful()) and second if the data exist at a particular location.
What does it return when there is no data or what listener do I have to use?
It will return an empty DocumentSnapshot object. So first you'll need to use a get() and use the addOnCompleteListener().
This can be obtained with a query on a collection via a get call on that collection.
Yes that's correct.
Finally, would you tie all this together from within one activity/fragment in the following way and order in onCreate/onCreateView?
Yes, you can tie all this together from within one activity/fragment.
What happens after the user has signed up/signed in? Is the user brought back to the originating activity/fragment? How do I handle this?
If the user signs out, you should redirect the user to the LoginActivity. I have exaplained in one of my tutorials step by step, the entire authentication process using Google and Firebase.
Check for data in the database: If there is no data then display an 'empty' page.
This is a recommended way in which you can retrieve data from a Cloud Firestore database and display it in a RecyclerView using FirestoreRecyclerAdapter. So in such case you can override the onDataChanged() like this:
#Override
public void onDataChanged() {
if (getItemCount() == 0) {
recyclerView.setVisibility(View.GONE);
emptyView.setVisibility(View.VISIBLE);
} else {
recyclerView.setVisibility(View.VISIBLE);
emptyView.setVisibility(View.GONE);
}
}

Use multiple firebase accounts in single android app for google analytics

I have a use case in which 1 app will be used by multiple separate companies (franchises) that will have their own marketing and management teams. I need the user to select a franchise when the mobile app starts up, and then from that point on, push all analytics data to that franchise's firebase (google analytics) account. Similarly any push notifications that are sent from that franchise's servers need to go to the user.
Is this configuration possible? In the past I used to set up a google analytics account for each franchise and just download the UA-xxx number from the server on franchise selection, and then set up the google analytics object based on that..
What is the appropriate way to achieve this via firebase connected to google analytics ?
I found the offical API reference: https://firebase.google.com/docs/configure/
This link explains how to do it for iOS but doesn't mention how to do it in android. It does say however that the firebase init runs before user code.. perhaps that means it is not possible?
Here is the init provider they mention: https://firebase.google.com/docs/reference/android/com/google/firebase/provider/FirebaseInitProvider
create for each new firebase app
FirebaseApp firebaseApp =
FirebaseApp.initializeApp(Context, FirebaseOptions,firebaseAppName);
you can create firebase app by passing options:
https://firebase.google.com/docs/reference/android/com/google/firebase/FirebaseOptions.Builder
FirebaseOptions options = new FirebaseOptions.Builder()
.setApiKey(String)
.setApplicationId(String)
.setDatabaseUrl(String)
.build();
then when You want to use Analytics you need to set default one by call:
FirebaseApp firebaseApp =
FirebaseApp.initializeApp(Context, FirebaseOptions,"[DEFAULT]");
keep in mind that only this DEFAULT firebase app will be used in analytics
but first off all you need to remove init provider in manifest
<!--remove firebase provider to init manually -->
<provider
android:name="com.google.firebase.provider.FirebaseInitProvider"
android:authorities="${applicationId}.firebaseinitprovider"
tools:node="remove"/>
and init default firebase app manually!
example how to send event via default firebase app(after initialized):
// get tracker instance
FirebaseAnalytics trakerInstance = FirebaseAnalytics.getInstance(context);
// create bundle for params
Bundle params = new Bundle();
// put param for example action
params.putString(ACTION_KEY, eventAction);
// send event
trackerInstance.logEvent(eventCategory, params);
#ceph3us I tried your solution, and it didn't work for me. If I
initialise firebase at runtime as you suggested then I get an error:
Missing google_app_id. Firebase Analytics disabled. Are you sure it is
working? – rMozes
first of all
did you removed default provider by putting in manifest tools:node="remove"?
did u initialized ['DEFAULT'] firebase app as i described
did you check if a ['DEFAULT'] firebase app is initialized before sending any event ?
ad 1) the error: Missing google_app_id suggests me that gardle plugin didn't removed provider as expected - and your app is starting a default provider which complains about missing app id
ad 3) don't do any calls relying on firebase app before firebase app is initialized
protected boolean isDefaultFirebaseAppInitialized() {
try {
// try get
return FirebaseApp.getInstance(FirebaseApp.DEFAULT_APP_NAME) != null;
// catch illegal state exc
} catch (IllegalStateException ise) {
// on such case not initialized
return false;
}
}
// check on default app
if(isDefaultFirebaseAppInitialized()) {
// get tracker
FirebaseAnalytics trakerInstance = FirebaseAnalytics.getInstance(context);
// log event
trackerInstance.logEvent(eventCategory, params);
} else {
// postpone
}
#ceph3us 1. I removed the provider as you said, if I wouldn't remove
the provider and try to initialise the default app then I would get a
IllegalStateException about default firebase app already exists. 2. I
initialised default firebase app as you described. 3. Yes, I logged
the app name and app_id and there is a log: AppName: [DEFAULT], Google
app id: valid_app_id But when I want to post something to analytics,
then it says that: Missing google_app_id. Firebase Analytics disabled.
– rMozes
99,99% you are trying to send event before app is initialized ! (see above example)
#ceph3us I initialise FirebaseApp in the onCreate method of
Application subclass. I send event to firebase when a button is
clicked. Anyway I uploaded a test project to github, can you take a
look at it? It is possible I misunderstand something.
github.com/rMozes/TestFirebaseAnalytics – rMozes
try (as initialization is asynchronous - so until you test its initialized you cant send events):
https://github.com/rMozes/TestFirebaseAnalytics/compare/master...c3ph3us:patch-2
if above fails you have two more chances :)
by define the string in xml: as a placeholder
<string name="google_app_id">fuck_you_google</string>
1) change the placeholder id via reflections to other one before any call to init/or use from firebase:
hint how to
2) provide a own text for the placeholder id via own Resources class implementation for Resources.getString(R.string.google_app_id) call:
an example how to achieve it (adding a new resources by id)
if you proper change R field via reflections or substitute a call to Resources.getString(R.string.google_app_id) with own text you will not get message wrong app id: "fuck_you_google"
& good luck :)
It's possible to have multiple Firebase instance in your apps
FirebaseOptions options = new FirebaseOptions.Builder()
.setApplicationId("Your AppId") // Required for Analytics.
.setApiKey("You ApiKey") // Required for Auth.
.setDatabaseUrl("Your DB Url") // If you wanted to
.build();
FirebaseApp.initializeApp(context, options, "CompanyA");
Which you can get Firebase instances by
FirebaseApp appCompanyA = FirebaseApp.getInstance("CompanyA");
You can see the full example use of Auth and Realtime Database using multiple Firebase instance here
I'll leave this solution here which may duplicate with another answers for someone who might need this
Hope this help :)

Categories

Resources