The following 3 functions are used to set up email and other auth checks (called in order)
private void buildSignInIntentBuilder() {
ActionCodeSettings actionCodeSettings = ActionCodeSettings.newBuilder()
.setAndroidPackageName(getString(R.string.packageName), true, null)
.setHandleCodeInApp(true)
.setUrl(getString(R.string.dynamic_link_url))
.build();
List<AuthUI.IdpConfig> providers = Arrays.asList(
new AuthUI.IdpConfig.EmailBuilder()
.enableEmailLinkSignIn()
.setActionCodeSettings(actionCodeSettings)
.build(),
// new AuthUI.IdpConfig.EmailBuilder().setRequireName(false).build(),
new AuthUI.IdpConfig.PhoneBuilder()
.build(),
new AuthUI.IdpConfig.GoogleBuilder()
.build(),
new AuthUI.IdpConfig.FacebookBuilder()
.build());
signInIntentBuilder = AuthUI.getInstance()
.createSignInIntentBuilder()
.setIsSmartLockEnabled(false)
.setAvailableProviders(providers)
.setLogo(R.drawable.icon_forget_me_not_1);
}
private void catchEmailLinkSignIn() {
Log.d(TAG, "Intent: " + getIntent().getExtras());
if (AuthUI.canHandleIntent(getIntent())) {
if (getIntent().getExtras() == null) {
return;
}
String link = getIntent().getExtras().getString(ExtraConstants.EMAIL_LINK_SIGN_IN);
Log.d(TAG, "link: " + link);
if (link != null) {
signInIntentBuilder.setEmailLink(link);
}
}
}
private void createCheckAndSigninListener() {
// set firebase sign in listener
mAuthStateListner = firebaseAuth -> {
// Already logged in
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
Log.d(TAG, "user already signed in");
// Check user even if signed in to register him to database (if haven't)
FirebaseAuthHelper.getInstance().checkRegisterUser(user, this, CHECK_USER_DB);
} else {
Log.d(TAG, "user hasn't signed in");
// Signed out or hasn't logged in
startActivityForResult(
signInIntentBuilder
.build(),
RC_SIGN_IN
);
}
};
}
I have set up a dynamic link with firebase hosting. And being able to redirect into the same activity upon clicking the received email link.
However,
String link = getIntent().getExtras().getString(ExtraConstants.EMAIL_LINK_SIGN_IN);
Log.d(TAG, "link: " + link); // --> produces "link: null"
Shows despite successfully getting a intent, there is no EMAIL_LINK_SIGNIN extra in the getExtras(). I spend few hours looking into the source code of FirebaseUi, but I didn't find where the constant EMAIL_LINK_SIGN_IN is used and how is the intent from dynamic link parsed.
Any idea how to fix this problem is appreciated. I had already spent a whole day trying to figure this out.
Instead of using String link = getIntent().getExtras().getString(ExtraConstants.EMAIL_LINK_SIGN_IN);
use getIntent().getData().toString(); instead.
Related
We have an XF Forms app that we are adding subscription support for. We implemented Plugin.InAppBilling. Works as expected on iOS. We followed JM's docs for configuring on Android - but getting "This app not configured for subscriptions" error.
This is the purchase method we are using:
public async Task<InAppBillingPurchase> MakePurchase()
{
if (!CrossInAppBilling.IsSupported)
return null;
var currentPage = GetCurrentPage();
if(currentPage != null)
{
var confirm = await currentPage.DisplayAlert("Q5id subscription", "Become a subscriber to create an alert to find your loved one.","Confirm", "Cancel");
if (!confirm) return null;
}
var billing = CrossInAppBilling.Current;
try
{
var connected = await CrossInAppBilling.Current.ConnectAsync();
if (!connected)
{
//Couldn't connect to billing, could be offline, alert user
return null;
}
//try to purchase item
var purchase = await CrossInAppBilling.Current.PurchaseAsync(SUBSCRIPTION_PRODUCT_ID, ItemType.Subscription);
deviceService.DeviceLog("PurchaseAsync purchase: ", purchase);
var isFinish = await CrossInAppBilling.Current.FinishTransaction(purchase);
deviceService.DeviceLog("PurchaseAsync isFinish: ", isFinish);
return purchase;
}
catch (Exception ex)
{
Debug.WriteLine("Error MakePurchase: " + ex.Message);
deviceService.DeviceLog("Error MakePurchase: ", ex);
return null;
}
finally
{
await billing.DisconnectAsync();
}
}
We have verified bundleid, Play Console looks right. Any ideas?
I'm here to make a game with anonymous login with firebase auth, and a database with firebase real-time database, everything works smoothly except for firebase auth. The problem is I don't know why? the unity won't connect to firebase auth.
Installation
I already install the package like always database and auth
also for the firebase is like this
Script
here my login script :
//Firebase variables
[Header("Firebase")]
public DependencyStatus dependencyStatus;
public FirebaseAuth auth;
public FirebaseUser User;
public DatabaseReference DBreference;
public static FirebaseManager instance;
void Awake()
{
if (instance != null)
{
Destroy(gameObject);
}
else
{
DontDestroyOnLoad(gameObject);
instance = this;
}
FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task =>
{
dependencyStatus = task.Result;
if (dependencyStatus == DependencyStatus.Available)
{
InitializeFirebase();
}
else
{
Debug.LogError(
"Could not resolve all Firebase dependencies: " + dependency status);
}
});
}
public void InitializeFirebase()
{
auth = FirebaseAuth.DefaultInstance;
DBreference = FirebaseDatabase.DefaultInstance.RootReference;
auth.StateChanged += AuthStateChanged;
AuthStateChanged(this, null);
}
void AuthStateChanged(object sender, System.EventArgs eventArgs)
{
//This checks if the user (your local user) is the same as the one from the auth
if (auth.CurrentUser != User)
{
bool signedIn = User != auth.CurrentUser && auth.CurrentUser != null;
User = auth.CurrentUser;
if (signedIn)
{
Debug.Log("Signed in " + User.UserId + " " + User.DisplayName);
}
else
{
StartCoroutine(SignAnonymously());
}
}
}
//it does not directly log the user out but invalidates the auth
void OnDestroy()
{
auth.StateChanged -= AuthStateChanged;
auth = null;
}
public IEnumerator SignAnonymously()
{
var loginTask = auth.SignInAnonymouslyAsync();
yield return new WaitUntil(predicate: () => loginTask.IsCompleted);
if (loginTask.Exception != null)
{
//If there are errors handle them
Debug.LogWarning(message: $"Failed to register task with {loginTask.Exception}");
}
else
{
User = loginTask.Result;
Debug.LogFormat("User signed in successfully: {0} ({1})", User.DisplayName, User.UserId);
}
}
What I've tried
Check the google-service.json ( already same or updated )
already implement this https://firebase.googleblog.com/2020/08/firebase-compatibility-with-unity-20201.html due to a compatibility issue.
already changed continuewith to ContinueWithOnMainThread(task..
note: I'm using unity ver 2020.3.1f1 LTS
am I doing it wrong?
Try to replace your Coroutine method SignAnonymously with this async method:
public async void SignAnonymously()
{
await auth.SignInAnonymouslyAsync().ContinueWith(task => {
if (task.IsCanceled) {
Debug.LogError("SignInAnonymouslyAsync was canceled.");
return;
}
if (task.IsFaulted) {
Debug.LogError("SignInAnonymouslyAsync encountered an error: " + task.Exception);
return;
}
Firebase.Auth.FirebaseUser newUser = task.Result;
Debug.LogFormat("User signed in successfully: {0} ({1})",
newUser.DisplayName, newUser.UserId);
});
}
I think that the problem could be the lacking of ContinueWithat the end of your SignInAnonymouslyAsync, maybe you can use it on your Coroutine but I've never done it that way, tell me if it works ^^
maybe my code will mess here beacuse I'm new into firebase stuff and there is lack doc for newbie like me, so I want to make save and load data from firebase, there is 2 scene the first for main menu and second one the main game,so when firstime login or auto login, user will load current user data from realtime database (I'm using firebase) also applies to user when back to main menu.
in the second scene itself, user load current user highscore data at first start then when game over if the score > lasthighscore that will save/update latest highscore to realtime database.
here the preview this is when I auto-login then playgame until game over:
it's supposed to be updated but not when back to main menu, I already try with player prefs since i erase all data when sign out so the highscore always be 0 and if login with other account that highscore is not belong to other account.
here my firebase script on main menu:
private void Start()
{
Time.timeScale = 1;
mainPanel.SetActive(true);
InitializeFirebase();
}
void InitializeFirebase()
{
auth = FirebaseAuth.DefaultInstance;
DBreference = FirebaseDatabase.DefaultInstance.RootReference;
auth.StateChanged += AuthStateChanged;
AuthStateChanged(this, null);
}
void AuthStateChanged(object sender, System.EventArgs eventArgs)
{
//This checks if the user (your local user) is the same as the one from the auth
if (auth.CurrentUser != User)
{
//this seems the same, but user could have been null before
bool signedIn = User != auth.CurrentUser && auth.CurrentUser != null;
if (!signedIn && User != null)
{
Debug.Log("Signed out " + User.UserId);
loginPanel.SetActive(true);
}
//this is important step, this user is the one you should be working with
User = auth.CurrentUser;
if (signedIn)
{
Debug.Log("Signed in " + User.UserId);
userNameShowText.text = User.DisplayName;
StartCoroutine(LoadUserData());
loginPanel.SetActive(false);
// //highScoreMainMenu.text = PlayerPrefs.GetInt("highscore").ToString("0000000");
}
else
{
loginPanel.SetActive(true);
}
}
}
public IEnumerator Login(string _email, string _password)
{
//Call the Firebase auth signin function passing the email and password
var LoginTask = auth.SignInWithEmailAndPasswordAsync(_email, _password);
//Wait until the task completes
yield return new WaitUntil(predicate: () => LoginTask.IsCompleted);
if (LoginTask.Exception != null)
{
//If there are errors handle them
Debug.LogWarning(message: $"Failed to register task with {LoginTask.Exception}");
FirebaseException firebaseEx = LoginTask.Exception.GetBaseException() as FirebaseException;
AuthError errorCode = (AuthError)firebaseEx.ErrorCode;
string message = "Login Failed!";
switch (errorCode)
{
case AuthError.MissingEmail:
message = "Missing Email";
break;
case AuthError.MissingPassword:
message = "Missing Password";
break;
case AuthError.WrongPassword:
message = "Wrong Password";
break;
case AuthError.InvalidEmail:
message = "Invalid Email";
break;
case AuthError.UserNotFound:
message = "Account does not exist";
break;
}
warningLoginText.text = message;
}
else
{
//User is now logged in
//Now get the result
User = LoginTask.Result;
Debug.LogFormat("User signed in successfully: {0} ({1})", User.DisplayName, User.Email);
warningLoginText.text = "";
confirmLoginText.text = "Logged In";
StartCoroutine(LoadUserData());
yield return new WaitForSeconds(2);
userNameShowText.text = User.DisplayName;
UserDataScreen(); // Change to user data UI
confirmLoginText.text = "";
ClearLoginFeilds();
ClearRegisterFeilds();
}
}
public IEnumerator UpdateHighScore(int _highScore)
{
var DBTask = DBreference.Child("users").Child(auth.CurrentUser.UserId).Child("highscore").SetValueAsync(_highScore);
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning(message: $"Failed to register task with {DBTask.Exception}");
}
else
{
//giscrore now updated
}
}
and this is for the game scene also same function for update higscore like on main menu.
void Start()
{
InitializeFirebase();
YetGameOver();
//GetScore
score = 0;
scoreText.text = score.ToString("00000");
//highScoreText.text = "HI :" + PlayerPrefs.GetInt("highscore", 0).ToString("00000");
maxTime = .1f;
}
void InitializeFirebase()
{
auth = FirebaseAuth.DefaultInstance;
DBreference = FirebaseDatabase.DefaultInstance.RootReference;
auth.StateChanged += AuthStateChanged;
AuthStateChanged(this, null);
}
void AuthStateChanged(object sender, System.EventArgs eventArgs)
{
//This checks if the user (your local user) is the same as the one from the auth
if (auth.CurrentUser != User)
{
//this seems the same, but user could have been null before
bool signedIn = User != auth.CurrentUser && auth.CurrentUser != null;
if (!signedIn && User != null)
{
Debug.Log("Signed out " + User.UserId);
}
//this is important step, this user is the one you should be working with
User = auth.CurrentUser;
if (signedIn)
{
Debug.Log("Signed in " + User.UserId);
StartCoroutine(LoadUserData());
//highScoreMainMenu.text = PlayerPrefs.GetInt("highscore").ToString("0000000");
}
}
}
public void GameOver()
{
SaveData();
StartCoroutine(WaitToDeath());
}
public void SaveData()
{
Debug.Log("Saved");
StartCoroutine(UpdateHighScore(PlayerPrefs.GetInt("highscore", 0)));
}
public IEnumerator WaitToDeath()
{
_CamShake.instance.shouldShake = true;
DeathSound();
gameOverPanel.SetActive(true);
endHighScoreText.text = "HI :" + PlayerPrefs.GetInt("highscore").ToString("0000000");
scoreText.gameObject.SetActive(false);
highScoreText.gameObject.SetActive(false);
yield return new WaitForSeconds(.1f);
//_AdmobAds.instance.ShowInterstitialAd();
isStarted = false;
isGameOver = true;
Time.timeScale = 0;
}
//same function like main menu
public IEnumerator UpdateHighScore(int _highScore)
{
var DBTask = DBreference.Child("users").Child(auth.CurrentUser.UserId).Child("highscore").SetValueAsync(_highScore);
yield return new WaitUntil(predicate: () => DBTask.IsCompleted);
if (DBTask.Exception != null)
{
Debug.LogWarning(message: $"Failed to register task with {DBTask.Exception}");
}
else
{
//highscore are now updated
}
}
public IEnumerator LoadUserData()
{
//Get the currently logged in user data
var DBTask = DBreference.Child("users").Child(User.UserId).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)
{
//No data exists yet
highScoreText.text = "0";
endHighScoreText.text = "0";
}
else
{
//Data has been retrieved
DataSnapshot snapshot = DBTask.Result;
highScoreText.text = endHighScoreText.text = snapshot.Child("highscore").Value.ToString();
}
}
You don't capture child in this method. You can only capture all the data from the root. So, simply remove the child. It will work.
my solution to firebase realtime database not working was that my google-services.json wasnt updated after adding the google-services.json from firebase auth. firebase, project overview cogwheel, project settings, json on that page somewhere.
and my problem with firebase auth before that was that i didnt set the Client ID in unity, window, google play games, setup, android setup.. get the id from google play console, play games services, setup & management, configuration, and figure out how to add game server credentials
I have a Xamarin.Forms application with a few renewable subscriptions. I am using the InAppBilling plugin to purchase these subscriptions. Now my questions is: (which I already asked in this post) How can I check if the renewable subscription is active? Thanks in advance.
Through the document, you can check the status by
var connected = await billing.ConnectAsync(ItemType.Subscription);:
Here is the example:
public async Task<bool> PurchaseItem(string productId, string payload)
{
var billing = CrossInAppBilling.Current;
try
{
var connected = await billing.ConnectAsync(ItemType.InAppPurchase);
if (!connected)
{
//we are offline or can't connect, don't try to purchase
return false;
}
//check purchases
var purchase = await billing.PurchaseAsync(productId, ItemType.InAppPurchase, payload);
//possibility that a null came through.
if(purchase == null)
{
//did not purchase
}
else if(purchase.State == PurchaseState.Purchased)
{
//purchased!
}
}
catch (InAppBillingPurchaseException purchaseEx)
{
//Billing Exception handle this based on the type
Debug.WriteLine("Error: " + purchaseEx);
}
catch (Exception ex)
{
//Something else has gone wrong, log it
Debug.WriteLine("Issue connecting: " + ex);
}
finally
{
await billing.DisconnectAsync();
}
I am trying to find out how to keep user logged in. I am using Facebook Plugin and storing the user data in CosmosDB, however i am not sure what data do i need to keep the user logged in or when to actually ask his permission as i am redirected straight to Facebook login.
This is the code that i am using to sign in
async Task LoginFacebookAsync(User user)
{
try
{
if (_facebookService.IsLoggedIn)
{
_facebookService.Logout();
}
EventHandler<FBEventArgs<string>> userDataDelegate = null;
userDataDelegate = async (object sender, FBEventArgs<string> e) =>
{
if (e == null) return;
switch (e.Status)
{
case FacebookActionStatus.Completed:
var facebookProfile = await Task.Run(() => JsonConvert.DeserializeObject<FacebookProfile>(e.Data));
var socialLoginData = new User
{
UserEmail = facebookProfile.Email,
UserName = $"{facebookProfile.FirstName} {facebookProfile.LastName}",
Id = facebookProfile.UserId,
};
user.UserEmail = socialLoginData.UserEmail;
user.UserName = socialLoginData.UserName;
user.Id = socialLoginData.Id;
user = await UserViewModel.GetOrCreateUser(user);
UserViewModel.SetUser(user);
await App.Current.SavePropertiesAsync();
App.Current.MainPage = new AppShell();
break;
case FacebookActionStatus.Canceled:
break;
}
_facebookService.OnUserData -= userDataDelegate;
};
_facebookService.OnUserData += userDataDelegate;
string[] fbRequestFields = { "email", "first_name", "gender", "last_name" };
string[] fbPermisions = { "email" };
await _facebookService.RequestUserDataAsync(fbRequestFields, fbPermisions);
}
catch (Exception ex)
{
Debug.WriteLine(ex.ToString());
}
}
Please if you could help how to approach this.
You may use local db like Sqlite or Settings Plugin to save the authentication token, expire date etc. So when the app run you can check them and let the user automatically login or not.