Firebase Auth USER_NOT_FOUND - javascript

I am trying to create an account system that creates a new user and then sets the data of the user to Firebase Firestore.
function signUp(email, name, password, theme, privacy){
firebase.auth().createUserWithEmailAndPassword(email, password).then(function(user) {
var userId = user.uid;
firebase.firestore().collection("Users").doc(`${userId}`).set({
name: name,
theme: theme,
privacy: privacy
}).then(() => {
console.log("Document successfully written!");
}).catch((error) => {
console.error("Error writing document: ", error);
});
}).catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log('User did not sign up correctly');
console.log(errorCode);
console.console.log(errorMessage);
});
}
And here are my database rules
match /Users/{userId}/{documents=**} {
allow read, write: if isSignedIn() && isOwnerOfContent();
}
function isSignedIn(){
return request.auth != null;
}
function isOwnerOfContent(){
return request.auth.uid == userId;
}
Error =
{error: {code: 400, message: "USER_NOT_FOUND",…}}
error: {code: 400, message: "USER_NOT_FOUND",…}
code: 400
errors: [{message: "USER_NOT_FOUND", domain: "global", reason: "invalid"}]
0: {message: "USER_NOT_FOUND", domain: "global", reason: "invalid"}
domain: "global"
message: "USER_NOT_FOUND"
reason: "invalid"
message: "USER_NOT_FOUND
Do you see anything wrong?
I also get this error:
signUp.js:135 Error writing document: FirebaseError: Missing or insufficient permissions. at new xr (prebuilt.js:184) at prebuilt.js:10608 at pr. (prebuilt.js:10560) at Kt (eventtarget.js:351) at jt (eventtarget.js:481) at mr.sa (webchannelbasetransport.js:368) at Qe (webchannelbase.js:2219) at Ue (channelrequest.js:822) at xe.N.Ca (channelrequest.js:703) at xe.N.Xa (channelrequest.js:564)

Your isOwnerOfContent uses userId, but that variable is not in scope:
function isOwnerOfContent(){
return request.auth.uid == userId;
}
To make it work, either move isOwnerOfContent inside match /Users/{userId}/{documents=**}, or pass the userId value along in the call:
match /Users/{userId}/{documents=**} {
allow read, write: if isSignedIn() && isOwnerOfContent(userId);
}
...
function isOwnerOfContent(userId){
return request.auth.uid == userId;
}

Related

await is only valid in async functions and the top level bodies of modules javascript express error

This code is to verify firebase authentication. Firstly, it checks the req.headers.Then retrieve uid from the token. After the decodedToken.uid is received, the code will check with its own MySQL database to obtain the id of the user using getID(uid) function. If the uid is not in the database, it will create a new user using the function makeNewUser(). When executed, the code returns an error of "await is only valid in async functions and the top level bodies of modules". How can I fix this? Should I make a new file to handle that stuff and the return from this code should be stored in res.locals? Here is the code.
const admin = require('./config/firebaseAuth'); // import admin from firebase initializeApp
const getId = require('../utils/getUserID'); // module to get userId form MySQL database
const makeNewUser = require('../utils/makeNewUser'); // module to make a new user into MySQL database
class Middleware {
async decodeToken(req,res,next) {
// get authorization from the headers
const { authorization } = req.headers;
// check if the authorization headers are well configured
// this includes checking if headers.authorization exist
// then if the format in headers.authorization matches with the configured
if (!authorization) return res.status(403).json({
status: 'fail',
type: 'server/missing-authorization',
message: 'Missing req.headers.authorization on request to the server. This is need for authorization!'
})
else if (!authorization.startWith('Bearer')) return res.status(400).json({
status: 'fail',
type: 'server/missing-bearer',
message: 'Missing Bearer in req.headers.authorization on request to the server. This is needed to extract the token!'
})
else if (authorization.split(' ').length !== 2) return res.status(400).json({
status: 'fail',
type: 'server/bearer-unrecognized',
message: 'Bearer in req.headers.authorization is not well configured. This is need to extract the token!'
})
// after passing the authorization header checks, now checks the token
const token = authorization.split(' ')[1]; // req.headers = {"Bearer $.token"}
admin.auth().verifyIdToken(token)
.then((decodedToken) => {
const {uid, name} = decodedToken; // get uid and name from the token
try {
// !this produces an error: await is only valid in async functions and the top level bodies of modules
const result = await getId(uid); // getId to get the id of the user regarding the uid
// check if exist uid in the database
if (result.length < 1) {
// if not make a new user
const result = await makeNewUser(uid, name); // make new user from the given uid and name
const id = result.insertId; // get the id of the new user
req.user = {id: id, name: name}; // set id and name to req.user
return next();
}
const id = result[0].id; // getId to get the id of the user from the result query since uid exist
req.user = {id: id, name: name}; // set id and name to req.user
return next();
} catch (err) {
return res.status(500).json({
status: 'fail',
type: 'database/fail-to-query',
message: err.message
})
}
})
.catch((err) => {
/*
on err for firebase tokens, such as sent was FMC token instead of id token or token has expired and many others!
err response: after executing console.log(err)
{
errorInfo: {
code: 'auth/argument-error',
message: 'Decoding Firebase ID token failed. Make sure you passed the entire string JWT which represents an ID token. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.'
},
codePrefix: 'auth'
}
or
{
errorInfo: {
code: 'auth/id-token-expired',
message: 'Firebase ID token has expired. Get a fresh ID token from your client app and try again (auth/id-token-expired). See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.'
},
codePrefix: 'auth'
}
*/
if (err.errorInfo.code === 'auth/internal-error') var statusCode = 500;
else var statusCode = 400;
return res.status(statusCode).json({status: "fail", type: err.errorInfo.code, message: err.errorInfo.message}); // return with status codes
})
}
}
module.exports = new Middleware();
Notes: getId and makeNewUser returns a promise!
use this
const admin = require('./config/firebaseAuth'); // import admin from firebase initializeApp
const getId = require('../utils/getUserID'); // module to get userId form MySQL database
const makeNewUser = require('../utils/makeNewUser'); // module to make a new user into MySQL database
class Middleware {
async decodeToken(req,res,next) {
// get authorization from the headers
const { authorization } = req.headers;
// check if the authorization headers are well configured
// this includes checking if headers.authorization exist
// then if the format in headers.authorization matches with the configured
if (!authorization) return res.status(403).json({
status: 'fail',
type: 'server/missing-authorization',
message: 'Missing req.headers.authorization on request to the server. This is need for authorization!'
})
else if (!authorization.startWith('Bearer')) return res.status(400).json({
status: 'fail',
type: 'server/missing-bearer',
message: 'Missing Bearer in req.headers.authorization on request to the server. This is needed to extract the token!'
})
else if (authorization.split(' ').length !== 2) return res.status(400).json({
status: 'fail',
type: 'server/bearer-unrecognized',
message: 'Bearer in req.headers.authorization is not well configured. This is need to extract the token!'
})
// after passing the authorization header checks, now checks the token
const token = authorization.split(' ')[1]; // req.headers = {"Bearer $.token"}
admin.auth().verifyIdToken(token)
.then( async (decodedToken) => {
const {uid, name} = decodedToken; // get uid and name from the token
try {
// !this produces an error: await is only valid in async functions and the top level bodies of modules
const result = await getId(uid); // getId to get the id of the user regarding the uid
// check if exist uid in the database
if (result.length < 1) {
// if not make a new user
const result = await makeNewUser(uid, name); // make new user from the given uid and name
const id = result.insertId; // get the id of the new user
req.user = {id: id, name: name}; // set id and name to req.user
return next();
}
const id = result[0].id; // getId to get the id of the user from the result query since uid exist
req.user = {id: id, name: name}; // set id and name to req.user
return next();
} catch (err) {
return res.status(500).json({
status: 'fail',
type: 'database/fail-to-query',
message: err.message
})
}
})
.catch((err) => {
/*
on err for firebase tokens, such as sent was FMC token instead of id token or token has expired and many others!
err response: after executing console.log(err)
{
errorInfo: {
code: 'auth/argument-error',
message: 'Decoding Firebase ID token failed. Make sure you passed the entire string JWT which represents an ID token. See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.'
},
codePrefix: 'auth'
}
or
{
errorInfo: {
code: 'auth/id-token-expired',
message: 'Firebase ID token has expired. Get a fresh ID token from your client app and try again (auth/id-token-expired). See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.'
},
codePrefix: 'auth'
}
*/
if (err.errorInfo.code === 'auth/internal-error') var statusCode = 500;
else var statusCode = 400;
return res.status(statusCode).json({status: "fail", type: err.errorInfo.code, message: err.errorInfo.message}); // return with status codes
})
}
}
module.exports = new Middleware();

UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client when adding response

When I add the following line res.status(201).json({ email }); I get the error message UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client. What can I do to fix this problem.
Below is a snippet of my code
module.exports.signup_post = (req, res ) => {
const { firstname, lastname, email, password } = req.body;
handleErrorSignup(firstname.trim(), lastname.trim(), email.trim(), password.trim())
.then( async (errors) => {
if(errors.firstname === '' && errors.lastname === '' && errors.email === '' && errors.password === '') {
const hash = bcrypt.hashSync('password', 10);
try {
await db.none('INSERT INTO users(firstname, lastname, email, password) VALUES($1, $2, $3, $4)', [firstname, lastname, email, hash]);
const token = createToken(email);
res.cookie('jwt', token, { httpOnly: true, maxAge: maxAge * 1000 });
res.status(201).json({ email });
}
catch(err) {
res.status(400).send('Error, user not created');
}
res.redirect('/');
}
else {
res.status(400).json({ errors });
}
});
}
The problem here is that you are sending response from try catch already, so you cannot redirect if you have sent the response already from the earlier parts of your code.
You need to remove this line from your code, or redirect only if response is not already sent in try and catch blocks.
try {
...
res.status(201).json({ email });
} catch (err) {
res.status(400).send('Error, user not created');
}
// Remove below code
res.redirect('/');
If you are looking to redirect to your home screen after signup, you need to handle the same in frontend based on the status code or response received for signup from backend.

Node JS and Angular Email Verification: Anyway to send html in a response?

To start off, I do want to clarify that I know how to use APi's created in NodeJS in Angular. The problem I have is a little tricky.
I have a function that verifies the email used in registering:
exports.confirmEmail = function (req, res) {
ConfirmToken.findOne({
token: req.params.token
}, function (err, token) {
if (err) {
return res.status(500).send({
message: "Internal Server Error " + err
})
}
// token is not found into database i.e. token may have expired
if (!token) {
return res.status(400).send({
message: 'Your verification link may have expired. Please click on resend for verify your Email.'
});
}
// if token is found then check valid user
else {
Account.findOne({
_id: token._accountId,
email: req.params.email
}, function (err, user) {
if (err) {
return res.status(500).send({
message: "Internal Server Error " + err
})
}
// User does not exist
if (!user) {
return res.status(401).send({
message: 'The account does not exist'
});
}
// user is already verified
else if (user.isVerified) {
return res.status(200).send('User has been already verified. Please Login');
}
// verify user
else {
// change isVerified to true
user.isVerified = true;
user.save(function (err) {
// error occur
if (err) {
return res.status(500).send({
message: err.message
});
}
// account successfully verified
else {
return res.status(200).send('Your account has been successfully verified');
}
});
}
});
}
})
}
This is the response I get when I register an account
Now my question is: is there a way to pass in html code or have it show in a custom Angular component instead of displaying as simple plain text on the web browser as such
Your service should send a isVerified status back to the client. You are sending only a string at the moment
return res.status(200).send('Your account has been successfully verified');
based on this status, let's call it, isVerified your angular app would render a isVerfiedComponent.ts or notVerifiedComponent.ts

Getting "undefined" from error.email when trying to link multiple auth providers in firebase

I'm trying to link multiple auth providers to one account using firebase. The user is trying to create an account with the same address as the Google OAuth account which is already on firebase.
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(async result => {
if (result.additionalUserInfo.isNewUser) {
firebase
.database()
.ref('/users/' + result.user.uid)
.set({
email: email,
profile_picture: image,
first_name: firstName,
last_name: lastName,
created_at: Date.now()
})
.then(snapshot => console.log("do something"))
} else {
firebase
.database()
.ref('/users/' + result.user.uid)
.update({
last_logged_in: Date.now()
})
.then(snapshot => console.log("do something"))
}
})
.catch(error => {
if (error.code === 'auth/email-already-in-use' || error.code === 'auth/credential-already-in-use' || error.code === 'auth/account-exists-with-different-credential') {
const pendingCred = error.credential
const email = error.email
firebase
.auth()
.fetchSignInMethodsForEmail(email)
.then(methods => {
switch (methods[0]) {
case 'password':
// email and password logic
break;
case 'facebook.com':
// facebook logic
break;
default:
break;
}
})
return;
}
})
The problem is I'm getting the proper error message:
[Error: The email address is already in use by another account.]
and the proper error.code:
auth/email-already-in-use
but, pendingCred or error.email come back undefined.
Update
I took the advise and tried the following:
firebase.auth()
.EmailAuthProvider
.credential(email, password)
.then(result => console.log("result", result))
.catch(error => console.log(error))
I'm getting the error:
[TypeError: undefined is not an object (evaluating '_firebase.default.auth().EmailAuthProvider.credential')]
You are using createuserwithEmailAndPassword which does not contain error.email or error.credential. According to the documentation to get the error you can either use error.message or error.code:
firebase.auth().createUserWithEmailAndPassword(email, password)
.catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
if (errorCode == 'auth/weak-password') {
alert('The password is too weak.');
} else {
alert(errorMessage);
}
console.log(error);
});
According to the documentation, the error.email and error.credential is used if you get the following error code:
auth/credential-already-in-use
auth/account-exists-with-different-credential
https://firebase.google.com/docs/reference/js/firebase.auth.Auth#error-codes_5
https://firebase.google.com/docs/reference/js/firebase.auth.Auth.html#sign-inwith-credential
The email was in error.customData.email for me.

How to create new user document in firestore

I am trying to implement this logic, when user successfully registers, app creates a document with id=user.email in firestore. For that, I created following security rule in firestore:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth != null;
}
match /users/{userId}{
allow read: if request.auth != null;
allow write: if request.auth.token.email == userId;
}
}
}
and following code in my app:
const { email, password, name, lastName } = value;
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(() => {
firestore.collection('users').doc(email).set({
name, lastName
})
.then(function(docRef) {
console.log("Document written with ID: ", docRef.id);
})
.catch(function(error) {
console.error("Error adding document: ", error);
});
this.props.navigation.navigate('Main')
})
.catch(error => alert(error))
when I run my app, I am getting following error:
Error adding document: , [FirebaseError: Missing or insufficient permissions.]
It's not a good idea to use the email address of the user as a unique identifier, as that can change over time. It's better to use the unique user id (uid) assigned by Firebase Authentication.
Rules:
match /users/{userId} {
allow read: if request.auth != null;
allow write: if request.auth.uid == userId;
}
Code:
firebase
.auth()
.createUserWithEmailAndPassword(email, password)
.then(userCredential => {
firestore.collection('users').doc(userCredential.user.uid).set({
name, lastName
})
This is far more common, makes it easier to write security rules, and is resistant to email changes.

Categories