Introduce the problem
I use an email to log in.
If I log out and try to log in to the same email using Google, the UID of the account changes. I can't change back to the old UID. How can I provide multiple providers for an account?
What I tried
I asked this question to ChatGPT but it didn't answer my question. I also googled this problem.
I have read this documentation and used its code, but it didn't work. Not sure if I'm using its code correctly.
I've read this question but it didn't help me.
A minimal, reproducible example
Future<void> signInWithEmailAndPassword(String email, String password) async {
try {
await firebaseAuth.signInWithEmailAndPassword(email: email, password: password);
final credential = EmailAuthProvider.credential(email: email, password: password);
await FirebaseAuth.instance.currentUser?.linkWithCredential(credential);
} on FirebaseAuthException catch (e) {
throw FirebaseAuthException(code: e.code, message: e.message);
}
}
You should be able to achieve this via the Firebase Console. The Firebase Authentication Settings tab allows you to link accounts with the same email.
https://console.firebase.google.com/u/0/project/yourprojectname/authentication/settings
Account linking works by first having a currently signed in user, and then linking an additional provider to it. In steps:
Sign in with the existing provider.
Check that currentUser isn't null.
Create credentials for the additional provider
Link the additional provider to the existing account by calling linkWithCredential.
Related
I try to change the email address of an MFA registered user in Flutter + Firebase with the below-mentioned code. I try this code with both an already registered (verified) MFA user and a non-registered (verified) user. Both of them return the error mentioned in the above title. I expect Firebase to send an email to stated on "newEmail" address like on the Firebase console as "Email address change" template. But there is no email sent to "newEmail". How to solve this problem?
onPressed: () async {
try {
var currentUser = FirebaseAuth.instance.currentUser;
if (currentUser!.emailVerified) {
await currentUser.updateEmail(newEmail.text);
}
} on Exception catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString())));
}
},
When you try to change the email address that exists inside the FirebaseUser object using the following line of code:
await currentUser.updateEmail(newEmail.text)
It doesn't mean that Firebase will automatically send an "Email address change". No, it doesn't work like that. In order to be able to perform such an operation, the new email should already be verified.
To solve this problem, when your user types the new email address, then you have to verify that email address before trying to update the FirebaseUser object. So when your user hits update, you need to send a verification email. As soon as the user clicks on the link in the email and the email is verified, then you can perform the update using the above line of code. So it's a two steps operation.
I've been logging in fine on my app using Firebase sign in with email/password auth but now it keeps returning the error :
There is no user record corresponding to this identifier. The user may have been deleted.
The user email address and account exist so I can't really tell why I get that error.
Here's my code to sign in:
final user = await signInWithEmail(context,'${driverIDController!.text}', passwordController!.text,);
And the email util function
final signInFunc = () => FirebaseAuth.instance.signInWithEmailAndPassword(email: email.trim(), password: password); return signInOrCreateAccount(context, signInFunc);
Any thoughts or suggestions on this problem?
I would like to force users that previously authenticated with Facebook to sign up using a new provider. The reason for this is that I would like to remove Facebook as an authentication provider. I would unlink the user once the user has been successfully linked with the new provider.
For example, the user is presented with new authentication options and the user selects to continue with email. I have the following code:
func createUserAndSignIn(
username: String,
email: String,
password: String
) async throws -> String {
let credential = EmailAuthProvider.credential(withEmail: email, password: password)
// if user is already logged in (in this case with Facebook)
if let user = Auth.auth().currentUser {
try await user.link(with: credential)
}
do {
let authDataResult = try await Auth.auth().createUser(withEmail: email, password: password)
return authDataResult.user.uid
} catch {
// throw error
}
}
The linking of accounts (user.link(with:)) fails with the following error:
Domain=FIRAuthErrorDomain Code=17014 "This operation is sensitive and requires recent authentication. Log in again before retrying this request." UserInfo={NSLocalizedDescription=This operation is sensitive and requires recent authentication. Log in again before retrying this request., FIRAuthErrorUserInfoNameKey=ERROR_REQUIRES_RECENT_LOGIN}
Would this be even be the correct approach for this?
You have to re-authenticate the user. Using the current credential
if
let user = Auth.auth().currentUser,
let currentAccessToken = AccessToken.current
{
// Prompt the user to re-provide their sign-in credentials
let result = try await user.reauthenticate(
with: FacebookAuthProvider.credential(withAccessToken: currentAccessToken.tokenString)
)
// Then link the user
try await user.link(with: newCredential)
// User can be unlinked from Facebook
try await Auth.auth().currentUser?.unlink(fromProvider: "facebook.com")
}
This is needed for several operations such as updating the user's email, password or deleting the user.
The approach you're taking is close. The error you're getting is because some operations in firebase require a recent authentication to have taken place:
FIRAuthErrorCodeRequiresRecentLogin: Updating a user’s email is a
security sensitive operation that requires a recent login from the
user. This error indicates the user has not signed in recently enough.
To resolve, reauthenticate the user by invoking
reauthenticateWithCredential:completion: on FIRUser. [1]
The steps you want to take are:
Authenticate the user with an existing auth method.
Prompt the user for their email and password
Use the email and password to create an AuthCredential object.
Pass that AuthCredential object to the user's linkWithCredential method.
There's a complete walkthrough for this in the Firebase docs: https://firebase.google.com/docs/auth/web/account-linking#link-email-address-and-password-credentials-to-a-user-account
But the key point is that you have to authenticate the user with an existing provider before you do this, even if they are technically "logged in".
Note that the steps are slightly different if you want to link the user to another Auth provider other than email (such as Google): https://firebase.google.com/docs/auth/web/account-linking#link-federated-auth-provider-credentials-to-a-user-account
After that, if you wish, you can use unlink to remove the Facebook authentication.
I have a flutter app and would like the user to authenticate both with FB and Google. I do not want multiple accounts, just a single account that links both.
I am using :
firebase_auth 0.15.1
google_sign_in 4.0.14
facebook_plugin 3.0.0
I am not able to get the email address of the user when the user's account already exist with a different provider. The email is needed in order to get the list of providers for that user using the API call "fetchSignInMethodsForEmail"
Here is an example:
1: User login with Google credentials. The account is created in firebase and google is linked.
2: The user now logoff
3: The user now tries to login with FB with the same email.
-- User get the following error
code:"ERROR_ACCOUNT_EXISTS_WITH_DIFFERENT_CREDENTIAL"
details: null
message: "An account already exists with the same email address but different sign-in credentials. Sign in using a provider associated with this email address., **null**))
As you can see the email is null. I need the email in order to get a list of providers. Then I can redirect the user to correct provider
here is a snippet of my code
Future<FirebaseUser> signInWithFacebook({bool isIos}) async {
AuthCredential credential;
try {
final FacebookLoginResult result = await facebookLogin.logIn(['email']);
if (result.accessToken != null) {
credential = FacebookAuthProvider.getCredential(
accessToken: result.accessToken.token
);
AuthResult authResult = await _firebaseAuth.signInWithCredential(
credential,
);
firebaseUser = authResult.user;
return firebaseUser;
} catch (e) {
throw Exception(e);
}
return null;
}
Thanks for your help
I need to have an option for creating accounts for users, and give them the accounts. So the accounts should be created by an admin of some company, the users will login with that account and then they can change the password.
I've found one solution, with Admin SDK but if I understand it correctly, you need your own backend.
Is there any other way? Or do you have another suggestion how to manage this? Basically, a user is suppose to be linked to a company. And he has a role in that company. Admin of the company chooses the role for each user.
The common way is to use the Admin SDK in a callable cloud function to do this. It is pretty straight forward.
If you do it from the front end when you create the user the admin user is logged out and the new user is logged in.
There is however a hack to do it from the front end without being logged out, by using a secondary firebase app such as below.
import * as firebase from 'firebase/app';
const secondaryApp = firebase.initializeApp(environment.firebase, 'Secondary');
async registerUser(email, password: string) {
try {
const userCredential = await secondaryApp.auth().createUserWithEmailAndPassword(email, password)
secondaryApp.auth().signOut(); // signout the user that was just created
// If you wanted to create a document for the user in firestore then you could do it here.
return userCredential;
} catch (err) {
throw err;
}
}