I implemented Facebook Login functionality in my android app. But there is a unwanted behaviour that appears.
loginButton.registerCallback(callbackManager,
object : FacebookCallback<LoginResult> {
override fun onSuccess(result: LoginResult?) {
Log.d("mytag", "onSuccess: ${result?.accessToken?.userId}")
}
override fun onCancel() {
}
override fun onError(error: FacebookException?) {
}
})
So when onSuccess is called, everything is okay, i get the userId. But internally this saves some user authentication data inside SharedPreferences. Which is not what i want, my intention is to get only userId and send it to my server and that is it. (I mean it is not actually my aim to use facebook login for app, i just want to get userProfile image and send it to server).
How can i achieve this result?
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'm trying to set up OTP verification so when the user enters their phone number, I send them a pin code, the onCodeSent() is called and I receive the code pin, but the problem is when onVerificationCompleted() is called, I would like to move to another activity where the user can enter the code pin to verify but it is not called at all and I don't understand why. Any help would be appreciated guys, thank you.
val auth = PhoneAuthOptions
.newBuilder(FirebaseAuth.getInstance())
.setPhoneNumber(phoneNumber)
.setTimeout(60L,TimeUnit.MILLISECONDS)
.setActivity(this)
.setCallbacks(object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
override fun onVerificationCompleted(p0: PhoneAuthCredential) {
// here i want to get the smscode and send it over the next activity to verify
// but this method is not called at all
Intent(this,ChangePasswordActivity::class.java).apply {
putExtra("code",p0.smsCode)
startActivity(this)
}
}
override fun onVerificationFailed(p0: FirebaseException) {
Timber.d("Firebase Exception ${p0.message}")
}
override fun onCodeSent(code: String, p1: PhoneAuthProvider.ForceResendingToken) {
super.onCodeSent(code, p1)
}
override fun onCodeAutoRetrievalTimeOut(p0: String) {
super.onCodeAutoRetrievalTimeOut(p0)
}
})
.build()
PhoneAuthProvider.verifyPhoneNumber(auth)
onVerificationCompleted() will only be called when the phone number has been verified without any input from the user. To do what you are trying, you should be sending your intent inside onCodeSent() instead.
Here is a rough flow of events (that are covered in detail in the documentation):
Obtain phone number from user
Call PhoneAuthProvider.verifyPhoneNumber(auth) (as you are already) to send the pin to the user
onCodeSent() is called, with the verification ID and a resending token.
Inside of onCodeSent(), create an intent to launch the "pin input screen" with the verification ID.
Get a pin from the user and then combine it with the verification ID by calling PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, userInput)
Use that credential to sign in the user using signInWithCredential(credential).
val auth = PhoneAuthOptions
.newBuilder(FirebaseAuth.getInstance())
.setPhoneNumber(phoneNumber)
.setTimeout(60L,TimeUnit.MILLISECONDS)
.setActivity(this)
.setCallbacks(object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
override fun onVerificationCompleted(credential: PhoneAuthCredential) {
// if here, phone number was verified automatically
mAuth.signInWithCredential(credential)
.addOnCompleteListener(/* ... */)
}
override fun onVerificationFailed(p0: FirebaseException) {
Timber.d("Firebase Exception ${p0.message}")
}
override fun onCodeSent(verificationId: String, resendToken: PhoneAuthProvider.ForceResendingToken) {
// if here, code was sent to phone number
// open pin input screen
Intent(this,ChangePasswordActivity::class.java).apply {
putExtra("verificationId",verificationId)
startActivity(this)
}
}
// we aren't using onCodeAutoRetrievalTimeOut, so it's omitted.
})
.build()
PhoneAuthProvider.verifyPhoneNumber(auth)
I was also facing this problem but when I added my project to google cloud console, enable api services(google cloud console) and enable phone verification(google cloud console)
then onVerificationCompleted is working fine.
https://console.cloud.google.com/home
Let me describe the application im trying to do using Kotlin, on Android Studio.
Splash Screen => Login Screen => Main App
Splash Screen: Just a photo
Login Screen: Provides different ways of logging (Google, Facebook, etc)
MainActivity: Allows you to log off, in that case, you must return to "Login Screen"
So far I have been working only with Facebook Login.
I managed to place the button, make it work, and get a proper uid. The trouble is that, when that button is clicked, it automatically switches to "Log Out". So, when I go back from my MainActivity to my Login Screen, instead of having the button to Login again, im having a "Log Out" button, when account is actually already logged out.
Is there a way to prevent this button from changing? I have been reading tons of documentation, but havent found anything useful.
Is my idea incorrect? Or is there a better way to do this?
Note that everytime I Leave the LoginScreen, I place a finish(). The reason of this was to try to reset the activity, and make it work as if the program was running from scratch.
Variables defined on LoginScreen
private var mCallBackManager : CallbackManager?= null
private var mFirebaseAuth : FirebaseAuth?= null
On create function
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.lay_login_screen)
//-----------Inicializadores-Facebook---------------
mFirebaseAuth = FirebaseAuth.getInstance()
FacebookSdk.sdkInitialize(getApplicationContext())
mCallBackManager = CallbackManager.Factory.create()
//--------------------------------------------------
//-----------Boton-Facebook-------------------------
Button_LoginScreen_LoginFace.setOnClickListener()
{
iniciarSesionFacebook();
}
//--------------------------------------------------
}
private fun iniciarSesionFacebook()
{
Button_LoginScreen_LoginFace.registerCallback(mCallBackManager, object : FacebookCallback<LoginResult>
{
override fun onSuccess(result: LoginResult?)
{
d(getString(R.string.TAG_FacebookLogin),"Login Correct")
handleFacebookToken(result!!.accessToken)
}
override fun onCancel()
{
d(getString(R.string.TAG_FacebookLogin),"Login cancelled")
}
override fun onError(error: FacebookException?)
{
d(getString(R.string.TAG_FacebookLogin),"Login Error")
}
})
}
private fun handleFacebookToken(accessToken: AccessToken?)
{
val credential = FacebookAuthProvider.getCredential(accessToken!!.token)
mFirebaseAuth!!.signInWithCredential(credential).addOnFailureListener()
{error->
d(getString(R.string.TAG_FacebookLogin),"Error 1"+error.message)
}
.addOnSuccessListener { resultado->
startActivity(Intent(this, MainActivity::class.java))
finish()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?)
{
super.onActivityResult(requestCode, resultCode, data)
mCallBackManager!!.onActivityResult(requestCode,resultCode,data)
}
Finally, this is how I "Log Out" from MainActivity, and return to LoginScreen
class MainActivity : AppCompatActivity()
{
//----------Variables-Globales----------------
private var mFirebaseAuth : FirebaseAuth?= null
//--------------------------------------------
override fun onCreate(savedInstanceState: Bundle?)
{
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//-------Inicializo-Variables------------------------
mFirebaseAuth = FirebaseAuth.getInstance()
//---------------------------------------------------
Button_MainActity_LogOf.setOnClickListener{
val firebaseaux=mFirebaseAuth
firebaseaux?.signOut()
startActivity(Intent(this, ActivityLoginScreen::class.java))
}
}
}
This is how the LoginScreen looks the first time is ran.
This is how it looks after manually logging of. Note that is not only the appearence of the code, the code itself changes. Now the button doesnt allow you to LogIn.
Just to add some extra information, i have found this property on the docs
Configuration: auto_logout_link
HTML5 Attribute: data-auto-logout-link
Description: If its activated, the button will be replaced by a LogOut button if the user has already logged in
Options: True,False
This es EXACTLY what im looking for. But, from what I can see, that is only ment for webpages and not android. Does somebody know how to touch this configuration in android? or the equivalent adroid option?
I will add the link where I found the property.
https://developers.facebook.com/docs/facebook-login/web/login-button/
Thanks in advance
Ok, after some time I managed to solve the Issue.
I was logging out from the Firebase Authentication, but not from Facebook authentication.
I didnt manage to stop the button from behaving like it does, but at least the behaviour now is correct.
I will share my new LogOut code.
Button_MainActity_LogOf.setOnClickListener{
FirebaseAuth.getInstance().signOut() //Log out from Firebase
if(isFacebookLogin()) //Check if logged in on facebook
{
LoginManager.getInstance().logOut() //Log out from facebook
}
//Here i should log out from other providers
startActivity(Intent(this, ActivityLoginScreen::class.java))
}
private fun isFacebookLogin(): Boolean
{
return AccessToken.getCurrentAccessToken() !=null
}
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.
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.