Email/Password auth provider returns empty userRecord - firebase

I have a cloud function that is triggered by Auth user creation. I look up the user data (email, name, etc) to populate my DB. It suddenly stopped working for the 'email/password' Auth provider type. The admin.auth().getUser(uid) now returns a userRecord which contains undefined/null values for most fields. This seemingly stopped working out of nowhere in Production after functioning for several weeks, is there any possible explanation?
exports.createUser = functions.auth.user().onCreate((user) => {
return createEmailUser(user);
});
function createEmailUser(user) {
const uid = user.uid;
return admin.auth().getUser(uid)
.then(function(userRecord) {
console.log(userRecord);
const email = userRecord.email;
const fullName = userRecord.displayName;
admin.database().ref('users/' + uid).set({
email: email,
name: fullName
});
})
.catch(function(error) {
console.log("Error fetching user data:", error);
});
}
In the past, the userRecord object contains valid email and displayName values. Now, I see an object like this:
UserRecord {
uid: 'Lrtj8zafsnYjZl4ckMgwNkgEiVH2',
email: undefined,
emailVerified: false,
displayName: undefined,
photoURL: undefined,
phoneNumber: undefined,
disabled: false,
metadata:
UserMetadata {
creationTime: 'Wed, 09 Jan 2019 21:40:31 GMT',
lastSignInTime: null },
providerData: [],
passwordHash: undefined,
passwordSalt: undefined,
customClaims: undefined,
tokensValidAfterTime: 'Wed, 09 Jan 2019 21:40:31 GMT' }

As users are registered with Email/Password method, then there is only the email address available in userRecord. Other sign-in providers might have different data at user creation.
What you can do here is to check user data at profile creation, and update profile with updateUser if anything is missing:
function createEmailUser(user) {
const uid = user.uid;
admin.auth().updateUser(uid, {
phoneNumber: "+11234567890",
displayName: "Foo Bar"
})
.then(function(userRecord) {
console.log(userRecord);
})
.catch(function(error) {
console.log("Error fetching user data:", error);
});
}

Related

Firebase.default.auth().currentUser.uid is null after createUserWithEmailAndPassword()

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 }));

Using vuex, firestore, and createUserWithEmailAndPassword, how do I create a user profile collection when a user registers?

For the app I'm building, I want my users to have a profile created for them when they register; the profile would contain the user's username, email, and the uid created by firebase authentication. I've got the authentication portion using createUserWithEmailAndPassword to work on its own. I'm also able to create a "users" collection, capturing a username and the user's email, on its own as well. However, I'm running into trouble grabbing and saving the uid to the user's profile in the user's collection.
Here is the code I have at the moment:
import * as firebase from "firebase/app";
import db from "../../components/firebase/firebaseInit";
actions: {
registerUser({ commit }, payload) {
commit("setLoading", true);
commit("clearError");
firebase
.auth()
.createUserWithEmailAndPassword(payload.email, payload.password)
.then(user => {
commit("setLoading", false);
const newUser = {
email: user.email,
id: user.uid,
courses: []
};
commit("setUser", newUser);
db.collection("users")
.add({
username: payload.username,
email: user.email,
userId: user.uid
})
.then(() => {
console.log("New user added!");
})
.catch(err => {
console.log(err);
});
})
.catch(err => {
commit("setLoading", false);
commit("setError", err);
});
},
In the research I've done, I've found these suggested solutions:
Get Current User Login User Information in Profile Page - Firebase and Vuejs
Cloud Firestore saving additional user data
And this video:
https://www.youtube.com/watch?v=qWy9ylc3f9U
And I have tried using the set() method instead of add(), as well.
But none of them seem to work, for me at least.
Thank you in advance for your help.
And if you need to see any more code, just let me know.
You haven't shared the error message you get, but most probably the error comes from the fact that the createUserWithEmailAndPassword() method returns a UserCredential and not a User.
So you have to do as follows:
import * as firebase from "firebase/app";
import db from "../../components/firebase/firebaseInit";
actions: {
registerUser({ commit }, payload) {
commit("setLoading", true);
commit("clearError");
firebase
.auth()
.createUserWithEmailAndPassword(payload.email, payload.password)
.then(userCredential=> {
commit("setLoading", false);
const user = userCredential.user; // <-- Here is the main change
const newUser = {
email: user.email,
id: user.uid,
courses: []
};
commit("setUser", newUser);
return db.collection("users")
.add({
username: payload.username,
email: user.email,
userId: user.uid
});
})
.then(() => {
console.log("New user added!");
})
.catch(err => {
commit("setLoading", false);
commit("setError", err);
});
},

Firebase set method not adding data to an existing Collection

I am using VUEX and Firebase to create a register form with three fields NAME, EMAIL, PASSWORD. First i am using createUserWithEmailAndPassword method to add the user but I also want to ad the name filed data to an Existing Blank collection, here I am using set method. But it is not adding the name field data in the collection.
methods: {
onSignUp() {
firebase
.auth()
.createUserWithEmailAndPassword(this.email, this.password)
.then(user => {
console.log(user);
console.log(user.user.uid);
firebase.database.collection("profiles").doc(user.user.id).set({
name: this.name
})
.then(function() {
console.log("Document successfully written!");
})
.catch(function(error) {
console.error("Error writing document: ", error);
});
this.$store.dispatch('signUserUp', user);
})
.catch(error => {
this.$store.dispatch('signUserError', error)
});
}
}
data(){
return {
name: "",
email: "",
password: "",
}
}
After submitting the form it's adding a new user and I can also see the uid in the console but some how its not updating the name field in the database.
You should use firebase.firestore() and not firebase.database (See this doc) and therefore adapt your code as follows:
onSignUp() {
firebase
.auth()
.createUserWithEmailAndPassword(this.email, this.password)
.then(user => {
console.log(user);
console.log(user.user.uid);
return firebase.firestore().collection("profiles").doc(user.user.id).set({
name: this.name
});
})
.then(() => {
console.log("Document successfully written!");
this.$store.dispatch('signUserUp', user);
})
.catch(error => {
this.$store.dispatch('signUserError', error)
});
}
}
You should also check that your security rules for the profiles collection are correctly set. Normally (authenticated) users should only be able to write a document with their own userid as document id.

Error in event handler for "click": "ReferenceError: user is not defined"

Please help me. I have been working for days trying to fix this error and keep ending back up at the same click handler error "user not defined" I just can't figure out what it means... this is a vuejs/vuex/vuetify/firestore project. Please any help would be appreciated.
relevant code from store/index.js
updateUser({ commit, state },
payload) {
commit('setLoading', true);
commit('clearError');
firebase
.firestore()
.collection('users').doc(`users/${user.uid}`).add()
.then((data, payload) => {
const key = data.key;
let displayName = payload.name;
let address = payload.address;
let city = payload.city;
let state1 = payload.state1
let zip = payload.zip
let country = payload.country
let company = payload.company
let email = payload.email
let phoneNumber = payload.phone
commit('updateUser', {
id: key,
name: displayName,
phone: phoneNumber,
email: email,
address: address,
city: city,
state1: state1,
zip: zip,
country: country,
company: company
})
}).catch(err => {
console.log(err)
})
},
Component code...
methods: {
onUpdate() {
this.$store.dispatch("updateUser", {
email: this.email,
name: this.name,
company: this.company,
address: this.address,
city: this.city,
state1: this.state1,
zip: this.zip,
country: this.zip,
phone: this.phone,
});
},
Assuming user is a state property, ie
state: {
loading: false,
errors: [],
user: null // 👈 here
},
then your Firestore docref should be
.doc(`users/${state.user.uid}`)
Since your action performs asynchronous operations, you should consider making it composable by having it return the promise, eg
updateUser({ commit, state }, payload) {
commit('setLoading', true)
commit('clearError')
return firebase.firestore()... // 👈 note the "return" here

How to verify email/password credentials using Firebase Admin SDK (server-side)?

I have written a Google Cloud Function Express app and a command-line tool that uses Node.js on my local Mac.
Calling myclitool login, a one-time prompt asks the user for their email and password. The CLI tool sends the email and password inside the request body using an HTTP POST request to the Express server, over SSL.
The server will send back a private API Key (generated by a trigger function at the time the user was registered) that will be written to ~/.myclitoolrc and will be used for all subsequent calls to my API endpoint.
Each subsequent call from the CLI tool will lookup the private API Key in the Firestore accounts collection, and authenticate on per API call basis.
admin.firestore()
.collection('accounts')
.where('privateApiKey', '==', privateApiKey)
.get() // and so on
So far, the following code will locate the admin.auth.UserRecord.
Service.prototype.signin = function signin(email, password) {
return new Promise(function(resolve, reject) {
admin.auth().getUserByEmail(email)
.then(userRecord => {
console.log(userRecord);
resolve('some value later');
})
.catch(err => {
reject(err);
});
});
};
The Firebase documentation says:
https://firebase.google.com/docs/reference/admin/node/admin.auth.UserRecord
passwordHash (string or null)
The user’s hashed password (base64-encoded), only if Firebase Auth
hashing algorithm (SCRYPT) is used. If a different hashing algorithm
had been used when uploading this user, as is typical when migrating
from another Auth system, this will be an empty string. If no password
is set, this will be null. This is only available when the user is
obtained from listUsers().
passwordSalt (string or null)
The user’s password salt (base64-encoded), only if Firebase Auth
hashing algorithm (SCRYPT) is used. If a different hashing algorithm
had been used to upload this user, typical when migrating from another
Auth system, this will be an empty string. If no password is set, this
will be null. This is only available when the user is obtained from
listUsers().
The UserRecord is retrieved and contains the SCRYPTd passwordHash and passwordSalt properties.
UserRecord {
uid: 'kjep.[snip]..i2',
email: 'email#example.com',
emailVerified: false,
displayName: undefined,
photoURL: undefined,
phoneNumber: undefined,
disabled: false,
metadata:
UserMetadata {
creationTime: 'Thu, 12 Apr 2018 09:15:23 GMT',
lastSignInTime: 'Thu, 03 May 2018 03:57:06 GMT' },
providerData:
[ UserInfo {
uid: 'email#example.com',
displayName: undefined,
email: 'email#example.com',
photoURL: undefined,
providerId: 'password',
phoneNumber: undefined } ],
passwordHash: 'U..base64..Q=',
passwordSalt: undefined,
customClaims: undefined,
tokensValidAfterTime: 'Thu, 12 Apr 2018 09:15:23 GMT' }
There appears to be no verification functions as part of the Firebase Admin SDK admin.auth().
Should I implement the SCRYPT verification myself by finding an algorithm or ready-made Node module, or should I take the absence of any verification functions as a sign that this is not the best approach?
If so, please recommend a better design, bearing in mind this is a prototype project and to implement full Oauth2 would be quite time consuming.
As requested in the comments, here is some example code for accessing Cloud Firestore using Node.js via the Firebase Javascript SDK (enforces security rules).
There is a bug filed in v4.13.0 (now closed). I haven't tested 4.13.1 yet, but the fix has been merged into the master branch. If it doesn't work, you should try v4.12.0.
const firebase = require('firebase');
require("firebase/firestore");
// Initialize Firebase
// You get these details from the Firebase Console
let config = {
apiKey: "yourAPIkey",
authDomain: "yourAuthDomain",
databaseURL: "https://yourProjectID.firebaseio.com",
projectId: "yourProjectID",
messagingSenderId: "yourId"
};
firebase.initializeApp(config);
let email = 'yourUser#example.com';
let password = 'yourVerySecurePassword';
firebase.auth().signInWithEmailAndPassword(email, password)
.catch(error => {
console.log(error);
});
firebase.auth().onAuthStateChanged((user) => {
if (user) {
console.log('I am logged in');
// Initialise Firestore
const firestore = firebase.firestore();
const settings = {timestampsInSnapshots: true};
firestore.settings(settings);
return firestore
.collection('accounts')
.where('privateApiKey', '==', privateApiKey)
.get()
.then((querySnapshot) => {
querySnapshot.forEach((documentSnapshot) => {
if (documentSnapshot.exists) {
console.log(documentSnapshot.id);
}
});
});
} else {
// User is signed out.
// ...
}
});

Resources