I'm allowing user to Sign Up with Google, Facebook or Email. Follow this link I know how to link multiple auth provider to the same account but here is my scenario
Let say an user create account with Google or Facebook but later(after his account created) he want to set password so he can login with email and password as well. I know how to link an email/password account with other federated provider(google, facebook) but not the other way. My only though is to create an new account with that new password and email associated with the method they choose in early stage but it will not work due to email already exist. I even enable allow multiple account with same address at my console but it return ERROR_EMAIL_ALREADY_IN_USE so I think it doesn't work with email/password.
So how can I add password to that account after sign up with Facebook or Google? Thank in any advace.
From the documentation on linking account providers:
Email-password sign-in
AuthCredential credential = EmailAuthProvider.getCredential(email, password);
And then you link the credential with the existing Google/Facebook accont with:
Pass the AuthCredential object to the signed-in user's linkWithCredential method:
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();
updateUI(user);
} else {
Log.w(TAG, "linkWithCredential:failure", task.getException());
Toast.makeText(AnonymousAuthActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
updateUI(null);
}
// ...
}
});
Update
use signInAndRetrieveDataWithCredential instead of linkWithCredential, because linkWithCredential is deprecated.
Related
I have made a sign-in page, and a sign-up page with Firebase Authentication in Flutter and Dart.
After the sign up, I'm trying to retrieve the current user's displayName, however, when I try retrieving it, I seem to get not the current one, but the one that I signed up with before this one.
However, when I for example hot-restart the app, I get the current user's details just fine.
I try to retrieve the current user's displayName property with this code:
static String? getUsername() {
return FirebaseAuth.instance.currentUser?.displayName!;
}
The way I call this, is I initialize a variable to store the username which I get from the method, on a different dart file, different from the signUp page I got. I also call this method in the initState() method.
This is how I sign-up the user and set the displayName:
static void signUpUser(String username, String emailAddress, String password) async {
try {
final credential =
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: emailAddress,
password: password,
);
// Here I set the displayName property
await credential.user!.updateDisplayName(username);
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {}
else if (e.code == 'email-already-in-use') {}
} catch (e) {}
}
I tried to use the user.reload(), and FirebaseAuth.userChanges() functions, but these did not seem to fix my problem.
Maybe I'm trying to retrieve the displayName property wrong, what am I missing? I'm quite new to developing my own apps and working with Firebase.
The Future that updateDisplayName returns completes when the call has been made to the underlying Firebase SDK. It does not automatically update the user profile in your application at that point though. That will only happen automatically once every hour (when the SDK refreshes the ID token on which that profile is based), or when the user signs out and in again.
To force a refresh of the profile from your application code outside of those automatic conditions, you can call reload() on the user object.
I've created a login and registration screen that works with my Flutter app, using Firebase as the backend authentication service. I'm able to switch between the login, registration and reset password screens well.
The Issue
At the moment, the registration screen accepts any email address that is entered, whether or not it is real. For example, if I were to type in gvevg#gverwbgw.com, it would allow the user to register. This is obviously an issue, when it comes to fake accounts and spam etc.
The Aim
I would basically like to be able to edit my code, to automatically generate an email address verification email, which prevents the user from signing in, before their email address has been verified. The code I have made uses a Future, FirebaseAuth and async/await to make this happen.
My Current Code
Firstly, I define an AuthBase abstract class, that creates the 'createUserWithEmailAndPassword' function (amongst others) as follows:
abstract class AuthBase {
Stream<User> get onAuthStateChanged;
Future<User> currentUser();
Future<User> createUserWithEmailAndPassword(String email, String password);
}
Then, I create an Auth function, that implements AuthBase, gets the current user from Firebase and creates the registration Future function, as follows:
class Auth implements AuthBase {
final _firebaseAuth = FirebaseAuth.instance;
// This creates the user ID token variable (if one doesn't already exist) which is then populated using one of the login methods.
User _userFromFirebase(FirebaseUser user) {
if (user == null) {
return null;
}
return User(uid: user.uid);
}
// This helps to get the user from Google Firebase, noting if there is or isn't a user with those login details already.
#override
Stream<User> get onAuthStateChanged {
return _firebaseAuth.onAuthStateChanged.map(_userFromFirebase);
}
// This identifies the current user, who is logged in at the time.
#override
Future<User> currentUser() async {
final user = await _firebaseAuth.currentUser();
return _userFromFirebase(user);
}
// This creates the user account for an email-and-password sign-in, with firebase, if it doesn't already exist.
#override
Future<User> createUserWithEmailAndPassword(
String email, String password) async {
final authResult = await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password);
return _userFromFirebase(authResult.user);
}
}
My Question
How do I edit my code, so that it allows me to implement email verification automatically for any user that wants to sign in with email? I believe the sendEmailVerification() function must use FirebaseUser, although I am not sure how to implement it here. I would appreciate any help. Thanks!
Email+password authentication requires nothing more than that the user knows the combination of email+password. It doesn't in itself require the email address to be verified to sign in. If you want the email address to be verified before allowing access to other data, you can do that by checking the user's token for the email_verified claim for example in the security rules of your database.
Also see:
How do I lock down Firebase Database to any user from a specific (email) domain?
Firebase email verification at SignUp
How to prevent user authentication in Firebase/Vue.js BEFORE email is verified
Only let pre-verified users log into Firebase
I am having some trouble understanding how to link an existing email account to an anonymous firebase account. is this possible ? or does it only link if the email account is new ?
when i call the following code to link accounts both the anonymous account and existing account exist. but if its a new email account then i see that the new email account as the same uid as the anonymous account and the anonymous account is gone.
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();
updateUI(user);
} else {
Log.w(TAG, "linkWithCredential:failure", task.getException());
Toast.makeText(AnonymousAuthActivity.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
updateUI(null);
}
// ...
}
});
so my question is: am i able to link anonymous user account to EXISTING user account ? because then my firebase console is going to be filled with anonymous user entries.
UPDATE: Using the firebase mergService here how can i delete the anonymous account ? i dont see it returning a credential for me to delete.
the mergeService describe looks like this:
public class MyManualMergeService extends ManualMergeService {
private Iterable<DataSnapshot> mChatKeys;
#Override
public Task<Void> onLoadData() {
final TaskCompletionSource<Void> loadTask = new TaskCompletionSource<>();
FirebaseDatabase.getInstance()
.getReference()
.child("chatIndices")
.child(FirebaseAuth.getInstance().getCurrentUser().getUid())
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
mChatKeys = snapshot.getChildren();
loadTask.setResult(null);
}
#Override
public void onCancelled(DatabaseError error) {
FirebaseCrash.report(error.toException());
}
});
return loadTask.getTask();
}
#Override
public Task<Void> onTransferData(IdpResponse response) {
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference chatIndices = FirebaseDatabase.getInstance()
.getReference()
.child("chatIndices")
.child(uid);
for (DataSnapshot snapshot : mChatKeys) {
chatIndices.child(snapshot.getKey()).setValue(true);
DatabaseReference chat = FirebaseDatabase.getInstance()
.getReference()
.child("chats")
.child(snapshot.getKey());
chat.child("uid").setValue(uid);
chat.child("name").setValue("User " + uid.substring(0, 6));
}
return null;
}
}
this gets called after user transitions from anonymous user to a real account. how can i then know the credential so i can delete the anonymous account ?
You can't link an existing credential to anonymous user.
You have to basically copy the data of the anonymous user to the existing credential user and then delete the anonymous user.
It is not possible for Firebase to handle this for you. You have 2 users with different uids and data saved on each users, not to mention different profile data on each. Firebase doesn't know which user to keep and how to merge the profile/data. In some cases, data could be saved outside of Firebase services.
FirebaseUI is currently doing a similar mechanism for upgrading anonymous users on sign in. If the credential is new, then linking will succeed without any additional action. If the credential already exists, linking will fail and the developer is expected to handle the merge conflict, copy the data from the non anonymous user and delete the anonymous user after.
This is the web flow in FirebaseUI-web: https://github.com/firebase/firebaseui-web#upgrading-anonymous-users
This is being implemented for FirebaseUI-android:
https://github.com/firebase/FirebaseUI-Android/pull/1185
Here is an example with web, given an authCredential and an anonymous user signed in.
Here is a simple web example how to handle merge conflicts.
let data;
// Default App with anonymous user.
const app = firebase.app();
// Anonymous user.
anonymousUser = app.auth().currentUser;
// Get anonymous user data.
app.database().ref('users/' + app.auth().currentUser.uid)
.once('value')
.then(snapshot => {
// Store anonymous user data.
data = snapshot.val();
// Sign in credential user.
return app.auth().signInWithCredential(authCredential);
})
.then(user => {
// Save the anonymous user's data to the credential user.
return app.database().ref('users/' + user.uid).set(data);
})
.then(() => {
// Delete anonymnous user.
return anonymousUser.delete();
})
})
I'm making chat which will be based on firebase.
The chat is gonna be part of our existing site which already has authentication and registration, and also users will not be able to create chat rooms or enter them,
it all will be handled by site automacally. Users only will be able to read and write messages to chat rooms to which they were previously added by site.
For automatic authetification I think to use signInWithCustomToken() method.
But I don't quite understand how to automatically create users and add them to chat rooms. The firebase.auth().createUserWithEmailAndPassword(email, password)
method needs email and password but I wanna just add users without any emails and passwords. I've read docs but could not find any solution, can anybody help me?
I had a similar problem and chose to use the email and password login and just created an email (username#myapp.com) and password each time I needed to add users
You can create custom tokens and use them to authenticate. Sample java code
public static String createCustomToken(String userId, Map<String, Object> additionalClaims) {
FirebaseOptions options = new FirebaseOptions.Builder()
.setServiceAccount(new FileInputStream(FIREBASE_ACCESS_FILE)).build();
FirebaseApp.initializeApp(options);
Task<String> customToken = FirebaseAuth.getInstance().createCustomToken(userId, additionalClaims);
return customToken.getResult().toString();
}
enter code here
When the user logs-in to your website, you can return above generated custom token. The client app(ios,android or web) can use this custom token to sign-in as below
firebase.auth().signInWithCustomToken(token).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// [START_EXCLUDE]
if (errorCode === 'auth/invalid-custom-token') {
alert('The token you provided is not valid.');
} else {
console.error(error);
}
// [END_EXCLUDE]
}
I am using Firebase authentication in my app and signing up a user with email and password. I want to get other users details (separate from the logged-in user) as well while a user is signed in with their own account. How can I get that information?
Values like email, display name and id (specific to authentication system) are available off of the Firebase User object. You can get a reference to the current logged in user off of the FIRAuth class. I provided links and class names for iOS, but other platforms are similarly structured.
If you want to store additional data for users, I would recommend including a users root node, using the uid off of the Firebase User object as the key for users child nodes.
//create user
auth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(SignupActivity.this, new OnCompleteListener < AuthResult > () {
#Override
public void onComplete(#NonNull Task < AuthResult > task) {
Toast.makeText(SignupActivity.this, "createUserWithEmail:onComplete:" + task.isSuccessful(), Toast.LENGTH_SHORT).show();
progressBar.setVisibility(View.GONE);
// If sign in fails, display a message to the user. If sign in succeeds
// the auth state listener will be notified and logic to handle the
// signed in user can be handled in the listener.
if (!task.isSuccessful()) {
Toast.makeText(SignupActivity.this, "Authentication failed." + task.getException(),
Toast.LENGTH_SHORT).show();
} else {
String user_id = auth.getCurrentUser().getUid();
DatabaseReference current_user_db = _Database.child(user_id);
current_user_db.child("name").setValue(name);
current_user_db.child("image").setValue("default");
startActivity(new Intent(SignupActivity.this, ProfileActivity.class));
finish();
}
}
});
}
});
It's not a security issue, but just a mean of how you treat personal information. You can store what you want in firebase so you can easily store when the user login his/her avatar url (aka facebook url) or just id or any other infos anyway, then retrieve it.
If you need to retrieve infos of users which are not using your apps beside, thden you can also easily via the facebook sdk with user permission of course. take care–