I'm using ionic2 + firebase now.
After I createUserWithEmailAndPassword, I tried to update the displayName using firebase.auth().currentUser.updateProfile
However, when I try to log firebase.auth().currentUser.providerData, the displayName is still null.
Anyone can tell me what did I overlook?
Below is my code;
const name = form.value.name //e.g james
var user = firebase.auth().currentUser
console.log('name: '+name) //manage to print "name: james"
user.updateProfile({
displayName: name,
photoURL: ''
}).then(function(){
console.log(JSON.stringify(user.providerData))
//it print "displayName":null
}, function(error){
console.log(error)
});
Related
I'm trying to create a new document on my "users" collection on Firebase for any new user created on the Signup screen for my React-Native app, and the document is supposed to include the new user's uid, first name, last name, phone number, and date of birth. The issue is, after I use createUserWithEmailAndPassword to create a user, when I try to grab the uid with currentUser.uid, I get the following error: null is not an object (evaluating '_Firebase.default.auth().currentUser.uid').
I've been experimenting with ways to get the new user's "uid" in the .then statement following createUserWithEmailAndPassword and also creating the new document within the .then statement but I haven't gotten any luck with that yet. How should I modify my code so that I'm able to successfully create a new "users" document after successfully creating a user?
Below is my code from my handleSignUp function that is called when my "Sign Up" button is clicked:
handleSignUp = () => {
Firebase.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.password)
.then(() => this.props.navigation.navigate("Main"))
.catch((error) => this.setState({ errorMessage: error.message }));
if (Firebase.auth().currentUser.uid) {
const user = {
uid: Firebase.auth().currentUser.uid,
firstName: this.state.firstName,
lastName: this.state.lastName,
phone: this.state.phone,
email: this.state.email,
dob: this.state.dob
};
db.collection("users").doc(response.user.uid).set(user);
}
};
If you want to:
Create a user
Write their details to the database
Navigate to a new page
You'll need to make sure you do these steps in that order, and use the promises returned by each step to make sure things happen at the right time:
Firebase.auth()
.createUserWithEmailAndPassword(this.state.email, this.state.password)
.then((credentials) => {
const user = {
uid: credentials.user.uid,
firstName: this.state.firstName,
lastName: this.state.lastName,
phone: this.state.phone,
email: this.state.email,
dob: this.state.dob
};
return db.collection("users").doc(response.user.uid).set(user);
}).then(() => {
this.props.navigation.navigate("Main")
}).catch((error) => this.setState({ errorMessage: error.message }));
EDIT**
Ok so I was able to get the parameters working thanks to first answer provided but now I have an issue whereby my function is creating a new user entirely in Firebase and not update an existing one, the uid that i am passing into the auth.admin.updateUser is teh uid of the existing user who's email i want to update. Here is the updated cloud function which is adding a new user rather than updating the existing:
exports.updateEmail = functions.https.onCall((data, context) => {
const email = data.email;
const uid = data.uid;
admin.auth().updateUser(uid, {
email: email
})
.then(function(userRecord) {
// See the UserRecord reference doc for the contents of userRecord.
console.log("Successfully updated user", userRecord.toJSON());
return response.status(200).json(userRecord.toJSON());
})
.catch(function(error) {
console.log("Error updating user:", error);
return response.status(404).json({
error: 'Something went wrong.'
});
});
});
I got the function from the firebase docs but it isn't doing what I intended it to do.
ORIGINAL POST**
I'm having some difficulty getting a cloud function to work when calling the function from within my flutter code. The issue that I am having is that the uid and email fields are undefined even though I am passing them through to the cloud function using busboy fields.
I'm trying to pass the email and uid field though to the function as follows:
final request = http.MultipartRequest('POST', Uri.parse('****************my function url************'));
request.fields['email'] = Uri.encodeComponent(newEmail);
request.fields['uid'] = Uri.encodeComponent(selectedUser.uid);
request.headers['Authorization'] = 'Bearer ${_authenticatedUser.token}';
final http.StreamedResponse streamedResponse = await request.send();
And on the Node.js side I am trying to use these fields using busboy, here is my cloud function in Node.js:
exports.changeEmail = functions.https.onRequest((request, response) => {
if (!request.headers.authorization ||
!request.headers.authorization.startsWith('Bearer ')
) {
return response.status(401).json({
error: 'Unauthorized.'
});
}
let idToken;
idToken = request.headers.authorization.split('Bearer ')[1];
let email;
let uid;
const busboy = new Busboy({
headers: request.headers
});
busboy.on('field', (fieldname, value) => {
if (fieldname == 'email') {
email = decodeURIComponent(value);
}
if (fieldname == 'uid') {
uid = decodeURIComponent(value);
}
});
admin.auth().updateUser(uid, {
email: email
})
.then(function(userRecord) {
// See the UserRecord reference doc for the contents of userRecord.
console.log("Successfully updated user", userRecord.toJSON());
return response.status(200).json(userRecord.toJSON());
})
.catch(function(error) {
console.log("Error updating user:", error);
return response.status(404).json({
error: 'Something went wrong.'
});
});
});
Even though I am passing the fields in with busboy fields they are not getting set in the function, is there something I am doing wrong here?
Why don't you use a callable function? It will automatically receive the authentication data.
The documentation even has examples on how to get the uid and email:
Declare the function:
exports.addMessage = functions.https.onCall((data, context) => {
// ...
});
Get the user properties from the context parameter:
// Message text passed from the client.
const text = data.text;
// Authentication / user information is automatically added to the request.
const uid = context.auth.uid;
const name = context.auth.token.name || null;
const picture = context.auth.token.picture || null;
const email = context.auth.token.email || null;
Call the function from your Flutter code:
Install cloud_functions package and then:
import 'package:cloud_functions/cloud_functions.dart';
await CloudFunctions.instance.call(functionName: 'addMessage');
If the user is authenticated before calling the function that's all you need to do.
You can also pass additional parameters to the function:
await CloudFunctions.instance.call(functionName: 'addMessage', parameters: {"email": "whatever#example.com"});
Any parameters will be passed to the data parameter on the function side.
I am trying to updatePhoneNumber and keep getting the following error above. Looking at the docs I was under the impressing this is a method?
Js:
user.updatePhoneNumber({
phoneNumber: "+15618104444",
});
I tried setting this in the updateProfile Method as well and still no luck.
user.updateProfile({
displayName: displayName,
photoURL: photoURL,
phoneNumber: "+15618104444"
});
updatePhoneNumber requires a phone credential since the phone number needs to be verified by SMS.
// 'recaptcha-container' is the ID of an element in the DOM.
var applicationVerifier = new firebase.auth.RecaptchaVerifier(
'recaptcha-container');
var provider = new firebase.auth.PhoneAuthProvider();
provider.verifyPhoneNumber('+16505550101', applicationVerifier)
.then(function(verificationId) {
var verificationCode = window.prompt('Please enter the verification ' +
'code that was sent to your mobile device.');
return firebase.auth.PhoneAuthProvider.credential(verificationId,
verificationCode);
})
.then(function(phoneCredential) {
return user.updatePhoneNumber(phoneCredential);
});
How do I get the verification link from the default Meteor method into a custom email method I have that uses sendgrid stmp.
here is the meteor verification method that works well on its own and has the link I want:
sendVerificationLink() {
let userId = Meteor.userId();
if ( userId ) {
return Accounts.sendVerificationEmail( userId );
}
},
Here is my custom method that uses sendgrid, everything works except for I cant figure out how to get the link with the custom token:
'signupEmail' (submission) {
this.unblock();
const link = ''
const message = `welcome ${submission.firstname} `
const text = `welcome ${submission.firstname}. Please verify your
account ${link}`
Email.send({
from: "hi#test.com",
to: submission.email,
subject: message,
text: text,
html: text,
});
}
Just in case anyone is looking for this in the future I found an answer on Meteor forums: https://forums.meteor.com/t/how-to-get-verification-link-for-custom-sent-verification-email/22932/2
Basically I added a token record and saved it in the database. Then used the token with the method: Accounts.urls.verifyEmail which created the link to insert in the email.
Here is my final method:
'signupEmail' (submission) {
this.unblock();
let userId = Meteor.userId();
var tokenRecord = {
token: Random.secret(),
address: submission.email,
when: new Date()};
Meteor.users.update(
{_id: userId},
{$push: {'services.email.verificationTokens': tokenRecord}}
);
const verifyEmailUrl = Accounts.urls.verifyEmail(tokenRecord.token);
const message = `welcome ${submission.firstname} `
const text = `welcome ${submission.firstname}. Please verify your account ${verifyEmailUrl}`
Email.send({
from: "hi#test.com",
to: submission.email,
subject: message,
text: text,
html: text,
});
},
Firebase v3 Auth offers an updateProfile method that passes displayName and photoURL to Firebase.
My understanding is that these properties are retrieved from 3rd party oAuth providers Google, Facebook, Twitter, or GitHub upon user login. In case of Password based Auth, they are not available or viewable from the Admin console.
Can I store this info for password Auth accounts, and if so can I view/administer this info via the Admin console?
BTW: I know this could be stored in the Realtime Database under a users node/branch but I am asking about storing this info in the Firebase Auth system.
// Updates the user attributes:
user.updateProfile({
displayName: "Jane Q. User",
photoURL: "https://example.com/jane-q-user/profile.jpg"
}).then(function() {
// Profile updated successfully!
// "Jane Q. User"
var displayName = user.displayName;
// "https://example.com/jane-q-user/profile.jpg"
var photoURL = user.photoURL;
}, function(error) {
// An error happened.
});
// Passing a null value will delete the current attribute's value, but not
// passing a property won't change the current attribute's value:
// Let's say we're using the same user than before, after the update.
user.updateProfile({photoURL: null}).then(function() {
// Profile updated successfully!
// "Jane Q. User", hasn't changed.
var displayName = user.displayName;
// Now, this is null.
var photoURL = user.photoURL;
}, function(error) {
// An error happened.
});
.updateProfile stores the displayName and photoURL properties in the Firebase Auth system. Therefore, there is no need to set/get this stuff under a users node in your Realtime Database.
You will not see these properties in the Firebase v3 Auth Console. It's not viewable that way.
Rolled into one, here how to register a password user:
registerPasswordUser(email,displayName,password,photoURL){
var user = null;
//nullify empty arguments
for (var i = 0; i < arguments.length; i++) {
arguments[i] = arguments[i] ? arguments[i] : null;
}
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(function () {
user = firebase.auth().currentUser;
user.sendEmailVerification();
})
.then(function () {
user.updateProfile({
displayName: displayName,
photoURL: photoURL
});
})
.catch(function(error) {
console.log(error.message);
});
console.log('Validation link was sent to ' + email + '.');
}