I'm fairly new to the Firebase ecosystem, so I hope I'm not asking something too basic.
I'm using the firebase-js-sdk along with an e-mail + password registration. When the user signs up using an e-mail I prompt them to select their username and I store it using the user.updateProfile() method. This works fine, as the next time I call firebase.auth().currentUser I see the displayName property containing the updated value.
As for facebook, I'm using the react-native-fbsdk, and I authenticate the user using the following function:
const fbLogin = () => {
return new Promise((resolve, reject) => {
LoginManager
.logInWithReadPermissions(['public_profile', 'email'])
.then((result) => {
if (result.isCancelled) {
console.log('login cancelled');
} else {
AccessToken
.getCurrentAccessToken()
.then((data) => {
const credentials = firebase.auth.FacebookAuthProvider.credential(data.accessToken);
firebase
.auth().signInWithCredential(credentials)
.then((res) => resolve(res))
.catch((err) => reject(err));
})
}
}).catch(err => reject(err));
});
}
Once I store the user's data on Firebase I ask him to choose an username and I update the displayName following the same steps of the e-mail authentication. This seems to work too because if I call firebase.auth().currentUser I see the updated displayName. The only problem is when I reload the app the displayName is back to the facebook name.
My questions are:
Is it possible to override the displayName provided by Facebook?
If so, is this the correct approach to do so?
Thanks in advance to anyone that will help :)
Related
I'm facing a problem with aws cognito login. When I'm logging in with google, it is fine. but when I'm logging out and again clicking on "sign in with google" button it is taking me to the previous account that I logged in before logging out without asking me to select an account. But when I clear the cookies clicking on the "View site information" button on the address bar(Cannot find anything in Application=>cookies storage, that's why I had to use this method) and log out, and again try to login with google then it is asking me to select a google account to log in. But without clearing cookies from view site information it is taking me to previous account without giving me list of account to log in.
See this image that may help you to understand better:
Here is my logOut function:
const logout = () => {
window.location.reload();
const user = Pool.getCurrentUser();
if (user) {
user.signOut();
}
const accessToken = JSON.parse(localStorage.getItem("accessToken"));
const refreshToken = JSON.parse(localStorage.getItem("refreshToken"));
if (accessToken) {
localStorage.removeItem("accessToken");
}
if (refreshToken) {
localStorage.removeItem("refreshToken");
}
const authUser = JSON.parse(localStorage.getItem("authUser"));
if (authUser) {
localStorage.removeItem("authUser");
}
navigate(RoutingPaths.Login);
};
authenticate function:
const authenticate = (Username, Password) => {
return new Promise((resolve, reject) => {
const user = new CognitoUser({
Username,
Pool: Pool,
});
const authDetails = new AuthenticationDetails({
Username,
Password,
});
user.authenticateUser(authDetails, {
onSuccess: (data) => {
resolve(data);
},
onFailure: (err) => {
reject(err);
},
newPasswordRequired: (data) => {
resolve(data);
},
});
});
};
I don't think it's anything wrong that you do with the logout on your part. Most probably it's Amazon Cognito remembering the preferred user and trying to log in with that user. If your Google session for that user was expired, I'm pretty sure that you would have seen that "choose account" screen again.
When you clear cookies through that "i" icon in the browser, you also clear Cognito's cookies. That's why it forgets the preferred Google user and asks to choose the account again. You don't see those cookies in the Application -> cookie storage, because the browser only shows localhost cookies there.
when create user with Firebase createUserWithEmailAndPassword. Function runs twice. That's why, it gives error that email is already in use. App in vue js 2 and firebase 9.
async registerForm(){
const auth = getAuth()
const user = await createUserWithEmailAndPassword(auth, this.email, this.password)
.then(userCredential => {
this.userId = userCredential.user.uid
console.log(this.userId);
this.x++
})
.catch(error => {
console.log(error);
})
console.log('user signed up', this.x);
this.$router.replace({name: 'home'})**
}
The problem maybe in the routing redirect, if you have located the auth function on the rendering side, when you change the route or move to another one you are rendering again the component which could be a reason to call the function twice
I'm working on adding Facebook and Google login to an app. So far so good with the Google side of things, ot works but not the Facebook. I found out that having two emails linked to different accounts causes a problem so I handled it in the catch() of loginWithProvider().
The problem I have is that I cannot get the user to pick another account. The Facebook login popup opens, detects that there is another account with that email and opens the Google popup. I want to let the user choose the Facebook account to log in with. I thought the prompt: "select_account" would fix it but it does not.
Here is my code:
const loginWithProvider = (provider, onSuccess) => async dispatch => {
Firebase.auth()
.signInWithPopup(provider)
.then(result => {
// register the user in my backend here
})
.catch(({ code, email }) => {
if (code === "auth/account-exists-with-different-credential") {
Firebase.auth()
.fetchSignInMethodsForEmail(email)
.then(providers => {
if (providers.includes("google.com")) {
dispatch(
loginWithGoogle(onSuccess, {
login_hint: email
})
);
}
});
}
})
.then(() => isFunction(onSuccess) && onSuccess());
};
export const loginWithFacebook = (onSuccess, parameters) => async dispatch => {
const provider = new Firebase.auth.FacebookAuthProvider();
provider.setCustomParameters({ ...parameters, prompt: "select_account" });
dispatch(loginWithProvider(provider, onSuccess));
};
export const loginWithGoogle = (onSuccess, parameters) => async dispatch => {
const provider = new Firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({ ...parameters, prompt: "select_account" });
dispatch(loginWithProvider(provider, onSuccess));
};
Any ideas?
Edit: After some research it turns out that prompt: "select_account" is valid for the Google popup but not for the Facebook one. I'm struggling to find what do I need to use for Facebook's.
Hi I am using express for backend authentication and these are my sign in functions/controllers on the front end.
export const signInUser = async credentials => {
console.log('this is for the signInUser', credentials)
try {
const resp = await api.post('/sign-in', credentials)
localStorage.setItem('token', resp.data.token)
return resp.data
} catch (error) {
throw error
}
}
onSignIn = event => {
event.preventDefault()
const { history, setUser } = this.props
signInUser(this.state)
.then(res => setUser(res.user))
.then(() => history.push('/Home'))
.catch(error => {
console.error(error)
this.setState({
loginUsername: '',
loginPassword: '',
})
})
}
setUser = user => this.setState({ user })
and this is my sign in controller on the backend
const signIn = async (req, res) => {
try {
console.log('hello' ,req.body);
const { loginUsername, username, loginPassword } = req.body;
const user = await User.findOne({
where: {
username: loginUsername
}
});
console.log('this is the user', user)
if (await bcrypt.compare(loginPassword, user.dataValues.password_digest)) {
const payload = {
id: user.id,
username: user.username,
password: user.password
};
const token = jwt.sign(payload, TOKEN_KEY);
return res.status(201).json({ user, token });
} else {
res.status(401).send("Username or Password is invalid- try again.");
}
} catch (error) {
return res.status(500).json({ error: error.message });
}
};
The issue is the state of the user doesn't persist on refresh but I still have the json webtoken in my local storage and this is an issue when I make post requests and even signing up since I am redirecting to the home page and losing the user state. Any help would be appreciated!
From your tags, I noticed that you are using React, so the solution is simple!
you can have an GlobalAuthManager context for your application that would wrap all the components at the most higher level! after <React.strictMode> like below:
<React.StrictMode>
<GlobalAuthManager.Provider value={{authData}}>
<App />
</GlobalAuthManager.Provider>
</React.StrictMode>
As you might guess, this would be a context! that would provide you your user data to all your components!
The Pattern:
1. Store token:
when your user logins to your app, you would receive a token ( in your response or in response header ), you need to store the token value in localstorage, or more better in cookie storage (there are a lot of articles about it why), one is here.
2. have a /getUserData endpoint in backend:
you need to have a /getUserData endpoint in backend to retrive your user data based on token
3. call /getUserData in app mount:
before every thing in your app, you need to call this endpoint if you find token in localstorage or cookie storage. so if you run this in your componnetDidMount or useEffect(() => { ... }, []), that would work!
4. store your user data and state in context:
after you've called the /getUserData and if you had a valid token(i mean not expired token or not interrupted and edited token) , you will get you user data and what you need to do is that you need to store this in your GlobalAuthManager and provide that in to your Global App component!
after that you have your user data available to you that you can decide to show login or sign up button in your Navbar or disable/enable comment section for example based on your user data!
Wrap up:
So the key is that you have to have a GlobalAuthManager for only one purpose, that before every thing it runs in the top level in your app and gets you your user data based on provided token from localstorage or cookie storage!
after that you can manage your app state based on that your user is logged in or not!
I am trying to change/update a user's email address using :
firebase.auth().changeEmail({oldEmail, newEmail, password}, cb)
But I am getting ...changeEmail is not a function error. I found the reference here from the old firebase docu.
So how to I do it in the 3.x version? Because I cant find a reference in the new documentation.
You're looking for the updateEmail() method on the firebase.User object: https://firebase.google.com/docs/reference/js/firebase.User#updateEmail
Since this is on the user object, your user will already have to be signed in. Hence it only requires the password.
Simple usage:
firebase.auth()
.signInWithEmailAndPassword('you#domain.example', 'correcthorsebatterystaple')
.then(function(userCredential) {
userCredential.user.updateEmail('newyou#domain.example')
})
If someone is looking for updating a user's email via Firebase Admin, it's documented over here and can be performed with:
admin.auth().updateUser(uid, {
email: "modifiedUser#example.com"
});
FOR FIREBASE V9 (modular) USERS:
The accepted answer will not apply to you. Instead, you can do this, i.e., import { updateEmail } and use it like any other import. The following code was copy/pasted directly from the fb docs at https://firebase.google.com/docs/auth/web/manage-users
Happy coding!
import { getAuth, updateEmail } from "firebase/auth";
const auth = getAuth();
updateEmail(auth.currentUser, "user#example.com").then(() => {
// Email updated!
// ...
}).catch((error) => {
// An error occurred
// ...
});
You can do this directly with AngularFire2, you just need to add "currentUser" to your path.
this.af.auth.currentUser.updateEmail(email)
.then(() => {
...
});
You will also need to reauthenticate the login prior to calling this as Firebase requires a fresh authentication to perform certain account functions such as deleting the account, changing the email or the password.
For the project I just implemented this on, I just included the login as part of the change password/email forms and then called "signInWithEmailAndPassword" just prior to the "updateEmail" call.
To update the password just do the following:
this.af.auth.currentUser.updatePassword(password)
.then(() => {
...
});
updateEmail needs to happen right after sign in due to email being a security sensitive info
Example for Kotlin
// need to sign user in immediately before updating the email
auth.signInWithEmailAndPassword("currentEmail","currentPassword")
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success now update email
auth.currentUser!!.updateEmail(newEmail)
.addOnCompleteListener{ task ->
if (task.isSuccessful) {
// email update completed
}else{
// email update failed
}
}
} else {
// sign in failed
}
}
async updateEmail() {
const auth = firebase.auth();
try {
const usercred = await auth.currentUser.updateEmail(this.email.value);
console.log('Email updated!!')
} catch(err) {
console.log(err)
}
}
You can use this to update email with Firebase.
Firebase v9:
const changeEmail = (userInput) => {
const { newEmail, pass } = userInput
signInWithEmailAndPassword(auth, oldEmail, pass)
.then(cred => updateEmail(cred.user, newEmail))
}