I have an android application which uses Firebase Authentication via Facebook. A user can delete their account in the application using the following function:
override fun deleteUserAcc() {
val user = FirebaseAuth.getInstance().currentUser
val userToken = FacebookAuthProvider.getCredential(authTokenProvider.provideToken())
user?.reauthenticate(userToken)?.addOnCompleteListener { task ->
user.delete()
}
}
After this a user really gets deleted on the Firebase servers. However when they try to access the application again and log in one more time, they are not able to do this (their account with uid had been deleted and somehow they are not assigned a new one uid).
The login function and the onSuccess callback are both implemented and called.
override fun login(): Completable {
LoginManager.getInstance().logInWithReadPermissions(
activityReference.get(),
listOf("public_profile", "user_birthday", "user_location")
)
return CompletableSubject.create().apply {
loginSubject = this
}
}
override fun onSuccess(result: LoginResult) {
val credential = FacebookAuthProvider.getCredential(result.accessToken.token)
authTokenProvider.saveToken(result.accessToken.token)
firebaseAuth.signInWithCredential(credential)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
getInfoOnFirestore(loginSubject)
} else {
loginSubject.onError(task.exception!!)
}
}
}
What can possibly be the cause of the following issue?
A little late to the party but I know what the issue is. Firebase deletes only the authentication, which means that the real-time database is still there with the same uid. In order to delete the database entry as well, you need to upgrade to the blaze program and add the extension.
Related
hi guys i'm trying to do auto login in my app but before login done i wonder if the user verified his email or no.
the problem : even if i verified my account the code doesn't see this and said false.
and here is my code.
class SignInActivity : BaseActivity<SignInViewModel, ActivitySignInBinding>(), Navigator {
private lateinit var preferenceManger: PreferenceManger
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
preferenceManger = PreferenceManger(applicationContext)
autoLogin()
binding.vm = viewModel
viewModel.navigator = this
addPrefManger()
}
private fun autoLogin() {
DataUtils.firebaseUser = Firebase.auth.currentUser
if (preferenceManger.getBoolean(Constants.KEY_IS_SIGNED_IN)) {
when {
DataUtils.firebaseUser!!.isEmailVerified -> {
startActivity(Intent(this, HomeActivity::class.java))
finish()
}
else -> {
startActivity(Intent(this, VerificationActivity::class.java))
finish()
}
}
}
}
this line is always false even if i verified my account.
DataUtils.firebaseUser!!.isEmailVerified
While the verification status of the user profile is updated on the server as soon as they've clicked the link, it may take up to an hour before that information is synchronized to the Android app.
If you want to detect the email verification in the app before it is automatically synchronized, you can:
Sign the user out and in again.
Force reloading of the user profile (after the user has clicked the link) by calling reload on the user object. You can put a button in your UI to do this, or automatically call that, for example in the onResume of the activity.
Also see:
How to verify email without the need of signing in again while using FirebaseUI-auth?
Verification email activity not refreshing
I've been struggling with this problem for more than a week help me out if you know the fix.
enter image description here
I am trying to delete the data from a child node in the Realtime Firebase but it keeps regenerating even when I delete the token data.
This is my code:
when a user logs in an FCM token is generated automatically (onCreate).
when I try to log him out of his account I want the token to be deleted from the token list but it keeps regenerating even when I logout
this is the User fragment is redirected to after login:
val currentUser : String = firebaseAuth.currentUser?.uid.toString()
val databaseReference = FirebaseDatabase.getInstance("https://trial-38785-default-rtdb.firebaseio.com/")
.getReference("AppUsers").child("Doctor").child(currentUser)
FirebaseMessaging.getInstance().token.addOnCompleteListener {
if (it.isComplete) {
val firebaseToken = it.result.toString()
val myRef =
FirebaseDatabase.getInstance("https://trial-38785-default-rtdb.firebaseio.com/")
.getReference("AppUsers").child("Doctor").child(currentUser)
myRef.child("Token").orderByChild("token").equalTo(firebaseToken)
.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val token : Token = Token(firebaseToken)
if (snapshot.exists()) {
return
} else {
databaseReference.child("Token")
.child(currentdate.toString()).setValue(token)
}
}
override fun onCancelled(error: DatabaseError) {
Toast.makeText(context, error.message, Toast.LENGTH_LONG).show()
}
})
}
}.addOnFailureListener {
Toast.makeText(context, it.message, Toast.LENGTH_LONG).show()
}
This is another fragment where the doctor logout.
pressing logout button this is the code i ran
val delRef = FirebaseDatabase.getInstance().getReference("AppUsers")
.child("Doctor").child(currentId.toString())
.child("Token").child(key.toString()).removeValue()
delRef.addOnSuccessListener {
println("Removed.")
}.addOnFailureListener {
println("Not Removed.")
}
When using the Query#addValueEventListener() method, it means that you are trying to listen for real-time updates. That being said, every change that takes place in the database and corresponds to your query, always triggers your onDataChange() method. Since when you are logging out, you remove a value from the query you are listening to, your token gets written again, hence that behavior. This is happening over and over again.
To solve this, simply change the above method call to Query#addListenerForSingleValueEvent(). This means that it listen for changes only once.
I finally figured out what I was doing wrong, I just had to move my code (myRef) outside of FirebaseMessaging.
I have an application where i register the user using phone number authentication , everything works fine but as the user is done verifying the phone number , i would like to push some credentials of the user to
realtime datbase , but the issue is that i'm not able to do so ... stuck basically , if any one could i'll appreciate it
This is my code
private fun signInWithAuthPhoneCredentials(credentials: PhoneAuthCredential) {
FirebaseAuth.getInstance().signInWithCredential(credentials)
.addOnCompleteListener {
if(it.isSuccessful){
// so basically if authentication is successful , i want to create a path to save user
// credentials which dosn't exist in the first place , upon checking if snapshot dosn't
// exist , i want to get user details and push them into database
// even with debugging , it gets to the last line of ( valueEventlister) and it stops
// there , it dson't even get to check wether snapshot exists or not
val currentUser = FirebaseAuth.getInstance().currentUser
if(currentUser != null){
val databaseReference = FirebaseDatabase.getInstance().reference.child("users").child(currentUser.uid)
databaseReference.addListenerForSingleValueEvent(object : ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
if(!snapshot.exists()){
val contactInfoMap = hashMapOf<String,Any>()
contactInfoMap["name"] = currentUser.displayName!!
contactInfoMap["phone"] = currentUser.phoneNumber!!
databaseReference.updateChildren(contactInfoMap)
}
userIsLoggedIn()
}
override fun onCancelled(error: DatabaseError) {
}
})
}
}
}
.addOnFailureListener {
Log.d("ERROR TAG","Error ${it.message}")
}
}
I am building a client application which uses Firebase for two things:
User Authentication
Using a realtime database
I have managed to set up everything correctly on my client and on my backend server (using Firebase's Admin SDK) and am able to correctly authenticate users and allow them to read/write to the database.
I am also using Retrofit2 to send requests from the client to the backend.
As part of allowing users access to the database, it is needed to send the user's token to the backend so the user can be verified.
To do this, I have the following logic:
val user = FirebaseAuth.getInstance().currentUser
if (user != null) {
user.getIdToken(false).addOnCompleteListener {
if (it.isSuccessful) {
val token = it.result?.token
//retrofit logic to send request happens from here
}
}
As you can see, getting the Id token of the user is an asynchronous call and in the current code base that I have, I have this code block for each one of my calls to the backend (duplication).
I want to know how I can export this snippet to a function (maybe a suspend method?) so that it can be reused for every call to the backend
I have searched online and have seen many SO questions, but none that fit this scenario.
I have thought about passing in a callback, but I have several methods that communicate to the backend, and each of them will require a different callback method.
The solution I am looking for looks something like this:
fun fetchDataFromDB() {
getIdTokenForUser()
//wait till it finishes and then
//perform request to DB
}
fun updateDataInDB() {
getIdTokenForUser()
//wait till it finishes and then
//perform request to DB
}
//......
I have tried reading about and implementing coroutines, but I lack the knowledge to do so correctly.
EDIT
Thanks to #Doug Stevenson for his answer and direction, I have managed to construct the following:
private suspend fun getUserIdToken(user: FirebaseUser) = coroutineScope {
val job = async {
user.getIdToken(false).result?.token
}
job.await()
}
And I use it in this fashion:
fun updateDB(context: Context) = runBlocking {
val user = FirebaseAuth.getInstance().currentUser
if (user != null) {
val token = getUserIdToken(user)
}
}
Is this the correct approach? Since the answers given below present a different implementation.
getIdToken is asynchronous returns a Task object. If you want to use a Task object in a Kotlin coroutine, you can use the library kotlinx-coroutines-play-services to add an extension method await() to the Task that makes it usable in a coroutine. With that, you can write something like this:
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.3.9"
import kotlinx.coroutines.tasks.await
suspend fun getIdTokenForUser(user: FirebaseUser): GetTokenResult {
return try {
user.getIdToken(false).await()
}
catch (e: Exception) {
// handle error
}
}
You might have to update the types here - I didn't try to compile or test this.
See also:
Android kotlin task to be executed using coroutines
Coroutines And Firebase: How to Implement Javascript-like Promise.all()
Using Firebase with Kotlin coroutines
In order to go from a callback based API like the following one:
val myCallback = object : ServiceCallback() {
override fun onResult(theobject: Something) {
// your callback code here
}
override fun onFailure(ex: Throwable) {
// error handling
}
}
theService.enqueue(callback)
You can use suspendCoroutine
What it does is that it suspends execution until the continuation is satified by the callback. So you can write a KTX like the following:
suspend fun Service.getSomething(): Something = suspendCoroutine{ cont ->
val callback = object : ServiceCallback(){
override fun onSuccess(data: Something): Unit = cont.resume(data)
override fun onFailure(ex: Throwable): Unit = cont.resume(ex)
}
this.enqueue(callback)
}
I am using Amazon Cognito for authentication and I want to ask that my access token and id token get expired like in one hour, moreover I have gone through many answers they have told to use refresh token as they have a validity of 10 years,
so my question is how to use refresh token in android?
Right now after login, I am setting CognitoCachingCredentialProvider's login map to the token - and I am using both Facebook and email login.
val authenticationHandler = object : AuthenticationHandler {
override fun onSuccess(userSession: CognitoUserSession?, newDevice: CognitoDevice?) {
//After Authentication User Cognito Access Id and Access Secret Extraction
currentSession = userSession!!
//Getting Session Token
val id = currentSession.idToken.jwtToken
//Credential Provider
val cognitoCachingCredentialsProvider = CognitoCachingCredentialsProvider(this#LoginActivity,resources.getString(R.string.cognito_identity_pool_id),Regions.myRegion)
cognitoCachingCredentialsProvider.clear()
//Login Map
val login = HashMap<String,String>()
login["myString"] = id
cognitoCachingCredentialsProvider.logins = login
//Off the main thread
SimpleAsyncTask(this#LoginActivity,cognitoCachingCredentialsProvider).execute()
}
override fun authenticationChallenge(continuation: ChallengeContinuation?) {
continuation?.continueTask()
}
override fun getAuthenticationDetails(authenticationContinuation: AuthenticationContinuation, userId: String) {
// The API needs user sign-in credentials to continue
Log.d(TAG, "userId is : $userId")
val authenticationDetails = AuthenticationDetails(userId, password, null)
authenticationDetails.authenticationType = "USER_PASSWORD"
// Pass the user sign-in credentials to the continuation
authenticationContinuation.setAuthenticationDetails(authenticationDetails)
// Allow the sign-in to continue
authenticationContinuation.continueTask()
}
override fun getMFACode(multiFactorAuthenticationContinuation: MultiFactorAuthenticationContinuation) {
// Multi-factor authentication is required; get the verification code from user
multiFactorAuthenticationContinuation.setMfaCode(null)
// Allow the sign-in process to continue
multiFactorAuthenticationContinuation.continueTask()
}
override fun onFailure(exception: Exception) {
// Sign-in failed, check exception for the cause
Log.e(TAG, "${exception.message}")
}
}
// Sign in the user
user.getSessionInBackground(authenticationHandler)
}
internal class SimpleAsyncTask(private val activity: Activity,private val credential:CognitoCachingCredentialsProvider) :
AsyncTask<Void, Void, Void>() {
override fun doInBackground(vararg p0: Void?):Void ?{
credential.refresh()
credential.setPersistenceEnabled(true)
return null
}
Similar kind of code is also used for facebook login like this
FacebookCallback<LoginResult> {
override fun onSuccess(loginResult: LoginResult) {
//Getting access Token
val accessToken = loginResult.accessToken.token
//Credentials Extraction
val credentials = CognitoCachingCredentialsProvider(this#LoginActivity,resources.getString(R.string.cognito_identity_pool_id),Regions.myRegion)
credentials.clear()
//Map of login
val login = HashMap<String,String>()
login["graph.facebook.com"] = accessToken
//Setting the value of map
credentials.logins = login
//Off the main thread
SimpleAsyncTask(this#LoginActivity,credentials).execute()
}
override fun onCancel() {
//Cancel code
Toast.makeText(this#LoginActivity,"Canceled",Toast.LENGTH_SHORT).show()
}
override fun onError(exception: FacebookException) {
//Error code
Toast.makeText(this#LoginActivity,exception.toString(),Toast.LENGTH_SHORT).show()
}
})
Now I am using this to check user status of login, I check the condition cognitoCachingCredentialProvider.cachedId!=null for checking user login.
But it gets logged in for like an hour how to get user logged in for a long long time
Refresh token is distinctly different from id or access token. You can use refresh token to get fresh access and id tokens (as the name suggests). When you call getSession it should automatically refresh your tokens if they have expired AND if your refresh token hasn't expired.
More information: https://stackoverflow.com/a/39480690/6941447
Okay, so as Ninad said we have to use getSession for refreshing credentials you have to just add this check.
if(credentialsProvider.cachedIdentityId==null)
{
userPool.currentUser.getSessionInBackground(AuthenticationHandler)
}
Make an authentication Handler seperate for this and your tokens are refreshed.