Kotlin: Firebase Password Authentication - android

Apologies in advance for the rookie question - I've been trying at this query for a while now and turn to Stack Overflow with the hope of resolving this small issue.
The Problem
In my case, it seems that the Firebase method to sign the user in through email (signInWithEmailAndPassword) doesn't seem to work.
My current setup is this:
AuthFragment, a Fragment which calls methods from its parent Activity, AuthActivity.
AuthActivity, an Activity which contains several methods/self-defined functions:
validate(), which checks if the email and password inputs are valid
signIn(), which uses Firebase's signInWithEmailAndPassword method to sign the user in.
The following are snippets of my code:
AuthActivity.kt
package com.example.myApplication
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.ktx.auth
import com.google.firebase.ktx.Firebase
class AuthActivity : AppCompatActivity() {
private lateinit var auth: FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_auth)
}
fun validate(email: String?, password: String?): Boolean {
return ((email != null) && (password != null)) && (password.length >= 5) && (email.contains("#"))
}
fun signIn(email: String, password: String) {
auth = Firebase.auth
Log.d("Auth", "Attempting to authenticate...")
auth.signInWithEmailAndPassword(email, password).addOnCompleteListener(this) { task ->
Log.d("Auth", "Result supposedly received.")
if (task.isSuccessful) {
Log.d("Auth", "User authentication successful.")
Toast.makeText(this, task.result.toString(), Toast.LENGTH_LONG).show()
} else {
Log.d("Auth", "User authentication failed.")
Toast.makeText(this, "User authentication failed. Please try again.", Toast.LENGTH_LONG).show()
}
}
}
}
AuthFragment.kt
package com.example.myApplication
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import android.widget.Toast
class AuthFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val root = inflater.inflate(R.layout.fragment_auth, container, false)
val emailField = root.findViewById<TextView>(R.id.auth_emailField)
val passwordField = root.findViewById<TextView>(R.id.auth_passwordField)
val signInButton = root.findViewById<Button>(R.id.auth_signInButton)
signInButton.setOnClickListener {
val authActivity = AuthActivity()
val email = emailField.text.toString()
val password = passwordField.text.toString()
val valid = authActivity.validate(email, password)
Log.d("Auth", valid.toString())
if (valid) {
authActivity.signIn(email, password)
} else {
Toast.makeText(activity, "Some fields are not properly filled. Please try again.", Toast.LENGTH_LONG).show()
}
}
return root
}
}
Expectations
Taking a look at the code in AuthActivity, I would have expected one of the following results to appear in the Logcat:
"User authentication successful."
"User authentication failed."
However, I only had this as a response in the Logcat:
I/BiChannelGoogleApi: [FirebaseAuth: ] getGoogleApiForMethod() returned Gms: com.google.firebase.auth.api.internal.zzaq#60b999e
All help is appreciated, thanks!

I lost a few hours on this one, so let me share my experience on this.
Gms stands for "Google Mobile Services", and its basically an addon API to Android developed by Google that allows to connect to a bunch of Google-provided services. If you use flutter, this is probably wrapped by flutter packages you use such as firebase_auth, flutter_facebook_login or/and google_sign_in.
This error may sometimes be handled by the higher level components, so if you have that error, it doesn't mean your configuration is wrong or it won't work. In my case I had this error with successful end result when I was logging using Google Sign in, something like this:
info flutter.tools W/BiChannelGoogleApi( 4106): [FirebaseAuth: ] getGoogleApiForMethod() returned Gms: com.google.firebase.auth.api.internal.zzak#8116b2c
info flutter.tools D/FirebaseAuth( 4506): Notifying id token listeners about user ( lCZZZZJELWhGUZZZZB3vDklZZZZ2 ).
info flutter.tools D/FirebaseAuth( 4506): Notifying auth state listeners about user ( lCZZZZJELWhGUZZZZB3vDklZZZZ2 ).
info flutter.tools I/flutter ( 4506): FirebaseUser({uid: lCZZZZJELWhGUZZZZB3vDklZZZZ2, photoUrl: https://lh5.googleusercontent.com/-ZZzir_P-ENw/AAAAAAAAAAI/AAAAAAAAAAA/PpxhiXg_ISk/s96-c/photo.jpg, isAnonymous: false, etc....
I have a dart call print(fireUser); in my code so we see it's a success.
But in the case of Facebook Auth, I just had this, and nothing happened after that, but on the Facebook server side, login was succeeding as I could see events in Facebook dev console.
info flutter.tools W/BiChannelGoogleApi( 4106): [FirebaseAuth: ] getGoogleApiForMethod() returned Gms: com.google.firebase.auth.api.internal.zzak#8116b2c
And nothing else after in the log, so something was stuck in the process.
My problem was I mixed two applications App Id and Secret. I'm a bonehead sometimes, but the thing is there's zero warning or specific error nowhere, so here are a list of things that can fail with Firebase and other plugins:
Make sure you have properly configured the sign-in provider (google, facebook, etc.) in Firebase console
Upgrade all flutter packages. I personnaly don't use package versions, I use all flutter packages latest version. And I use AndroidX.
In the specific case of Facebook Auth (I find Google Sign In much easier to integrate):
make sure your looking at the good app
make sure the App ID and App secret are the good one. In Facebook dev console, check the Basic Settings there.
make sure you have added the "Facebook Login" product. If you don't see it in the left menu, in Facebook dev console, check the Dashboard and add it.
in the "Facebook Login" product settings, make sure you have added the "Valid OAuth Redirect URIs" that Firebase console gives you (in the Facebook Sign-in provider config). You can check it in the "Redirect URI validator" tool lower in that page.
in the Basic Settings of the app, if you use flutter (=> mobile platforms), make sure you have an IOS section/platform and and Android section/platform. To achive that you can do it manually "Add platform", or use the Quick Starts.
in the end, the IOS section can contain only the "Bundle ID"
and the Android section should containe the "Google Play Package Name", the "Class Name" and at least one "Key Hashes" (for debug). If you don't know how to create the hash, use the "Facebook Login" quick start.
Hope this helps

Related

Handling FirebaseAuthUserCollisionException when using FirebaseUI

I am currently using Firebase-UI for Android to implement authentication flow in my app. I currently have Google, Facebook, and Email auth providers enabled. My Android app is built using Jetpack Compose and I'm using rememberLauncherForActivityResult to launch the login intent. Everything is working as expected with the normal flow.
However, when I try to use my Facebook login with the same email that I have previously authenticated using Gmail, I am getting the below error.
A sign-in error occurred.
com.google.firebase.auth.FirebaseAuthUserCollisionException: This credential is already associated with a different user account.**
at com.google.android.gms.internal.firebase-auth-api.zzxc.zzb(com.google.firebase:firebase-auth##21.1.0:4)
at com.google.android.gms.internal.firebase-auth-api.zzya.zza(com.google.firebase:firebase-auth##21.1.0:7)
at com.google.android.gms.internal.firebase-auth-api.zzyb.zzl(com.google.firebase:firebase-auth##21.1.0:1)
at com.google.android.gms.internal.firebase-auth-api.zzxy.zzq(com.google.firebase:firebase-auth##21.1.0:3)
at com.google.android.gms.internal.firebase-auth-api.zzxy.zze(com.google.firebase:firebase-auth##21.1.0:1)
at com.google.android.gms.internal.firebase-auth-api.zzxa.zze(com.google.firebase:firebase-auth##21.1.0:1)
at com.google.android.gms.internal.firebase-auth-api.zzvf.zzd(com.google.firebase:firebase-auth##21.1.0:8)
at com.google.android.gms.internal.firebase-auth-api.zzuf.zzb(com.google.firebase:firebase-auth##21.1.0:2)
at com.google.android.gms.internal.firebase-auth-api.zzyj.zzb(com.google.firebase:firebase-auth##21.1.0:12)
at com.google.android.gms.internal.firebase-auth-api.zzyj.zza(com.google.firebase:firebase-auth##21.1.0:14)
at com.google.android.gms.internal.firebase-auth-api.zzxp.zzq(com.google.firebase:firebase-auth##21.1.0:4)
at com.google.android.gms.internal.firebase-auth-api.zzug.zzb(com.google.firebase:firebase-auth##21.1.0:4)
at com.google.android.gms.internal.firebase-auth-api.zzvf.zzM(com.google.firebase:firebase-auth##21.1.0:5)
at com.google.android.gms.internal.firebase-auth-api.zzvf.zzs(com.google.firebase:firebase-auth##21.1.0:4)
at com.google.android.gms.internal.firebase-auth-api.zzxb.zzm(com.google.firebase:firebase-auth##21.1.0:6)
at com.google.android.gms.internal.firebase-auth-api.zzvr.zzc(com.google.firebase:firebase-auth##21.1.0:1)
at com.google.android.gms.internal.firebase-auth-api.zzyc.run(com.google.firebase:firebase-auth##21.1.0:1)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637)
at java.lang.Thread.run(Thread.java:1012)
I would like someway to handle this exception but I am not able to find a way. Am I missing something obvious?
Here is my implementation
ProfileViewModel.kt
override fun buildLoginIntent(): Intent {
val authUILayout = AuthMethodPickerLayout.Builder(R.layout.auth_ui)
.setGoogleButtonId(R.id.btn_gmail)
.setEmailButtonId(R.id.btn_email)
.setFacebookButtonId(R.id.btn_facebook)
.build()
val googleScopes = arrayListOf(
"https://www.googleapis.com/auth/userinfo.profile",
"https://www.googleapis.com/auth/userinfo.email"
)
val intent = AuthUI.getInstance().createSignInIntentBuilder()
.setAvailableProviders(
listOf(
AuthUI.IdpConfig.EmailBuilder().build(),
AuthUI.IdpConfig.GoogleBuilder().setScopes(googleScopes).build(),
AuthUI.IdpConfig.FacebookBuilder().build()
)
)
.enableAnonymousUsersAutoUpgrade()
.setLogo(R.mipmap.ic_launcher)
.setAuthMethodPickerLayout(authUILayout)
.build()
return intent
}
#SuppressLint("RestrictedApi")
override fun onLoginResult(result: FirebaseAuthUIAuthenticationResult) {
// Handle result
}
ProfileUI.kt
Composable UI where I launch the intent
val loginLauncher = rememberLauncherForActivityResult(
profileViewModel.buildLoginActivityResult()
) { result ->
if (result != null) {
profileViewModel.onLoginResult(result = result)
}
}
if (isAnonymousUser) {
SignInUI() {
loginLauncher.launch(profileViewModel.buildLoginIntent())
}
}
However, when I try to use my Facebook login with the same email that I have previously authenticated using Gmail, I am getting the below error.
This credential is already associated with a different user account.
That's the expected behavior since your user was first time authenticated with Google. When you authenticate a user in Firebase with Google, there is a user created that contains the data that corresponds to that particular provider. If you try to use that data to authenticate to Facebook, the authentication will fail, because the user was already created with another provider, hence the error.
A solution for this kind of situation would be to check if the user already exists, before authenticating it. If the user already exists, then you have to read the provider and display a message to the user, so it can choose the right authentication provider.
Another option might be to allow the creation of different accounts for different providers. You can find this option which is called "Create multiple accounts for each identity provider" right inside the Firebase Console, in the Settings tab inside the Authentication.

Unable to connect to API on local xampp server from android app (via emulator)

I have a simple API written in PHP to interact with my MYSQL database. The API returns the correct response when I call it from the browser in the emulator but the request fails when its being done from the code.
package com.example.studenthealthapp
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.android.volley.Request
import com.android.volley.Response
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import com.github.kittinunf.fuel.Fuel
import com.github.kittinunf.result.Result
import org.json.JSONArray
class InfoActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
supportActionBar?.hide()
setContentView(R.layout.info_layout)
val api_url =
"http://10.0.2.2:80/HealthTracker/api.php?email=" + getIntent().getStringExtra("email")
//Toast.makeText(this,api_url,Toast.LENGTH_LONG).show()
Fuel.get(api_url).response { request, response, result ->
when (result) {
is Result.Success -> {
//var data = JSONArray(String(response.data))
//Toast.makeText(this,data.length().toString(),Toast.LENGTH_SHORT).show()
Toast.makeText(this, "Worked", Toast.LENGTH_SHORT).show()
}
is Result.Failure -> {
Toast.makeText(this, "Didnt work", Toast.LENGTH_SHORT).show()
}
}
}
}
}
I have tried both my IP address and 10.0.2.2 when calling the API all of them has the same result.
I may have run into this problem before and your solutions may lie in one of two options. I don't recommend the first as it sets a bad precedent for future projects.
The first would be to allow "http://..." without the "s", also called clear text traffic in your apps manifest. A quick search on youtube will get you some outdated tutorials and this is probably not a good sign.
My preferred solution is that you switch from Volley to Retrofit. It has better up-to-date tutorials both on youtube and in articles on the web and is much easier to use. It might seem like starting from scratch but you'll be saving yourself from a lot of future headaches.
Good luck!

Branch IO - Show referral parameters in custom event

In my app I am tracking app install referrals using Branch and I also have a custom event called SIGN_UP which gets sent after the sign up request completes.|
So after using a branch link (fb one in particular) to install and sign up in my app, even though I get normal campaign data in the INSTALL event, I do not seem to get any relevant data in my custom one:
Screenshot of dashboard webhook events when filtered by AAID (see how columns Campaign, Ad Partner 3P, Ad Partner, Channel, Feature are empty in the first row.)
Any reason why this happens?
Code used:
In my Application class:
#Override
public void onCreate() {
super.onCreate();
// ...
Branch.getAutoInstance(this);
// ...
}
In my launcher Activity:
private val callback = Branch.BranchReferralInitListener { referringParams, error ->
if (error == null) {
Timber.i("BRANCH SDK success: %s", referringParams.toString())
} else {
Timber.e("BRANCH SDK error: %s", error.message)
}
}
override fun onStart() {
super.onStart()
Branch.sessionBuilder(this).withCallback(callback).withData(this.intent?.data).init()
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
Branch.sessionBuilder(this).withCallback(callback).reInit()
}
When sending a SIGN_UP event:
Branch.getInstance().setIdentity(memberId)
BranchEvent("SIGN_UP")
.addCustomDataProperty("UDID", udid)
.logEvent(activity)
Branch version: 5.0.1
For Facebook as Ad Network, Branch has the following data limitations
We cannot send device-level Facebook attribution data to third parties.
We cannot send events attributed to Facebook via Data Integrations. Please instead consider analyzing this data in-house (using Webhooks, the Daily Export API, or CSV Exports), or using the Branch Dashboard for all of your analytics and attribution needs.
This data is also not returned in the deeplink session initialization callback within the app. Also, you must have signed Facebook's "Advanced Mobile Measurement" agreement ("Data Use Terms for Advanced Mobile App Measurement") to view this data.

com.google.android.gms.common.api.ApiException: 16:

I try to learn how i can use google sign in in my android App, but i catch com.google.android.gms.common.api.ApiException: 16
And i can't find on stackoveflow answer, what is it and why i catch it. In documentation i read, what it "was canceled by user", but my google account accepted to install apps
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.auth.api.signin.GoogleSignInAccount
import android.content.Intent
import com.google.android.gms.tasks.Task
import com.google.android.gms.common.api.ApiException
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val gso = GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestEmail()
.build()
val mGoogleSignInClient = GoogleSignIn.getClient(this, gso)
val account = GoogleSignIn.getLastSignedInAccount(this)
if(account != null){
Log.e("!!!", account.email)
} else {
val signInIntent = mGoogleSignInClient.signInIntent
startActivityForResult(signInIntent, 0)
}
}
public override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
// Result returned from launching the Intent from GoogleSignInClient.getSignInIntent(...);
if (requestCode == 0) {
// The Task returned from this call is always completed, no need to attach
// a listener.
val task = GoogleSignIn.getSignedInAccountFromIntent(data)
handleSignInResult(task)
}
}
private fun handleSignInResult(completedTask: Task<GoogleSignInAccount>) {
try {
val account = completedTask.getResult(ApiException::class.java)
// Signed in successfully, show authenticated UI.
Log.e("!!!", account.email)
} catch (e: ApiException) {
e.printStackTrace()
}
}
}
I followed this guide. Did the configuration of the project.
If it's matter, i use VDS for this. Account was created in the same place
Here is stackTrace:
com.google.android.gms.common.api.ApiException: 16:
at com.google.android.gms.common.internal.ApiExceptionUtil.fromStatus(Unknown Source)
at com.google.android.gms.auth.api.signin.GoogleSignIn.getSignedInAccountFromIntent(Unknown Source)
at foryou.friendly.alisa.alisa.MainActivity.onActivityResult(MainActivity.kt:47)
at android.app.Activity.dispatchActivityResult(Activity.java:7124)
at android.app.ActivityThread.deliverResults(ActivityThread.java:4173)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4220)
at android.app.ActivityThread.-wrap20(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1579)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:163)
at android.app.ActivityThread.main(ActivityThread.java:6228)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:904)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:794)
I had the same problem, start activity result kept coming back with RESULT_CANCELED and errorCode 16. The problem was my client configuration in Google Cloud Platform Console. I was using the regular debug and release api key. The result came back OK when I used web application as my Google Console configuration.
Hope it helps.
I am developing an Android application using Flutter, tried to integrate Google Sign In and faced the same problem with ApiException: 16 and SIGN_IN_FAILED (instead of RESULT_CANCELED).
Application type on Firebase was set to android.
In my case, after many hours of debugging, it turned out to be a wrong SHA-1 problem.
As soon as I extracted the SHA-1 key from my project and updated Firebase console, it worked.
Was having the same problem, turns out I did not set support mail on firebase project settings.
If this is the case, firebase will show you edit project settings when trying to enable Google sign in on Firebase Authentication. You can copy your client Id from firebase
For me the only thing that worked was to provide 2 oauth client ids. a web application client id AND an android client id
In my android app i use the client id and client secret of the web application. Even though i don't use the android client id anywhere in my app it is still required. ie if i delete the oauth android client in the google api console my app stops working EVEN THOUGH i dont use that client id ANYWHERE in my app.
this makes absolutely no sense to me! go figure . but so far this is the only thing that has worked.
un-freaking-believable.
Maybe a bit late to the party here but after more than 4 hours debugging I realized that:
1.- Add an Android client with your signing-certificates fingerprints under the OAuth client IDs list. This is mandatory.
2.- Add the Web application client ID in your code in case your need to get an id token
// ID and basic profile are included in DEFAULT_SIGN_IN
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken("YOUR_CLIENT_ID")
.requestEmail()
.build();
Hope it helps
I used a different oauth id key which i found from downloading the project settings and added the client 3 key in the project.

Error : "Forbidden" when doing GeocodeRequest in Here-API for android

When doing a Geocoderequest in the Here API for Android I get the result FORBIDDEN for any search string.
Here is a snippet of the code (Kotlin) :
class GeoListner : ResultListener <MutableList<Location>>
{
override fun onCompleted(p0: MutableList<Location>?, p1: ErrorCode?) {
Log.d(this.javaClass.toString(),"Result code of search is ${p1?.name}")
}
}
fab_search.setOnClickListener { View ->
var currentPos = GeoCoordinate(49.2849,-123.1252)
val listner : ResultListener<MutableList<Location>> = GeoListner()
val request = GeocodeRequest("Granville").setSearchArea(currentPos,5000)
if (request.execute(listner) != ErrorCode.NONE)
{
}
}
This search area and string is picked from the HERE-API documentation for Here. Also i notice that the GeocodeRequest is deprecated, but the result for GeocodeRequest2 is the same error.
Anyone ?
Regards
Trond
Summary: Please ensure that you set the APP_ID and APP_CODE in the manifest file correctly.
For the background: Developers using HERE SDK with their app are required to register for a set of HERE credentials and to specify these credentials (App_Id and App_Code) in their app's Android manifest XML file. Failure to do so results in blocked access to certain features and degradation in the quality of other services.
To obtain these credentials visit the developer portal at https://developer.here.com/plans and register for a license. Once your project is created, you can generate these credentials on your Project Details page. If you already have a plan, you can also retrieve these credentials from your Project Details page.
Note: Credentials are unique to your account and your application's package namespace. Avoid reusing credentials across multiple applications.

Categories

Resources