I am trying to implement Sign in with Apple using Firebase Authentication. I am following the firebase/quickstart-android sample.
My sign-in fragment overrides onStart() to check for any pending results:
override fun onStart() {
super.onStart()
val pending = auth.pendingAuthResult
pending?.addOnSuccessListener { authResult ->
Timber.d("Successful login, pending")
}?.addOnFailureListener { e ->
Timber.d("Failed login, pending")
}
}
And a button that initiates the sign-in flow:
btnApple.onClick {
viewModel.appleLogin(requireActivity())
}
The viewModel calls the following method from a repository:
// Initiate sign-in flow only if there are no pending results
if (auth.pendingAuthResult != null) {
return
}
val scopes = listOf("email", "name")
val provider = OAuthProvider.newBuilder("apple.com", auth)
.setScopes(scopes)
.build()
auth.startActivityForSignInWithProvider(activity, provider)
.addOnSuccessListener { authResult ->
Timber.d("Successful login, normal")
}
.addOnFailureListener { e ->
Timber.e(e, "Failed login, normal")
}
The official manual states:
Signing in with this method puts your Activity in the background, which means that it can be reclaimed by the system during the sign in flow.
So I started testing the pending result by terminating the app in Android Studio while completing the sign-in flow in Chrome. Once I returned back to the app, the onStart() was called, but the pendingAuthResult was always null.
To make it more interesting, when I restart the app, I am logged in. Then if I log out and enter the sign-in fragment again, there is a pending result now and I receive Successful login, pending. On top of that, the pending result does not disappear. If I leave the sign-in fragment and go back, the pending result is still there and I receive yet another Successful login, pending.
I even tested the firebase/quickstart-android sample itself and it has exactly the same issue.
What could be the possible cause of this issue? I am using firebase-auth:19.2.0.
if you are using the firebase for signing with apple and if it's handled manually then you should use a string decoding mechanism, Please find the firebase guide for the same and hope it'll works. let me know if any issue!
Firebase guild signing with apple
Related
I'm trying to implement In-app messaging to display a snackbar if a subscription has had it's payment declined.
Following the documentation here and adding billingClient.showInAppMessages doesn't seem to work. I subscribe using the Test card, always approves and change it to Test card, always declines and wait for the payment to be put in grace period, but the snackbar from the documentation does not show up even after restarting the application.
Expected result after payment has been declined and app was restarted:
In-app messaging works as I can send messages via firebase, but I am unsure if I'm missing something obvious here?
Implementation:
(This is called on app start)
// onCreate
billingClient = createBillingClient()
setupInAppMessaging(activity)
if (!billingClient.isReady) {
logD { "BillingClient: Start connection..." }
billingClient.startConnection(this)
}
fun createBillingClient() = BillingClient.newBuilder(context)
.setListener(this)
.enablePendingPurchases()
.build()
fun setupInAppMessaging(activity: Activity) {
val inAppMessageParams = InAppMessageParams.newBuilder()
.addInAppMessageCategoryToShow(InAppMessageParams.InAppMessageCategoryId.TRANSACTIONAL)
.build()
billingClient.showInAppMessages(activity, inAppMessageParams) { inAppMessageResult ->
if (inAppMessageResult.responseCode == InAppMessageResult.InAppMessageResponseCode.NO_ACTION_NEEDED) {
// The flow has finished and there is no action needed from developers.
logD { "SUBTEST: NO_ACTION_NEEDED"}
} else if (inAppMessageResult.responseCode == InAppMessageResult.InAppMessageResponseCode.SUBSCRIPTION_STATUS_UPDATED) {
logD { "SUBTEST: SUBSCRIPTION_STATUS_UPDATED"}
// The subscription status changed. For example, a subscription
// has been recovered from a suspend state. Developers should
// expect the purchase token to be returned with this response
// code and use the purchase token with the Google Play
// Developer API.
}
}
}
Has it worked at all? I believe these messages are only shown once per day so if you already saw it once, you will have to wait another 24 hours to see it again if your payment method is still failing.
I am trying to implement in-app updates in my android app following the official documentation.
I launched one version of my app on Play Store using internal testing track followed by another version with incremented versionCode.
When I opened the app first time it crashed with the following exception:
Fatal Exception: com.google.android.play.core.install.InstallException: Install Error(-10): The app is not owned by any user on this device. An app is "owned" if it has been acquired from Play. (https://developer.android.com/reference/com/google/android/play/core/install/model/InstallErrorCode#ERROR_APP_NOT_OWNED)
at com.google.android.play.core.appupdate.o.a(o.java:6)
at com.google.android.play.core.internal.o.a(o.java:28)
at com.google.android.play.core.internal.j.onTransact(j.java:20)
at android.os.Binder.execTransactInternal(Binder.java:1166)
at android.os.Binder.execTransact(Binder.java:1130)
But when I reopened the app, the update flow started properly. So maybe the PlayCore library wasn't able to fetch the right data first time and it threw the InstallException.
What I want is to catch all such InstallExceptions but I am not able to find where exactly to put the try-catch block. Which function of AppUpdateManager throws this InstallException? Is it the startUpdateFlow() method?
My code:
private lateinit var updateInfo: AppUpdateInfo
suspend fun checkForUpdate() {
updateInfo = appUpdateManager.requestAppUpdateInfo() // suspend function from play-core-ktx
if(updateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE && updateInfo.isImmediateUpdateAllowed) {
startImmediateUpdate(activity)
}
}
fun startImmediateUpdate(activity: Activity) {
appUpdateManager.startUpdateFlow( // Here I am using startUpdateFlow and not startUpdateFlowForResult
updateInfo, activity, AppUpdateOptions.defaultOptions(AppUpdateType.IMMEDIATE)
).addOnSuccessListener { result ->
if (result == Activity.RESULT_CANCELED) {
activity.finish()
}
}
}
I'm new with Xamarin form, my question is how can I check user is logged in for entire app, example every time when users go to a new page, it checks for authent. I tried successfully for every page check for auth but is there any another ways to do it? I did some research on internet and some said that i have to auth on the App.cs on OnStart() but the event is not raise when i go to the next page, it starts only when user open the app.
Here is my code, I'm using Google auth.
On the logged in page (HomePage):
public HomePage(NetworkAuthData networkAuthData)
{
if (string.IsNullOrEmpty(networkAuthData.Id))
{
//Always require user authentication
//Application.Current.MainPage.Navigation.PopAsync();
Application.Current.MainPage = new NavigationPage(new SocialLoginPage(oAuth2Service));
}
else
{
BindingContext = networkAuthData;
InitializeComponent();
}
}
It works but when i move these code to app.cs on OnStart() it just run once when opened the app.
I have a singleton in my Android app, started at startup of the app, that listens to auth state changes like so:
fun listenForAuthChanges() {
if (authStateListener != null) {
FirebaseAuth.getInstance().removeAuthStateListener(authStateListener!!)
}
authStateListener = FirebaseAuth.AuthStateListener { auth ->
val user = auth.currentUser
writeStatusToFirebase()
if (user != null) {
Log.debug("User : ${user.uid} -> ${user.loginType}")
} else {
Log.debug("User : signed out")
loginAnonymously()
}
}
FirebaseAuth.getInstance().addAuthStateListener(authStateListener!!)
}
This works perfectly, detecting if a use is logged out, or logged in. As you can see in the code above using the loginAnonymously(), when there is no user logged-in then I automatically login anonymously. This al works like a charm, however.... when I call the Firebase-UI to login and the user logs in via Facebook the Auth state listener is not called.
I figured out that FirebaseUI actually does not create a new user, instead the anonymous user is upgraded to Facebook user (checked in the Firebase Auth console and by using breakpoints in the Android studio console). This is actually the behaviour that I want.
So I guess, the conclusion is that the Auth state listener is not called because the user's uid does not change?
However, I do need a reliable way to detect also this event (meaning user upgraded from anonymous to e.g. Facebook).
What would be the best way to do this?
Note: I know its possible to detect what auth provider (id) a user is using to authenticate.
Here is a bit of code I use for this:
val FirebaseUser.loginType: LoginType
get() {
if (isAnonymous) {
return LoginType.Anonymous
}
loop# for (userInfo in providerData) {
when (userInfo.providerId) {
EmailAuthProvider.PROVIDER_ID -> return LoginType.Email
GoogleAuthProvider.PROVIDER_ID -> return LoginType.Google
FacebookAuthProvider.PROVIDER_ID -> return LoginType.Facebook
else -> continue#loop
}
}
return LoginType.Unknown
}
That is not my issue, my questions is: how to detect that a user has been upgraded from anonymous to e.g. Facebook?
how to detect that a user has been upgraded from anonymous to e.g. Facebook?
When an anonymous user signs in for the first time, save the authentication type in the database:
Firestore-root
|
--- users (collection)
|
--- uid
|
--- type: "anonymous"
When a user is changing the authentication type, you should simply change the type field in the database to hold "Facebook" instead of "anonymous". This is possible because the uid will always be the same, no matter what the provider id. To be notified when the operation takes place, simply attach a listener on the type property and you'll be notified in real-time.
I am using Firebase 12.0.1. Somewhat occasionally, but not too occasionally, one in 500-1000 users, my app experiences NPE while executing FirebaseAuth.getInstance().getCurrentUser. I have attached a screenshot of the crash report at the bottom.
My setupPage1 shown in the stack trace, is executing this line:
val provdata = FirebaseAuth.getInstance().currentUser?.providerData
Is there some precaution I need to take before getting the user? The user is logged in to FA via the following before the above code runs
this.authMgr.signInWithCredential(credential).addOnCompleteListener(this) { task: Task<AuthResult> ->
if (task.isSuccessful) {
val user = task.result.user
user?.getIdToken(false)?.addOnCompleteListener { task: Task<GetTokenResult> ->
if (task.isSuccessful) {
...stuff is done in here that leads to getCurrentUser
It's hard to be certain without an MCVE, but my first guess is that this might be while Firebase is still determining if the user is signed in. You should always check if there is a current user:
val user = FirebaseAuth.getInstance().currentUser
if (user != null) {
val provdata = user!.providerData
...
}
According to the support response I received, this was a known issue and was fixed in v15.0.1. I have not upgraded yet to verify.