I am trying to First SignIn the User using Email and Password, Then after it, I want the user's Phone Number too. So what I did I first Signed the user using his EmailAndPasswordAuthon one Activity(Custom Sign in), and after it, I Signed In the user by his Phone Number on the Next activity. But since it becomes two accounts on the same phone, and I want to merge these google and phone credentials into one account.
Then I tried to follow https://firebase.google.com/docs/auth/android/account-linking, but it is giving me the exception.
com.google.firebase.FirebaseException: User has already been linked to the given provider.
PhoneAuthActivity.java
String email = intent.getStringExtra("email");
String password = intent.getStringExtra("pass");
Toast.makeText(this, "Email" + email + "Password" + password, Toast.LENGTH_SHORT).show();// this is working
emailCredential = EmailAuthProvider.getCredential(email, password);
mPhoneNumberField = findViewById(R.id.field_phone_number);
mVerificationField = findViewById(R.id.field_verification_code);
mStartButton = findViewById(R.id.button_start_verification);
mVerifyButton = findViewById(R.id.button_verify_phone);
mResendButton = findViewById(R.id.button_resend);
mStartButton.setOnClickListener(this);
mVerifyButton.setOnClickListener(this);
mResendButton.setOnClickListener(this);
mAuth = FirebaseAuth.getInstance();
mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onVerificationCompleted(PhoneAuthCredential credential) {
Log.d(TAG, "onVerificationCompleted:" + credential);
signInWithPhoneAuthCredential(credential);
}
#Override
public void onVerificationFailed(FirebaseException e) {
Log.w(TAG, "onVerificationFailed", e);
if (e instanceof FirebaseAuthInvalidCredentialsException) {
mPhoneNumberField.setError("Invalid phone number.");
} else if (e instanceof FirebaseTooManyRequestsException) {
Snackbar.make(findViewById(android.R.id.content), "Quota exceeded.",
Snackbar.LENGTH_SHORT).show();
}
}
#Override
public void onCodeSent(String verificationId,
PhoneAuthProvider.ForceResendingToken token) {
Log.d(TAG, "onCodeSent:" + verificationId);
mVerificationId = verificationId;
mResendToken = token;
}
};
}
private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "signInWithCredential:success");
FirebaseUser user = task.getResult().getUser();
linkCredential(emailCredential);
startActivity(new Intent(PhoneActivity.this, MainActivity.class));
finish();
} else {
Log.w(TAG, "signInWithCredential:failure", task.getException());
if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
mVerificationField.setError("Invalid code.");
}
}
}
});
}
private void startPhoneNumberVerification(String phoneNumber) {
PhoneAuthProvider.getInstance().verifyPhoneNumber(
phoneNumber, // Phone number to verify
60, // Timeout duration
TimeUnit.SECONDS, // Unit of timeout
this, // Activity (for callback binding)
mCallbacks); // OnVerificationStateChangedCallbacks
}
private void verifyPhoneNumberWithCode(String verificationId, String code) {
PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
signInWithPhoneAuthCredential(credential);
}
private void resendVerificationCode(String phoneNumber,
PhoneAuthProvider.ForceResendingToken token) {
PhoneAuthProvider.getInstance().verifyPhoneNumber(
phoneNumber, // Phone number to verify
60, // Timeout duration
TimeUnit.SECONDS, // Unit of timeout
this, // Activity (for callback binding)
mCallbacks, // OnVerificationStateChangedCallbacks
token); // ForceResendingToken from callbacks
}
private boolean validatePhoneNumber() {
String phoneNumber = mPhoneNumberField.getText().toString();
if (TextUtils.isEmpty(phoneNumber)) {
mPhoneNumberField.setError("Invalid phone number.");
return false;
}
return true;
}
#Override
public void onStart() {
super.onStart();
FirebaseUser currentUser = mAuth.getCurrentUser();
if (currentUser != null) {
}
}
#Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.button_start_verification:
if (!validatePhoneNumber()) {
return;
}
startPhoneNumberVerification(mPhoneNumberField.getText().toString());
break;
case R.id.button_verify_phone:
String code = mVerificationField.getText().toString();
if (TextUtils.isEmpty(code)) {
mVerificationField.setError("Cannot be empty.");
return;
}
verifyPhoneNumberWithCode(mVerificationId, code);
break;
case R.id.button_resend:
resendVerificationCode(mPhoneNumberField.getText().toString(), mResendToken);
break;
}
}
public void linkCredential(AuthCredential credential) {
mAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "linkWithCredential:success");
FirebaseUser user = task.getResult().getUser();
Toast.makeText(PhoneActivity.this, "Merged", Toast.LENGTH_SHORT).show();
} else {
Log.w(TAG, "linkWithCredential:failure", task.getException());
Toast.makeText(PhoneActivity.this, "Failed to merge" + task.getException().toString(), Toast.LENGTH_SHORT).show();
}
}
});
}
As Commented down I've used my own custom phoneAuth, not using the one provied by FirebaseUI, Please Help me merging these two accounts into one.
And In firebase Console it is forming two accounts.
I guess you are doing it the wrong way.
The flow as mentioned in documentation:
Complete the sign-in flow for the new authentication provider up to, but not including, calling one of the FirebaseAuth.signInWith methods. For example, get the user's Google ID token, Facebook access token, or email and password.
As quoted from the documentation of linking auth provider steps, it is mentioned that you should not call any FirebaseAuth.signInWith methods, rather you need to:-
get AuthCredential for the new authentication provider
Pass the AuthCredential object to the signed-in user's linkWithCredential method, like this:
mAuth.getCurrentUser().linkWithCredential(credential)
As user is already signed with one auth provider, we don't need to sign him again. We just need to link both the providers, so that he'll be able to sign in again with either of the providers.
As the flow from your code suggests, after verifying the phone number, you are signing the user in again with PhoneAuthCredential, and then you are trying to link the emailCredential; which the current signed in user is already linked to, hence the error.
This should be your code for mCallbacks:
mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onVerificationCompleted(PhoneAuthCredential credential) {
Log.d(TAG, "onVerificationCompleted:" + credential);
linkCredential(credential);
}
#Override
public void onVerificationFailed(FirebaseException e) {
Log.w(TAG, "onVerificationFailed", e);
if (e instanceof FirebaseAuthInvalidCredentialsException) {
mPhoneNumberField.setError("Invalid phone number.");
} else if (e instanceof FirebaseTooManyRequestsException) {
Snackbar.make(findViewById(android.R.id.content), "Quota exceeded.",
Snackbar.LENGTH_SHORT).show();
}
}
#Override
public void onCodeSent(String verificationId,
PhoneAuthProvider.ForceResendingToken token) {
Log.d(TAG, "onCodeSent:" + verificationId);
mVerificationId = verificationId;
mResendToken = token;
}
};
And Here is the linkCredentials method.
public void linkCredential(AuthCredential credential) {
mAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "linkWithCredential:success");
FirebaseUser user = task.getResult().getUser();
Toast.makeText(PhoneActivity.this, "Merged", Toast.LENGTH_SHORT).show();
moveToHome();
} else {
Log.w(TAG, "linkWithCredential:failure", task.getException());
Toast.makeText(PhoneActivity.this, "Failed to merge" + task.getException().toString(), Toast.LENGTH_SHORT).show();
}
}
});
}
I agree with the answer of #kruti , Authenticating a user multiple times is wrong way.
-
First store the email and password in a variable
-
Sign In user with the phone number , this will generate a user id for that user
then you can link the Email and password with this Firebase account ( provided your data structure is fine )
get generated id through phone auth
var myid = firebase.auth().currentuser.uid
now link info with account
firebase.database().ref('Users/' + myid).push({
Email : useremail
Password : userpassword
})
Related
I am building an app that requires users to fill in the details for the sign-up screen.. once they fill in all the details and verification of details then I allow them to enter their email ID. once the email id is validated (1. blank email 2. invalid email id 3. existing email id check through Firebase) then the app will CREATE email for the user (using email password).. CREATION OF EMAIL has 3 phases.
A. Email creation itself and
B. Sending verification email to the user.
C. Waiting until the user verifies the email.
upon verification of email, I am allowing the user to enter the mobile number. On filling the mobile validation of it will happen (1. blank mobile 2. invalid mobile No 3. existing mobile check through Firebase). if validation turns positive I am asking them to verify the mobile through OTP (new activity). after OTP verification, Finally, I am storing all the user's data into firebase (except the password).
My actual problem lies here. I want users to log in through the phone OTP process or email password process. currently, I am unable to link the email and phone of the user as firebase is considering as 2 accounts. on google, I came to know that I had to link using the LinkWithCredential option. however, it's not working...
Here is my VerifyOTP codes where phone authentication happening...
public class VerifyOTP extends AppCompatActivity {
#BindView(R.id.PinViewUser)
PinView PinViewUser;
#BindView(R.id.btnVerify)
Button btnVerify;
#BindView(R.id.btnGetOTP)
Button btnGetOTP;
#BindView(R.id.btnResend)
Button btnResend;
#BindView(R.id.txtCodeVerificationText)
TextView txtCodeVerificationText;
#BindView(R.id.Verificationlabel)
TextView Verificationlabel;
#BindView(R.id.txtNoteToUser)
TextView txtNoteToUser;
FirebaseAuth mAuth;
AuthCredential emailCredential;
String UserPhoneNo, CodeBySystem, PhNoThroughSignUp, PhNoThroughForgotPassword, ForwardEmailToNextClass, GetCredEmail, GetCredPass;
PhoneAuthProvider.ForceResendingToken mResendToken;
private static final String TAG = "AnonymousAuth";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RunAppOnFullScreenMode();
setContentView(R.layout.activity_verify_o_t_p);
ButterKnife.bind(this);
//get Ph No & Other Details from SignupLayout
PhNoThroughSignUp = getIntent().getStringExtra(ReUsuableCode.CustomMobileNo);
GetCredEmail = getIntent().getStringExtra("CredEmail");
GetCredPass = getIntent().getStringExtra("CredPass");
emailCredential = EmailAuthProvider.getCredential(GetCredEmail, GetCredPass);
//get Ph No from ForgotPassword
PhNoThroughForgotPassword = getIntent().getStringExtra("FPMobile");
if (PhNoThroughSignUp == null) {
UserPhoneNo = PhNoThroughForgotPassword;
} else {
UserPhoneNo = PhNoThroughSignUp;
}
Toast.makeText(this, "Email: " + GetCredEmail + "Password: " + GetCredPass + "Phone: " + UserPhoneNo, Toast.LENGTH_LONG).show();// this is working
txtCodeVerificationText.setText("Click On Get OTP Button to Get the OTP on the Mentioned Phone No\n\n Note: Standard SMS charges May Apply");
Log.d(TAG, "DetailsCollected");
// Initialize Firebase Auth
mAuth = FirebaseAuth.getInstance();
//Send OTPCode to User
btnGetOTP.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SendVerificationCodeToUser(UserPhoneNo);
Log.d(TAG, "Verification Code Sent");
txtNoteToUser.setVisibility(View.VISIBLE);
txtCodeVerificationText.setText("Enter the One Time Password received on\n " + UserPhoneNo);
Toast.makeText(VerifyOTP.this, "OTP Initiated!! Wait For Auto Verification of OTP", Toast.LENGTH_SHORT).show();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
btnVerify.setVisibility(View.VISIBLE);
}
}, 10000); // where 1000 is equal to 1 sec (1 * 1000)
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
btnResend.setVisibility(View.VISIBLE);
}
}, 60000); // where 1000 is equal to 1 sec (1 * 1000)
}
});
//ReSend OTPCode to User
btnResend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
resendVerificationCode(UserPhoneNo, mResendToken);
txtCodeVerificationText.setText("Resent verification code to " + UserPhoneNo + "\n\n Note: Standard SMS charges May Apply");
btnResend.setVisibility(View.INVISIBLE);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
btnResend.setVisibility(View.VISIBLE);
}
}, 60000); // where 1000 is equal to 1 sec (1 * 1000)
}
});
//Manual Click on VerifyButton
btnVerify.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String code = PinViewUser.getText().toString();
if (ReUsuableCode.fieldisEmpty(code)) {
PinViewUser.setError("Field is Empty");
} else {
VerifyCode(CodeBySystem, code);
}
}
});
}
//======================================= Other Private Codes==================
private void SendVerificationCodeToUser(String phNo) {
PhoneAuthOptions options =
PhoneAuthOptions.newBuilder(mAuth)
.setPhoneNumber(phNo) // Phone number to verify
.setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit
.setActivity(this) // Activity (for callback binding)
.setCallbacks(mCallbacks) // OnVerificationStateChangedCallbacks
.build();
PhoneAuthProvider.verifyPhoneNumber(options);
}
public void linkCredential(AuthCredential credential) {
mAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "linkWithCredential:success");
FirebaseUser user = task.getResult().getUser();
Toast.makeText(VerifyOTP.this, "linkWithCredential:success", Toast.LENGTH_SHORT).show();
if (PhNoThroughSignUp == null) {
Intent GoToAnotherLayout1 = new Intent(VerifyOTP.this, SetPassword.class);
ForwardEmailToNextClass = getIntent().getStringExtra("FEmail");
GoToAnotherLayout1.putExtra("FEmail", ForwardEmailToNextClass);
startActivity(GoToAnotherLayout1);
finish();
} else {
Intent GoToAnotherLayout1 = new Intent(VerifyOTP.this, Retailer_SignUP.class);
GoToAnotherLayout1.putExtra(ReUsuableCode.MobileRegistration, "Mobile Validation Successful");
setResult(RESULT_OK, GoToAnotherLayout1);
finish();
}
} else {
Log.w(TAG, "linkWithCredential:failure", task.getException());
Toast.makeText(VerifyOTP.this, "linkWithCredential:failure" + task.getException().toString(), Toast.LENGTH_SHORT).show();
}
}
});
}
private PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks =
new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onCodeSent(#NonNull String s, #NonNull PhoneAuthProvider.ForceResendingToken forceResendingToken) {
super.onCodeSent(s, forceResendingToken);
// Save verification ID and resending token so we can use them later
CodeBySystem = s;
mResendToken = forceResendingToken;
Log.d(TAG, "OnCode Completed");
}
#Override
public void onVerificationCompleted(#NonNull PhoneAuthCredential phoneAuthCredential) {
Log.d(TAG, "On Verification start");
Toast.makeText(VerifyOTP.this, "OTP Verification Successful", Toast.LENGTH_SHORT).show();
String code = phoneAuthCredential.getSmsCode();
if (code != null) {
PinViewUser.setText(code);
VerifyCode(CodeBySystem, code);
Log.d(TAG, "Pre Link State");
linkCredential(emailCredential);
Log.d(TAG, "Post Link State");
}
}
#Override
public void onVerificationFailed(#NonNull FirebaseException e) {
Log.d(TAG, "Verification Failed");
if (e instanceof FirebaseAuthInvalidCredentialsException) {
Toast.makeText(VerifyOTP.this, "Failed to Verify!! Try After Some Time...", Toast.LENGTH_SHORT).show();
return;
} else if (e instanceof FirebaseTooManyRequestsException) {
Toast.makeText(VerifyOTP.this, "Too Many Attempts!! Try After Some Time...", Toast.LENGTH_SHORT).show();
return;
} else {
Toast.makeText(VerifyOTP.this, "Some Other Error Occured...", Toast.LENGTH_SHORT).show();
return;
}
}
};
private void VerifyCode(String verificationId, String code) {
PhoneAuthCredential Credential = PhoneAuthProvider.getCredential(verificationId, code);
Log.d(TAG, "Verified Code");
}
// [START resend_verification]
private void resendVerificationCode(String phoneNumber,
PhoneAuthProvider.ForceResendingToken token) {
PhoneAuthOptions options =
PhoneAuthOptions.newBuilder(mAuth)
.setPhoneNumber(phoneNumber) // Phone number to verify
.setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit
.setActivity(this) // Activity (for callback binding)
.setCallbacks(mCallbacks) // OnVerificationStateChangedCallbacks
.setForceResendingToken(token) // ForceResendingToken from callbacks
.build();
PhoneAuthProvider.verifyPhoneNumber(options);
}
here are my error logs:
Please do guide me where I am missing. How to do I make a single account for the user?
I was able to get an answer to my code. Here is the website which I referred to fix the code: Learning Path for Linking Phone and Email
Steps to Link with 2nd Auth Provider of Firebase:
As per my scenario, I am first making the user enter an email ID and hence I make the user to first SIGN IN using email ID and password, which is nothing but,
Creation of Account with Email and Password
Sending Verification email (Optional step)
Waiting until User validates email ID (Optional Step)
Signing in the User using first Auth Provider (EmailAuthProvider).
After the signing process is complete. I made the user enter a mobile number so that he can proceed to validate the Ph No. In this follow the below process
Send a verification code to the user's phone
In the callbacks method of onVerificationCompleted. instead of regular signing-in with phAuth provider you have to use Linkwithcredentials(phcredentials)
which is:
a)
PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
b)
linkCredential(PhCredential);
If you are using the manual verification of OTP then link the 2a and 2b step to verifybtn as well.
public void linkCredential(AuthCredential credential) {
mAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "linkWithCredential:success");
} else {
Log.w(TAG, "linkWithCredential:failure", task.getException());
Toast.makeText(VerifyOTP.this, "linkWithCredential:failure" + task.getException().toString(), Toast.LENGTH_SHORT).show();
}
}
});
For the email and password authentication in firebase we have both "register" and "log in" functions, but for the log in with Facebook we only have the login(or am i'm wrong?)
I'm trying to determine whether this is the first time the user have connected to the application with this facebook account, and if so, add it to the database as well.
this is my current code, becuase firebase is an async database, this mechanizem does not work
#Override
protected void onCreate (Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_customer);
mLoginButton = (LoginButton) findViewById(R.id.login_button);
mLoginButton.setVisibility(View.GONE);
mCallbackManager = CallbackManager.Factory.create();
mLoginButton.setReadPermissions("email", "public_profile");
mLoginButton.registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() {
#Override
public void onSuccess(LoginResult loginResult) {
handleFacebookAccessToken(loginResult.getAccessToken());
Toast.makeText(MainCustomerActivity.this, "Login Success", Toast.LENGTH_SHORT).show();
}
#Override
public void onCancel() {
Toast.makeText(MainCustomerActivity.this, "Cancelled", Toast.LENGTH_SHORT).show();
}
#Override
public void onError(FacebookException error) {
Log.d(TAG, error.toString());
Toast.makeText(MainCustomerActivity.this, "Error!", Toast.LENGTH_SHORT).show();
}
});
private void handleFacebookAccessToken (AccessToken token) {
Log.d(TAG, "handleFacebookAccessToken:" + token);
AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success");
currentUser = mAuth.getCurrentUser();
testRegisterNewUser();
updateUI();
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.getException());
Toast.makeText(MainCustomerActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
//TODO: HANDLE BAD LOGIN
}
// ...
}
});
}
private void testRegisterNewUser () {
String uid = currentUser.getUid();
mDatabase = FirebaseDatabase.getInstance().getReference().child("Users").child(uid);
mDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
testRegisterUserName = (String) dataSnapshot.getValue();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
//this is where I want to find whether this uid is already in the database
if (testRegisterUserName != null) {
return;
} else {
//this is where I want to store to the database
Can I somehow query the handleFacebookAccessToken to determine if it's the first time the user logged in to the server?
You can use the API isNewUser() for when the sign in process is complete. See the docs.
Returns whether the user is new or existing.
So your code (only the onComplete part) would look like this now:
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success");
currentUser = mAuth.getCurrentUser();
boolean isNewUser = task.getResult().getAdditionalUserInfo().isNewUser();
if(isNewUser){
testRegisterNewUser();
}
updateUI();
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.getException());
Toast.makeText(MainCustomerActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
//TODO: HANDLE BAD LOGIN
}
// ...
}
I am new to Android development. I am not getting OTP message from Fire base but if I enter the code manually then it works. I am not sure why I am not getting text message. Your help is highly appreciated. I am not sure whether I am doing correctly sendVerificationcode method correctly or not.
Steps Completed:
1) I added GSON file to app directory
2) I added test phone number in the firebase console
3) I added SHA1 code to fire base
4) I added SMS permission in the Android manifest file.
5) I enabled firebase authentication in Android studio
6) I tried different phone numbers too
VerifyPhoneActivity.java
public class VerifyPhoneActivity extends AppCompatActivity {
private String verificationId;
private FirebaseAuth mAuth;
private ProgressBar progressBar;
private EditText editText;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_verify_phone);
mAuth = FirebaseAuth.getInstance();
progressBar = findViewById(R.id.progressbar);
editText = findViewById(R.id.editTextCode);
String phonenumber = getIntent().getStringExtra("phonenumber");
sendVerificationCode(phonenumber);
findViewById(R.id.buttonSignIn).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String code = editText.getText().toString().trim();
if (code.isEmpty() || code.length() < 6) {
editText.setError("Enter code...");
editText.requestFocus();
return;
}
verifyCode(code);
}
});
}
private void verifyCode(String code) {
PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
signInWithCredential(credential);
}
private void signInWithCredential(PhoneAuthCredential credential) {
// private void signInWithCredential(PhoneAuthCredential) {
mAuth.signInWithCredential(credential)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Intent intent = new Intent(VerifyPhoneActivity.this, ProfileActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
} else {
Toast.makeText(VerifyPhoneActivity.this, task.getException().getMessage(), Toast.LENGTH_LONG).show();
}
}
});
}
private void sendVerificationCode(String number) {
progressBar.setVisibility(View.VISIBLE);
PhoneAuthProvider.getInstance().verifyPhoneNumber(
number,
60,
TimeUnit.SECONDS,
TaskExecutors.MAIN_THREAD,
mCallBack
);
}
private PhoneAuthProvider.OnVerificationStateChangedCallbacks
mCallBack = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onCodeSent(String s, PhoneAuthProvider.ForceResendingToken forceResendingToken) {
super.onCodeSent(s, forceResendingToken);
verificationId = s;
}
#Override
public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
String code = phoneAuthCredential.getSmsCode();
if (code != null) {
editText.setText(code);
verifyCode(code);
}
System.out.println("Hello Phone Number"+code);
}
#Override
public void onVerificationFailed(FirebaseException e) {
Toast.makeText(VerifyPhoneActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
};
}
Below code looks like not working:
#Override
public void onVerificationCompleted(PhoneAuthCredential phoneAuthCredential) {
String code = phoneAuthCredential.getSmsCode();
// String code="000000";
if (code != null) {
editText.setText(code);
verifyCode(code);
}
System.out.println("Hello Phone Number2"+code);
}
Use like this following:
onCreate
mAuth = FirebaseAuth.getInstance();
mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks()
{
#Override
public void onVerificationCompleted(PhoneAuthCredential credential)
{
// This callback will be invoked in two situations:
// 1 - Instant verification. In some cases the phone number can be instantly
// verified without needing to send or enter a verification code.
// 2 - Auto-retrieval. On some devices Google Play services can automatically
// detect the incoming verification SMS and perform verificaiton without
// user action.
Log.d(TAG, "onVerificationCompleted:" + credential);
Log.e("number","credential=-=-=>>><<>>>signInWithPhoneAuthCredential-->>");
signInWithPhoneAuthCredential(credential);
}
#Override
public void onVerificationFailed(FirebaseException e)
{
// This callback is invoked in an invalid request for verification is made,
// for instance if the the phone number format is not valid.
Log.e(TAG, "onVerificationFailed", e);
// Show a message and update the UI
// ...
}
#Override
public void onCodeSent(String verificationId,
PhoneAuthProvider.ForceResendingToken token)
{
// The SMS verification code has been sent to the provided phone number, we
// now need to ask the user to enter the code and then construct a credential
// by combining the code with a verification ID.
Log.e(TAG, "onCodeSent:" + verificationId+"<<token>>"+token);
// Save verification ID and resending token so we can use them later
mVerificationId = verificationId;
//mResendToken = token;
// ...
}
};
//String phoneNumber=Settings.PREFIX + Settings.PREFIX_PHONE;
String phoneNumber="your phone number with prefix";
Log.e("number","credential=-=-=>>>22222>>"+phoneNumber);
if(phoneNumber!=null && !phoneNumber.isEmpty())
{
startPhoneNumberVerification(phoneNumber);
}
Method:
private void startPhoneNumberVerification(String phoneNumber)
{
Log.e("startPhoneNumber","startPhoneNumberVerification------>>"+phoneNumber);
PhoneAuthProvider.getInstance().verifyPhoneNumber(
phoneNumber, // Phone number to verify
60, // Timeout duration
TimeUnit.SECONDS, // Unit of timeout
this, // Activity (for callback binding)
mCallbacks); // OnVerificationStateChangedCallbacks
Log.e("startPhoneNumber","startPhoneNumberVerification--2222222---->>"+phoneNumber);
}
Method:
private void signInWithPhoneAuthCredential(PhoneAuthCredential credential)
{
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>()
{
#Override
public void onComplete(#NonNull Task<AuthResult> task)
{
if (task.isSuccessful())
{
// Sign in success, update UI with the signed-in user's information
Log.e(TAG, "signInWithCredential:success");
} else
{
// Sign in failed, display a message and update the UI
Log.e(TAG, "signInWithCredential:failure", task.getException());
if (task.getException() instanceof FirebaseAuthInvalidCredentialsException)
{
}
}
}
});
}
For the email and password authentication in firebase we have both "register" and "log in" functions, but for the log in with Facebook we only have the login(or am i'm wrong?)
I'm trying to determine whether this is the first time the user have connected to the application with this facebook account, and if so, add it to the database as well.
this is my current code, becuase firebase is an async database, this mechanizem does not work
#Override
protected void onCreate (Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_customer);
mLoginButton = (LoginButton) findViewById(R.id.login_button);
mLoginButton.setVisibility(View.GONE);
mCallbackManager = CallbackManager.Factory.create();
mLoginButton.setReadPermissions("email", "public_profile");
mLoginButton.registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() {
#Override
public void onSuccess(LoginResult loginResult) {
handleFacebookAccessToken(loginResult.getAccessToken());
Toast.makeText(MainCustomerActivity.this, "Login Success", Toast.LENGTH_SHORT).show();
}
#Override
public void onCancel() {
Toast.makeText(MainCustomerActivity.this, "Cancelled", Toast.LENGTH_SHORT).show();
}
#Override
public void onError(FacebookException error) {
Log.d(TAG, error.toString());
Toast.makeText(MainCustomerActivity.this, "Error!", Toast.LENGTH_SHORT).show();
}
});
private void handleFacebookAccessToken (AccessToken token) {
Log.d(TAG, "handleFacebookAccessToken:" + token);
AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success");
currentUser = mAuth.getCurrentUser();
testRegisterNewUser();
updateUI();
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.getException());
Toast.makeText(MainCustomerActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
//TODO: HANDLE BAD LOGIN
}
// ...
}
});
}
private void testRegisterNewUser () {
String uid = currentUser.getUid();
mDatabase = FirebaseDatabase.getInstance().getReference().child("Users").child(uid);
mDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
testRegisterUserName = (String) dataSnapshot.getValue();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
//this is where I want to find whether this uid is already in the database
if (testRegisterUserName != null) {
return;
} else {
//this is where I want to store to the database
Can I somehow query the handleFacebookAccessToken to determine if it's the first time the user logged in to the server?
You can use the API isNewUser() for when the sign in process is complete. See the docs.
Returns whether the user is new or existing.
So your code (only the onComplete part) would look like this now:
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success");
currentUser = mAuth.getCurrentUser();
boolean isNewUser = task.getResult().getAdditionalUserInfo().isNewUser();
if(isNewUser){
testRegisterNewUser();
}
updateUI();
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.getException());
Toast.makeText(MainCustomerActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
//TODO: HANDLE BAD LOGIN
}
// ...
}
For the email and password authentication in firebase we have both "register" and "log in" functions, but for the log in with Facebook we only have the login(or am i'm wrong?)
I'm trying to determine whether this is the first time the user have connected to the application with this facebook account, and if so, add it to the database as well.
this is my current code, becuase firebase is an async database, this mechanizem does not work
#Override
protected void onCreate (Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_customer);
mLoginButton = (LoginButton) findViewById(R.id.login_button);
mLoginButton.setVisibility(View.GONE);
mCallbackManager = CallbackManager.Factory.create();
mLoginButton.setReadPermissions("email", "public_profile");
mLoginButton.registerCallback(mCallbackManager, new FacebookCallback<LoginResult>() {
#Override
public void onSuccess(LoginResult loginResult) {
handleFacebookAccessToken(loginResult.getAccessToken());
Toast.makeText(MainCustomerActivity.this, "Login Success", Toast.LENGTH_SHORT).show();
}
#Override
public void onCancel() {
Toast.makeText(MainCustomerActivity.this, "Cancelled", Toast.LENGTH_SHORT).show();
}
#Override
public void onError(FacebookException error) {
Log.d(TAG, error.toString());
Toast.makeText(MainCustomerActivity.this, "Error!", Toast.LENGTH_SHORT).show();
}
});
private void handleFacebookAccessToken (AccessToken token) {
Log.d(TAG, "handleFacebookAccessToken:" + token);
AuthCredential credential = FacebookAuthProvider.getCredential(token.getToken());
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success");
currentUser = mAuth.getCurrentUser();
testRegisterNewUser();
updateUI();
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.getException());
Toast.makeText(MainCustomerActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
//TODO: HANDLE BAD LOGIN
}
// ...
}
});
}
private void testRegisterNewUser () {
String uid = currentUser.getUid();
mDatabase = FirebaseDatabase.getInstance().getReference().child("Users").child(uid);
mDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
testRegisterUserName = (String) dataSnapshot.getValue();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
//this is where I want to find whether this uid is already in the database
if (testRegisterUserName != null) {
return;
} else {
//this is where I want to store to the database
Can I somehow query the handleFacebookAccessToken to determine if it's the first time the user logged in to the server?
You can use the API isNewUser() for when the sign in process is complete. See the docs.
Returns whether the user is new or existing.
So your code (only the onComplete part) would look like this now:
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success");
currentUser = mAuth.getCurrentUser();
boolean isNewUser = task.getResult().getAdditionalUserInfo().isNewUser();
if(isNewUser){
testRegisterNewUser();
}
updateUI();
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.getException());
Toast.makeText(MainCustomerActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
//TODO: HANDLE BAD LOGIN
}
// ...
}