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.
Related
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?
For a Flutter mobile app I am trying to use a Microsoft OAuthProvider to get a Firebase credential with which to sign the user into Firebase with their Azure AD account.
The closest I got was using a third party Active Directory auth package to log the user in and get an access token. However the sign-in to Firebase fails with an error message that suggests the idToken is invalid.
final AadOAuth oauth = new AadOAuth(config);
await oauth.login();
// accessToken looks legit
String accessToken = await oauth.getAccessToken();
String idToken = await oauth.getIdToken();
OAuthProvider provider = OAuthProvider('microsoft.com');
// Also tried the constructor without the idToken
OAuthCredential credential = provider.credential(accessToken: accessToken, idToken: idToken);
// app fails here:
await FirebaseAuth.instance.signInWithCredential(credential);
// this works fine, but only on web platform:
await FirebaseAuth.instance.signInWithPopup(provider);
Because it is a platform specific error (iOS in this case), the exception details are not surfaced. All I get is:
PlatformException(internal-error, ) nativeErrorCode: 17999
Here is my app settings in the Azure portal:
Full manifest here
Has anyone been successful in using Microsoft Auth to sign a user in to Firebase in a Flutter mobile app?
You can use Firebase Auth OAuth package for it.
And sign in to the firebase using the Microsoft Auth provider.
User user = await FirebaseAuthOAuth().openSignInFlow(
"microsoft.com", ["email openid"], {'tenant': 'your-tenent-id'});
This integrates nicely with firebase so, firebase authStateChange also works with this method.
You have just to not receiving idToken, just verify that you have add the id_token for the response_type and also openid scope like
provider.addScope('openid');
Also check weather you have allowed implicit flow with id token in the Azure portal app settings (you sould check ID tokens on the Authentication tab under Implicit grant section).
Ok than have you added Microsoft as a authentication provider in the firebase authentication configuration Sign-in method page? And also have you tried to authenticate with Auth, after getCredentials method as stated in the documentation?
provider.getCredentialWith(nil) { credential, error in
if error != nil {
// Handle error.
}
if credential != nil {
Auth().signIn(with: credential) { authResult, error in
if error != nil {
// Handle error.
}
// User is signed in.
// IdP data available in authResult.additionalUserInfo.profile.
// OAuth access token can also be retrieved:
// authResult.credential.accessToken
// OAuth ID token can also be retrieved:
// authResult.credential.idToken
}
}
}
firebase authentication package has a method called signInWithPopup so you don't need firebase_auth_oauth anymore. here my code:
Future<UserCredential?> loginWithMicrosoft() async {
OAuthProvider provider = OAuthProvider('microsoft.com');
provider.setCustomParameters({
"tenant": "your-tenant-id",
});
provider.addScope('user.read');
provider.addScope('profile');
try {
final userCredential = await FirebaseAuth.instance.signInWithPopup(provider);
return userCredential;
} on FirebaseAuthException catch(err) {
debugPrint(err.message);
// Handle FirebaseAuthExceptions
// ex: firebase_auth/account-exists-with-different-credential
}
}
Remeber add the redirect URI and enable de scopes in Azure Portal.
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;
}
}
I built a custom authentication system using FirebaseAuthentication tokens.
My signup / login flow should work like this:
User presses login button
My server generates the authentication token and sends it to the client
Check if the user already exists (in the 'Auth' table or in my database?)
If true: sign in using FIRAuth.auth()?.signIn(withCustomToken:...
If false: Show a form to to enter custom information (name, etc..)
sign using FIRAuth.auth()?.signIn(withCustomToken:...
save the custom information to my database
My question is: How can I find out if the user has already signed up?
Would a publicly accessible database with only uid's be the way to go?
This is fairly opinion based, but yes, I would use a standalone DB that stores each user's username who has signed up. Then all that is required is a quick web request through a PHP file querying for any rows returned with that username.
The firebase sign in method will feedback in asynchronous callback.
FIRAuth.auth()?.signInWithEmail(email, password: password, completion: { (user , error) in
if let error = error {
print(error.localizedDescription)
return
}
self.signedIn(user)
})
If you haven't sign up yet. The error will print out
There is no user record corresponding to this identifier. The user may have been deleted.