How to check if Firebase Facebook user exists - Unity - c#

I want to check whether the user already exists in Firebase when they log in with Facebook or whether it's their first time. Firebase documentation just provides a function to login to facebook but doesn't mention if the user is new or not. A new user gets a new firebase user id, while an existing user just logs in.
By reading other similiar questions here- some people advised using AdditionalUserInfo.isNewUser for Javascript, but this function doesn't work for Unity code. Nothing about Unity here.
Basic code lines (after login to facebook)
Firebase.Auth.Credential credential =
Firebase.Auth.FacebookAuthProvider.GetCredential(accessToken);
auth.SignInWithCredentialAsync(credential).ContinueWith(task => {
if (task.IsCanceled) {
Debug.LogError("SignInWithCredentialAsync was canceled.");
return;
}
if (task.IsFaulted) {
Debug.LogError("SignInWithCredentialAsync encountered an error: " +
task.Exception);
return;
}
Firebase.Auth.FirebaseUser newUser = task.Result;
Debug.LogFormat("User signed in successfully: {0} ({1})",
newUser.DisplayName, newUser.UserId);
});
One solution that I was thinking about is to catch the exception - but couldn't find the exception code in their documentation - Firebase don't have a lot of info for Unity there

Here is a brief code that can help you.
public FirebaseAuth auth;
auth.FetchProvidersForEmailAsync(emailGotFromFacebook).ContinueWith((emailCheckingTask) =>
{
List<string> allProviders = ((IEnumerable<string>)emailCheckingTask.Result).ToList();
for (int i = 0; i < allProviders.Count; i++)
{
string provider = allProviders[i];
Debug.Log(provider);
}
});

You can check wheter it's the first login or if the user already exists using the following comparison: newUser.Metadata.CreationTimestamp == newUser.Metadata.LastSignInTimestamp
.. as shown below:
Firebase.Auth.FirebaseUser newUser = task.Result;
if (newUser.Metadata.CreationTimestamp == newUser.Metadata.LastSignInTimestamp) {
Debug.Log("User signed for the first time");
}
else {
Debug.Log("User already exists");
}

Related

Unity Firebase SignInAnonymously sometimes works and sometimes not

I have an issue with signing in anonymously into my Firebase database for my Unity game. I have a method for signing anonymously into the database and another one that reads the database and prints a json string.
public IEnumerator anonymousSignIn()
{
var register = auth.SignInAnonymouslyAsync();
yield return new WaitUntil(predicate: ()=> register.IsCompleted);
}
public IEnumerator readDatabase()
{
var DBTask = DBreference.Child("users").GetValueAsync();
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning(message: $"Failed to register task with {DBTask.Exception}");
}
else if (DBTask.Result.Value == null)
{
Debug.LogWarning("No data found in the database");
}
else
{
DataSnapshot snapshot = DBTask.Result;
string json = snapshot.GetRawJsonValue();
Debug.Log(json);
}
}
I then call these functions via a button in my Unity games using the method:
public void readButton()
{
StartCoroutine(anonymousSign());
StartCoroutine(readDatabase());
}
However, this sometimes works and other times It says permission denied and I don't understand why.
My database rules for reading are: ".read": "auth != null",
I got the same error.
After that, I changed the code
From:
Auth.SignInAnonymouslyAsync().ContinueWith(task => { SignInAnonymously(task, "Guest"); });
To:
Auth.SignInAnonymouslyAsync().ContinueWithOnMainThread(task => { SignInAnonymously(task, "Guest"); });
Don't use Async for Anonymous Sign In, but run on Main Thread.
It worked for me!

How to load a scene when it completes getting data from firebase?

there are two scenes in my game.
first one is "main Scene" and second one is "user Data Scene".
in "user data scene" I am retrieving current user data from firebase.
The problem is when I switch to "user data scene" from "main scene" there are no data for few seconds until its retrieved from firebase. it looks very bad to me.
All I want is when user is on "main scene" and try to open "user data scene" it should be not active until retrieving data from firebase is done.
I attached the script that gets data from firebase to the main scene's empty object which does not destroy on load. and calling it in start function of the "user data scene".
I am calling below function in start function of "user data scene".
I want to wait until data is loaded from firebase and value is attached to text objects before activating the "user data scene" ?
public IEnumerator getUserDataFromDb(Text userNameTxt, Text totalDiamondTxt, Text totalGoldText) //get data from database of diamond and how much diamond has been sold
{
userNameTxt.text = FirebaseAuth.DefaultInstance.CurrentUser.DisplayName;
var getDbTask = FirebaseDatabase.DefaultInstance.GetReference("Users").Child(FirebaseAuth.DefaultInstance.CurrentUser.UserId).GetValueAsync();
yield return new WaitUntil(predicate: () => getDbTask.IsCompleted);
if (getDbTask.Exception != null)
{
Debug.LogWarning(getDbTask.Exception.InnerExceptions[0].InnerException.Message);
}
else
{
DataSnapshot snapshot = getDbTask.Result;
totalDiamondTxt.text = snapshot.Child("Sell").Child("Total").Child("TotalDiamond").Value.ToString();
int dbDiamondValue = int.Parse(snapshot.Child("Sell").Child("Total").Child("TotalDiamond").Value.ToString());
totalGoldText.text = snapshot.Child("Sell").Child("Total").Child("TotalGold").Value.ToString();
int dbGoldValue = int.Parse(snapshot.Child("Sell").Child("Total").Child("TotalGold").Value.ToString());
}
}
Maybe something like this:
Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWith(task => {
var dependencyStatus = task.Result;
if (dependencyStatus == Firebase.DependencyStatus.Available) {
db = Firebase.Firestore.FirebaseFirestore.DefaultInstance;
//use your data and load your secene after
DocumentReference docRef =
db.Collection("yourCollection").Document("yourDocumentCode");
docRef.GetSnapshotAsync().ContinueWithOnMainThread(task => {
DocumentSnapshot snapshot = task.Result;
if (snapshot.Exists) {
//do you stuff here after retrieving the info
} else {
Debug.Log(String.Format("Document {0} does not exist!", snapshot.Id));
}
});
} else {
UnityEngine.Debug.LogError(System.String.Format("Could not resolve all Firebase dependencies: {0}", dependencyStatus));
// Firebase Unity SDK is not safe to use here.
}
});
Need that for the specific use of firestore, but you got a similar snippet in the documentation so that right after you do whatever you need with the firebase services you move on executing your app.

Unity Firebase Authentication CreateUserWithEmailAndPasswordAsync returns empty UserID

When I run CreateUserWithEmailAndPasswordAsync() the task returns as IsCompleted and the FirebaseUser is also not null, but the UserID and email are null/empty. I can see the new user (with UserID) in the firebase console.
The same thing happens when trying to SignInWithEmailAndPasswordAsync() with the newly created User. But when I SignInWithEmailAndPasswordAsync() with an older user account everything works fine.
It's strange because it is the same code I used a year ago. In the mean time I did update the Firebase SDK to 7.0.2. Could that be the cause of the issue?
Here is my code for creating a new user:
public static async Task<CallbackMessage> RegisterNewUser(string email, string password)
{
CallbackMessage callback = new CallbackMessage();
callback.isSuccess = false;
if (!await DatabaseManager.CheckConnection())
{
callback.notConnected = true;
return callback;
}
return await auth.CreateUserWithEmailAndPasswordAsync(email, password).ContinueWith(task =>
{
if (task.IsCanceled)
{
callback.errorMessage = "Create User was canceled";
}
else if (task.IsFaulted)
{
callback.errorMessage = GetErrorMessage(task.Exception);
}
else if (task.IsCompleted)
{
FirebaseUser newUser = task.Result;
Debug.Log("User created succesfully! userID: " + newUser.UserId);
callback.userID = newUser.UserId;
callback.email = newUser.Email;
callback.isSuccess = true;
}
CleanTask(task);
return callback;
});
}
Could really use a nudge in the right direction on this one :)
I was able to solve it. Apologies for doubting the Firebase Auth service.
In my AuthStateChanged code I added a Signout() whenever a user signed in, but wasn't emailVerified yet. That's where the problem was.
My guess what happened: When user creation is called it signs the user in immediately, then goes to Firebase to set things up there. In the mean time I sign out the user because it's not email verified. When the callback comes back it looks for the FirebaseUser that it signed in before going to Firebase, but finds nothing.
Something like pulling the chair from underneath someone as he's about to sit :P

store logged in user details in xamarin forms using SQLite

I am working on Xamarin forms applicaiton which will have three welcome pages while the user is logging into the applicaiton for the first time, if the user user is trying to start the application after logging in only dashboard has to be displayed. I have written the following code for that :
public LoginUserInfoDbModel SaveUserDetails(LoginUserInfoDbModel model)
{
if(model.LoggedInUser!=null)
{
return null;
}
else
{
connection.Insert(model);
return model;
}
}
and called the above method in LoginPage.xaml.cs in the following way :
if (model.ResultString == "Valid")
{
App.UserDatabase.SaveUserDetails(model);
await DisplayAlert("", "You have logged in succesfully ", "Ok");
await Navigation.PushAsync(new DashBoardPage());
}
So now I need to check whether the User is already a existing user or not in DB, so I have written the following code in App.xaml.cs :
var connection = DependencyService.Get<ISQLiteDb>().GetConnection();
var c=connection.CreateTableAsync<LoginUserInfoDbModel>().Result;
var UserData = connection.Table<LoginUserInfoDbModel().ToListAsync().Result?.FirstOrDefault();
if(UserData != null)
{
MainPage= new DashBoardPage();
}
else
{
MainPage=new WelcomePages();
}
But this code is not working I am getting null reference Exception and some other exceptions, So do we have any alternative for this scenario or else can you suggest any modifications for this code ?

Check if PasswordVault/credential manager has app data at load

Hey I'm using PasswordVault for storing user credentials in my windows 8 app.
What I want the app to do on loading is check to see if the PasswordVault/credential manager already has a stored value for my app. if it don't don't I want it to stay on the page so the user can login, but if the credentials are already there then I would like it to go straight to page 2.
I tried using the following code:
private Windows.Security.Credentials.PasswordCredential GetCredentialFromLocker()
{
Windows.Security.Credentials.PasswordCredential credential = null;
var vault = new Windows.Security.Credentials.PasswordVault();
var credentialList = vault.FindAllByResource("MYapp");
if (credentialList.Count > 0)
if (credentialList.Count == 1)
credential = credentialList[0];
else
// User selecor
return credential;
}
and then on page load I have
private void Page_Loaded(object sender, RoutedEventArgs e)
{
var loginCredential = GetCredentialFromLocker();
if (loginCredential != null)
this.Frame.Navigate(typeof(page2));
else
{
loginBigButton.Visibility = Windows.UI.Xaml.Visibility.Visible;
signUpButton.Visibility = Windows.UI.Xaml.Visibility.Visible;
signUpTextBlock.Visibility = Windows.UI.Xaml.Visibility.Visible;
}
}
The problem is that if there is no credential stored with the Resource (MYapp) the code:
var credentialList = vault.FindAllByResource("MYapp");
yields:
WinRT information: Cannot find credential in Vault
Additional information: Element not found.
Method FindAllByResource throws exception when there are no credentials for specified resource, so you need to wrap it with try catch block.
Alternatively you can use 'RetrieveAll' which doesn't throw exception if there are no credentials stored and iterate over each returned PasswordCredential and check it's Resource property.
I'll try to answer this question the best as I can:
Firstable as Tadeusz said FindAllByResource throws an exception when there are no credentials for specified resource, in order to dont crash you app, you would need to wrap you logic within a try-catch block.
the reason to do this is for you to be able to create your resource in case you dont have one. so the way I see it, you logic should look like this:
private Windows.Security.Credentials.PasswordCredential GetCredentialFromLocker()
{
Windows.Security.Credentials.PasswordCredential credential = null;
var vault = new Windows.Security.Credentials.PasswordVault();
try
{
var credential = vault.FindAllByResource("MYapp").FirstOrDefault();
return credential;
}
catch(Exception ex)
{
Debug.WriteLine($"Error retrieving Token: {ex.Message}");
}
return null;
}
now all you have to do is to store a new token in your passwordvault
you have to login first and then you store the token, so next time you try to retrieve your credentials it wont throw an exception since its already store.
by example it should be something like this:
await client.LoginAsync(provider);
then you store your token like this:
PasswordVault.Add(new PasswordCredential("MYapp",
client.CurrentUser.UserId,
client.CurrentUser.MobileServiceAuthenticationToken));
I hope this answer helps in order, sorry for the late answer but, I found myself trying to solve this problem right now and I thought i should give a more detail or complete answer to this question.
Also you should consider token expiration checks and refreshing handling.

Categories