I am trying to link more account with the same credentials (Email and Password, Google and Github), but when i try to log in with github i get this error:
FirebaseError: Firebase: Error (auth/account-exists-with-different-credential).
The Google authentication is working. The situation on database is shown below:
image
Here i call the functions in the authStore.js file (those functions are shown in the next code block).
async function loginGoogle() {
await authHandlers
.loginGoogle()
.then(() => {
goto('/');
})
.catch((error) => {
if (error.code === 'auth/account-exists-with-different-credential') {
alert('You have already signed up with a different auth provider for that email.');
} else {
console.error(error);
}
});
}
async function loginGithub() {
await authHandlers
.loginGithub()
.then(() => {
goto('/');
})
.catch((error) => {
if (error.code === 'auth/account-exists-with-different-credential') {
console.error(error);
alert('You have already signed up with a different auth provider for that email.');
} else {
console.error(error);
}
});
}
The code below is where i make the calls to the firebase.
export const authHandlers = {
login: async (email, password) => {
await signInWithEmailAndPassword(auth, email, password);
},
signup: async (email, password, username) => {
const { user } = await createUserWithEmailAndPassword(auth, email, password);
await updateProfile(user, {
displayName: username
});
sendEmailVerification(user)
.then(() => {
console.log('Email sent');
})
.catch((error) => {
console.log(error);
});
},
logout: async () => {
await signOut(auth);
},
loginGoogle: async () => {
await signInWithPopup(auth, new GoogleAuthProvider());
},
loginGithub: async () => {
await signInWithPopup(auth, new GithubAuthProvider());
},
The default behavior for Firebase Authentication is that it only allows a single account for each email address.
If you want to link the multiple sign-in methods to the same account in Firebase, have a look at Firebase JS API auth - account-exists-with-different-credential
If you want these sign-in methods to become separate accounts, you can enable Create multiple accounts for each identity provider under the Authentication settings in the Firebase console.
Related
I am trying to add users with email and password function in Firebase but the information is not added once the sign up button is clicked. I have inserted the block of code I've written to perform this action. I'm not sure what I'm missing.The user information does not appear in the console and there is no error message. All other data that is entered works, the authentication part is the only thing that is not working correctly.
// signing new users up
const signupForm = document.querySelector('.signup');
signupForm.addEventListener('submit', (e) => {
e.preventDefault();
const email = signupForm.email.value;
const password = signupForm.password.value;
createUserWithEmailAndPassword(auth, email, password)
.then((cred) => {
// console.log('user created:', cred.user);
signupForm.reset();
})
.catch((err) => {
// console.log(err.message);
});
// logging in and out
const logoutButton = document.querySelector('.logout');
logoutButton.addEventListener('click', () => {
signOut(auth)
.then(() => {
alert('user signed out');
})
.catch((err) => {
console.log(err.message);
});
});
const loginForm = document.querySelector('.login');
loginForm.addEventListener('submit', (e) => {
e.preventDefault();
const email = loginForm.email.value;
const password = loginForm.password.value;
signInWithEmailAndPassword(auth, email, password)
.then((cred) => {
console.log('user logged in:', cred.user);
loginForm.reset();
})
.catch((err) => {
console.log(err.message);
});```
When the user clicks on the "sign-in" button and if user.challangeName === 'NEW_PASSWORD_REQUIRED'is true, I redirect the user to a page (form screen) where he can provide the input for required attributes and a new password. Even tho the user object is getting populated upon clicking on the sign-in button, using Auth.currentsession on the form screen will print "No user found"
Can someone let me know why I'm seeing no user? What am I doing wrong here?
Here's my login function (triggered when clicked on sign-in button) where I direct the user to the change password screen (form screen) if user.challangeName === 'NEW_PASSWORD_REQUIRED' is true.
const login = async (email, password) => {
try {
const user = await Auth.signIn(email, password);
if (user.challengeName === 'NEW_PASSWORD_REQUIRED') {
navigate('/change-password');
return;
}
if (user) {
setToken(user.signInUserSession.idToken.jwtToken);
const userDetails = await getAccountDetails();
dispatch({
type: LOGIN,
payload: {
user: {
attributes: user.attributes,
username: user.username
},
client: userDetails
}
});
}
} catch (error) {
await logout();
throw error;
}
};
Here's my onSubmit function on the change password screen where eventually I want to use Auth.completeNewPassword to update the user's password in Cognito
const onSubmitClick = (e) => {
e.preventDefault();
if (validateFields());
Auth.currentSession()
.then((user) => console.log(user))
.catch((err) => console.log(err));
};
Here's the documentation provided by AWS https://docs.amplify.aws/lib/auth/manageusers/q/platform/js/#forgot-password, and the code provided by AWS to update password
Auth.signIn(username, password)
.then(user => {
if (user.challengeName === 'NEW_PASSWORD_REQUIRED') {
const { requiredAttributes } = user.challengeParam; // the array of required attributes, e.g ['email', 'phone_number']
Auth.completeNewPassword(
user, // the Cognito User Object
newPassword, // the new password
// OPTIONAL, the required attributes
{
email: 'xxxx#example.com',
phone_number: '1234567890'
}
).then(user => {
// at this time the user is logged in if no MFA required
console.log(user);
}).catch(e => {
console.log(e);
});
} else {
// other situations
}
}).catch(e => {
console.log(e);
});
New updated answer to reflect your updated post, change the onSubmitClick to the following:
const onSubmitClick = (e) => {
e.preventDefault();
if (validateFields());
Auth.currentAuthenticatedUser()
.then(user => {
console.log(user))
})
.then((data) => console.log(data))
.catch((err) => console.log(err));
};
You are looking at the documentation which shows how to use the user after Auth.signIn() (which was my previous answer), compared to what you use: Auth.currentAuthenticatedUser(). The proper documentation example you have to look at is this one: https://www.docs.amplify.aws/lib/auth/manageusers/q/platform/js
I cannot update user name where user is sign in to website it doesnt addit to user account, on displaName it shows null.The user account is created succefully but without user name as i said
const onSubmit = async ({ name, email, password }) => {
setIsLoading(true);
try {
await signup(email, password);
await updateProfile(currentUser, {
displayName: name,
});
setInterval(() => {
<Redirect to="/" />;
}, 5000);
} catch (error) {
setIsLoading(false);
console.log(error);
}
};
You can use .then().
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(() => {
// do whatever in here
setUserInfo()
sendEmailVerification()
setDisplayName()
alert('Account created!')
})
.catch(error => {
console.log(error)
alert(error.message)
})
Inside the setDisplayName(), you can set the displayName. In my case, I am using the firstName and lastName, which I am getting through my form.
const setDisplayName = () => {
firebase
.auth()
.currentUser
.updateProfile({
displayName: firstName + ' ' + lastName
})
.then(() => {
console.log('display name set')
})
.catch(error => {
console.log(error)
alert(error.message)
})
}
I am receiving the following error when triggering this cloud function: "Error unauthenticated".
I do not wish to allow unauthenticated calls to this cloud function.
The workflow is as follows:
User registers in app via firebase password authentication
Firebase Auth Credentials are created (firebase signs in user upon success)
Once the credentials have been created, the cloud function is triggered in the firebase auth callback.
At this point, the call should be authenticated, given it's being triggered in the firebase auth response.
However, it keeps erroring with
Error: unauthenticated
The user is authenticated at this point.
Any suggestions?
CLIENT CODE ->
const onRegisterPress = () => {
if (password !== confirmPassword) {
alert("Passwords don't match.")
return
}
setLoading(true);
//CREATE'S USER'S AUTH CREDENTIALS
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then((response) => {
const data = {
...
}
//console.log(response);
return new Promise(async function(resolve, reject) {
await firebase.functions().httpsCallable('writeAccountUser')({
data
}).then((response) => {
console.log("Write Account User Response: ", response);
resolve(setLoading(false));
}).catch((error) => {
console.error("Cloud Function Error: ", error);
setLoading(false);
reject(error)
})
});
})
.catch((error) => {
alert(error)
});
}
CLOUD FUNCTION ->
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
const firestore = admin.firestore();
exports.writeAccountUser = functions.https.onCall((data, context) => {
console.log("Incoming Data: ", data);
console.log("Incoming Context: ", context);
const clientData = data.data;
console.log("Client Data: ", clientData);
console.log("Account ID: ", clientData.accountID);
return new Promise(async function (resolve, reject) {
const accountsRef = firestore.collection('Accounts');
const usersRef = firestore.collection('users');
const now = new Date();
if (clientData.accountExists === true) {
console.log("Account Exists");
await accountsRef
.doc(clientData.accountID)
.update({
users: admin.firestore.FieldValue.arrayUnion(clientData.uid)
}).catch((error) => { console.error(error); reject(false) });
}
else {
console.log("Account Does Not Exist!");
const account_data = clientData.accountData;
const product_lines = clientData.productLines;
await accountsRef
.doc(clientData.accountID)
.set({
account_data,
product_lines,
users: [clientData.uid],
dateCreated: {
date: now,
timestamp: now.getTime()
}
}).catch((error) => { console.error(error); reject(false)});
};
const email = clientData.email;
const fullName = clientData.fullName;
const acceptTerms = clientData.acceptTerms;
const userData = {
id: clientData.uid,
email,
fullName,
accountID: clientData.accountID,
dateCreated: {
date: now,
timestamp: now.getTime()
},
lastUpdateFetch: {
date: now,
timestamp: now.getTime()
},
termsConditionsAccepted: acceptTerms
};
await usersRef
.doc(clientData.uid)
.set(userData)
.catch((error) => { console.error(error); reject(false) });
resolve(true);
});
});
Error ->
[Unhandled promise rejection: Error: unauthenticated]
at node_modules/#firebase/firestore/dist/rn/prebuilt.rn-f9cd27ba.js:12199:33 in
at http:///node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:169743:29 in _errorForResponse
at node_modules/#firebase/firestore/dist/rn/prebuilt.rn-f9cd27ba.js:12747:31 in yu
at node_modules/tslib/tslib.js:77:12 in
at http://REDACTED/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:120748:21 in
at http://REDACTED/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&hot=false&minify=false:120702:31 in fulfilled
You can try refactoring the function as shown below:
const onRegisterPress = async () => {
if (password !== confirmPassword) {
alert("Passwords don't match.")
return
}
setLoading(true);
const response = await firebase.auth().createUserWithEmailAndPassword(email, password)
const data = {...}
const fnResponse = await firebase.functions().httpsCallable('writeAccountUser')({data})
console.log("Write Account User Response: ", response);
}
You can also create the account using the Admin SDK in the same function and log the user on your web app after the response. That'll ensure the Cloud function's action has been executed as well (just in case the function is not called after user sign up for any reason).
I am trying to add custom user claims after user sign up, to define user role, using setCustomUserClaims:
/api/users/addUser.js
export default async (req, res) => {
const { displayName, email, password, role } = req.body;
if (!displayName || !password || !email || !role) {
return res.status(400).send({ message: 'Missing fields' });
}
try {
const { uid } = await admin.auth().createUser({
displayName,
email,
password,
});
await admin.auth().setCustomUserClaims(uid, role);
res.status(201).send(uid);
} catch (error) {
handleError(res, error);
}
};
This code checks for any change in the authentication state and sets user to the currently logged in user:
/utils/use-auth.js
useEffect(() => {
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
if (user) {
setUser(user);
} else {
setUser(false);
// Router.push('/login');
}
});
in my /pages/user/home,jsx:
import { useAuth } from '../../utils/use-auth';
function home() {
const { user } = useAuth();
return (
<div>
<pre>{JSON.stringify(user, null, 4)}</pre>
<AdminLayout componentProps={{ selected: '1' }} Component={HomeContent} />
</div>
);
}
The displayed object doesn't have any custom claims.
when I check the firebase console, I find that the user is actually added.
Try using
admin.auth().setCustomUserClaims(uid, claims).then(() => {
// Do your stuff here
});
And verify the claims like
admin.auth().verifyIdToken(idToken).then((claims) => {
// check claims
if (claims) {
// do your stuff here
}
});
for more info check https://firebase.google.com/docs/auth/admin/custom-claims#node.js