Firebase Ionic - Returning Error - firebase

I have a condition where I am checking if username is taken or not. If not, then I check if email is registered or not (if not, then proceed with account creation). I am separating firebase functions from business logic...but I am unable to figure out how to return error from createUserWithEmailAndPassword() to page class to handle further.
if(usernameTaken) {
}else{
/*Username is available - Check email taken or not (if not, register)*/
var error = self.firebaseProvider.registerUser(self.email,self.password,self.username, self.name);
if(error.code !== ""){
if(error.code == "auth/email-already-in-use"){
let alert = self.alertCtrl.create({
title: 'Email Exists',
subTitle: 'The email you entered is already registered.',
buttons: ['Retry']
});
alert.present();
}else if(error.code == 'auth/weak-password') {
let alert = self.alertCtrl.create({
title: 'Validation Error',
subTitle: 'The password entered is weak and should be 6 characters.',
buttons: ['Retry']
});
alert.present();
}
}
}
And here is the code in firebase.ts:
registerUser(email: string, password: string, username: string, name: string): (any) {
var self = this;
this.fbAuth.auth.createUserWithEmailAndPassword(email, password).then(function() {
var user = firebase.auth().currentUser;
self.createUserRecord(username, email, name);
}).catch(function(error) {
//Handle error
return error;
});
}
The returned error is null (undefined) hence nothing is working as expected. Please help.

Your code needs a bit of refactoring, your parent call is expecting a promise so var error is going to by a thenable object, it's not going to have a response of err.code.
Also returning an error in your registerUser class as a successful response can be difficult to debug so it's best to made the catch handle on the parent call.
if (usernameTaken) {
} else
/*Username is available - Check email taken or not (if not, register)*/
self.firebaseProvider.registerUser(self.email,self.password,self.username, self.name)
.then(resp => {
// Do Something
})
.catch(err => {
if (error.code !== "") {
if(error.code == "auth/email-already-in-use"){
let alert = self.alertCtrl.create({
title: 'Email Exists',
subTitle: 'The email you entered is already registered.',
buttons: ['Retry']
});
alert.present();
} else if(error.code == 'auth/weak-password') {
let alert = self.alertCtrl.create({
title: 'Validation Error',
subTitle: 'The password entered is weak and should be 6 characters.',
buttons: ['Retry']
});
alert.present();
}
}
});
}
You need to add a return in front of the this.fbAuth.auth.createUserWithEmailAndPassword call.
registerUser(email: string, password: string, username: string, name: string): Promise<any> {
var self = this;
return this.fbAuth.auth.createUserWithEmailAndPassword(email, password)
.then(function() {
var user = firebase.auth().currentUser;
return self.createUserRecord(username, email, name);
});
}
Now if the function throws an error it's handled in the parent class and you can see the difference between createUserRecord and an error.

Related

Sequelize error handling & code optimization

My use case is fairly simple, i want to create a new user (username,email,password) but first check that the username/email doesn't exist already.
After checking, i use bcrypt to hash the password and create/store the user in my database
Here is the code i'm actually using, it works but i think it's a little too complicated so i'm wondering if there is anything i can do to make it a bit more readable/optimized
ipcMain.on('register', (e, newUser) => {
userRepo.findByUsername(newUser.username).then(
(user) => {
if (user)
e.sender.send('register-failed', "Username already exists!");
else {
userRepo.findByEmail(newUser.email).then(
(user) => {
if (user)
e.sender.send('register-failed', "Email already exists!");
else {
bcrypt.hash(newUser.password, saltRounds).then(
(hasedPassword) => {
newUser.password = hasedPassword;
userRepo.create(newUser).then(
(user) => {
e.sender.send('register-success', user.get({plain:true}));
},
(error) => {
e.sender.send('register-failed', "Unexpected Error");
}
)
}
)
}
}
)
}
},
(error) => { e.sender.send('register-failed', "Unexpected Error"); }
).catch(error => e.sender.send('register-failed', "Unexpected Error"));
});
userRepo module :
const db = require('../db.js');
const findByUsername = function (username) {
return db.models.User.findOne({
where: {
username: username
}
});
}
const findByEmail = function (email) {
return db.models.User.findOne({
where: {
email: email
}
});
}
const create = function (newUser) {
return db.models.User.create({
username: newUser.username,
email: newUser.email,
password: newUser.password
});
}
module.exports = { findByUsername, findByEmail, create }
Thanks for the help.
EDIT:
Here's a much more readable code (that may be optimized more, but i find it readable enough)
ipcMain.on('register', (e, newUser) => {
Promise.all([userRepo.isUsernameAvailable(newUser.username), userRepo.isEmailAvailable(newUser.email)])
.then(creation => {
bcrypt.hash(newUser.password, saltRounds).then(
(hashedPassword) => {
newUser.password = hashedPassword;
userRepo.create(newUser).then(
(user) => {
e.sender.send('register-success', user.get({ plain: true }));
}
).catch(error => e.sender.send('register-failed', "Unexpected internal error!"))
}
).catch(error => e.sender.send('register-failed', "Unexpected internal error!"));
}) // User already exists
.catch((exists) => e.sender.send('register-failed', exists))
})
Using the two functions to check availability of username and email
async function isUsernameAvailable(username){
const user = await findByUsername(username);
if(!user)
return Promise.resolve(`Username : "${username}" is available`)
return Promise.reject(`Username : "${username}" is already taken !`)
}
async function isEmailAvailable(email){
const user = await findByEmail(email);
if(!user)
return Promise.resolve(`Email : "${email}" is available`)
return Promise.reject(`Email : "${email}" is already taken !`)
}
You are first checking whether username exists and then checking whether email exists. However, both of them can be checked asynchronously using Promise.all.
If anyone of the username or email already exists then you can return an error message.
You have used a promise chain however code looks much cleaner and easy to read if you implement the same code using async-await.
Please refer the below blog for how to implement Promise.all using async-await.
https://www.taniarascia.com/promise-all-with-async-await/

Send email when user is created on firestore using Cloud Functions

I'm trying to send the email verification link after the user is created on my flutter app, but the email isn't sent and in my Cloud Functions Log I'm receiving the message when I deploy:
{"#type":"type.googleapis.com/google.cloud.audit.AuditLog","status":{"code":9,"message":"FAILED_PRECONDITION"},"authenticationInfo":{"principalEmail":"*************"},"requestMetadata":{"callerIp":"186.216.140.62","callerSuppliedUserAgent":"FirebaseCLI/6.5.0,gzip(gfe),gzip(gfe)","requestAttributes":{"time":"2019-03-29T23:21:10.130Z","auth":{}},"destinationAttributes":{}},"serviceName":"cloudfunctions.googleapis.com","methodName":"google.cloud.functions.v1.CloudFunctionsService.UpdateFunction","authorizationInfo":[{"permission":"cloudfunctions.functions.update","granted":true,"resourceAttributes":{}},{"resource":"projects/pppppp-9800a/locations/us-central1/functions/sendVerificationEmail","permission":"cloudfunctions.functions.update","granted":true,"resourceAttributes":{}}],"resourceName":"projects/pppppp-9800a/locations/us-central1/functions/sendVerificationEmail","request":{"#type":"type.googleapis.com/google.cloud.functions.v1.UpdateFunctionRequest","function":{"labels":{"deployment-tool":"cli-firebase"},"eventTrigger":{"eventType":"providers/cloud.firestore/eventTypes/document.create","resource":"projects/pppppp-9800a/databases/(default)/documents/users/{userId}","service":"firestore.googleapis.com"},"sourceUploadUrl":"https://storage.googleapis.com/gcf-upload-us-central1-dc1829cf-3a07-4951-be81-1a15f892ed8d/8ea3f162-c860-4846-9064-04a855efca2f.zip?GoogleAccessId=service-73683634264#gcf-admin-robot.iam.gserviceaccount.com&Expires=1553903464&Signature=******************","name":"projects/pppppp-9800a/locations/us-central1/functions/sendVerificationEmail"}}}
My code:
exports.sendVerificationEmail = functions.firestore.document('users/{userId}').onCreate((snap, context) => {
const user = snap.data();
console.log("----------------------");
console.log("user created: " + user.uidColumn);
admin.auth().generateEmailVerificationLink(user.email).then((link) => {
console.log("**********" + link);
sendVerificationEmail(user.emailColumn, link);
return 0;
}).catch(e => {
console.log(e);
})
return 0;
});
function sendVerificationEmail(email, link) {
var smtpConfig = {
host: 'smtp.gmail.com',
port: 465,
secure: true, // use SSL
auth: {
user: 'myappemail#gmail.com',
pass: 'password'
}
};
var transporter = nodemailer.createTransport(smtpConfig);
var mailOptions = {
from: "qeaapp#gmail.com", // sender address
to: email, // list of receivers
subject: "Email verification", // Subject line
text: "Email verification, press here to verify your email: " + link,
html: "<b>Hello there,<br> click here to verify</b>" // html body
};
transporter.sendMail(mailOptions, function (error, response) {
if (error) {
console.log(error);
} else {
console.log("Message sent: " + response.message);
}
return 0;
});
return 0;
}
When I the the command firebase deploy I get the message functions: failed to update function sendVerificationEmail
HTTP Error: 400, Change of function trigger type or event provider is not allowed
I'm new in JS and I don't know what these erros mean
Delete your first function called sendVerificationEmail, then redeploy. It looks like you maybe initially deployed it as something other than a Firestore trigger.

Login page lets user get inside the app even if the authentication failed

I've made a simple app with phone authentication (sms).
My problem splits to two, the first part is that the verification code (sms) is always wrong somehow (I do get it, however it doesn't pass the confirmation), and the second part (as stated in the title) is that the user can still access the main activities even if authentication failed.
the function is invoked via a button.
the function is :
signIn(){
const appVerifier = this.recaptchaVerifier;
const phoneNumberString = "+972" + this.phoneNumber.substring(1,10);
firebase.auth().signInWithPhoneNumber(phoneNumberString, appVerifier)
.then( confirmationResult => {
// SMS sent. Prompt user to type the code from the message, then sign the
// user in with confirmationResult.confirm(code).
let prompt = this.alertCtrl.create({
title: 'Enter the Confirmation code',
inputs: [{ name: 'confirmationCode', placeholder: 'Confirmation Code' }],
buttons: [
{ text: 'Cancel',
handler: data => { console.log('Cancel clicked'); }
},
{ text: 'Send',
handler: data => {
confirmationResult.confirm(data.confirmationCode)
.then(function (result) {
// User signed in successfully.
this.uid = result.user.uid
this.addUser(this.fullName, this.uid);
console.log(result.user);
// ...
}).catch(function (error) {
console.log("Invalid code") // always getting here
});
}
}
]
});
prompt.present();
}).catch(function (error) {
console.log("SMS not sent")
});
}
UPDATE (app.component)
the decision is made in the constructor of app.component.ts
constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
var that = this
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
statusBar.styleDefault();
splashScreen.hide();
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
that.rootPage = TabsPage; // even though auth failed, he comes here
} else {
that.rootPage = LoginPage;
}
});
});
}
I dont see it in your code but anywhere you call a method to push the main App-Page. You only should show the main App-Page after User successfully logged in. If this dont work maybe the user comes inside of your app, because the Firebase function is asynchron.

Nuxt SSR auth guard with Firebase auth

I'm trying to implement auth guards in Nuxt with Firebase Auth, but I keep running in to problems. At the moment I'm able to login, but the correct page isn't loaded after login, after login the user should be redirected to the '/profile-overview' page but that doesn't happen. When I navigate away from the 'profile' page to another page and then go back I do automatically go to the 'profile-overview' page. So the login works, there is just something wrong with the navigation / refresh of the page after login. Also when I refresh the page the user is logged out again, I would except the user to still be logged in then?
My code so far:
Page:
loginGoogle () {
this.$store.dispatch('signInWithGoogle').then(() => {
console.log('reload')
location.reload()
//window.location.reload(true)
}).catch((e) => {
this.title = 'Google login failed'
this.message =
"Something went wrong, please try again later. If this problem keeps happening please contact: jonas#co-house.be " + "Error: " + e.message;
this.dialog = true;
})
},
Middleware:
export default function ({ store, redirect, route }) {
console.log('user state' + store.state.user)
console.log('route ' + route.name)
store.state.user != null && route.name == 'profile' ? redirect('/profile-overview') : ''
store.state.user == null && isAdminRoute(route) ? redirect('/profile') : ''
}
function isAdminRoute(route) {
if (route.matched.some(record => record.path == '/profile-overview')) {
return true
}
}
Plugin:
import { auth } from '#/services/fireInit.js'
export default context => {
const { store } = context
return new Promise((resolve, reject) => {
auth.onAuthStateChanged(user => {
if (user) {
return resolve(store.commit('setUser', user))
}
return resolve()
})
})
}
Store (function to login only:
signInWithGoogle ({ commit }) {
return new Promise((resolve, reject) => {
auth.signInWithPopup(GoogleProvider).then((result) => {
// This gives you a Google Access Token. You can use it to access the Google API.
var token = result.credential.accessToken;
// The signed-in user info.
var user = result.user;
return resolve(store.commit(state.user, result.user))
// ...
}).catch((error) => {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
// ...
})
})
},
Does anyone have any idea what I could be doing wrong, or some documentation / tutorial I could read?
Thanks in advance.
You need to init your user on server in nuxtServerInit. See this repo for example implementation https://github.com/davidroyer/nuxt-ssr-firebase-auth.v2

Meteor showing Accounts.createUser errors

I have the following code in my Meteor app where I create new users, assign them 'basic' role. Yet I am having a trouble showing on the client side errors returned while processing Accounts.createUser, can someone please tell me how I can return errors returned by Accounts.createUser while having it on the server as my code below. Thanks
/server/users.js
Meteor.methods({
'createMemberAccount': function (data, role) {
var userId;
Meteor.call('createNewAccount', data, function(err, result) {
if (err) {
return err;
}
console.log('New account id: '+ result);
Roles.addUsersToRoles(result, role);
return userId = result;
});
return userId;
},
'createNewAccount': function (adminData) {
return Accounts.createUser({email: adminData.email, password : adminData.password, roles: adminData.roles});
}
});
/client/signup.js
Template.signupForm.events({
'submit #signup-form': function(e, t){
e.preventDefault();
var userData = {};
userData.email = $(e.target).find('[name=email]').val();
userData.password = $(e.target).find('[name=password]').val();
userData.roles = ['basic'];
Meteor.call('createMemberAccount', userData, 'basic', function(err, userId) {
if (!err) {
console.log('All OK');
} else {
console.log('Error: ' + err.message);
}
});
return false;
}
});
Since You are creating an static rol "basic", you don't need to do that pair of methods, and Meteor.calls, instead you can use
So, use the v on the client side, just like this.
Template.register.events({
'submit #register-form' : function(e, t) {
e.preventDefault();
var email = t.find('#account-email').value
, password = t.find('#account-password').value;
// Trim and validate the input
Accounts.createUser({email: email, password : password}, function(err){
if (err) {
// Inform the user that account creation failed
} else {
// Success. Account has been created and the user
// has logged in successfully.
}
});
return false;
}
});
If you see there is not any role yet incude, so now on the server.js use the onCreateUser method.
//Server.js
Accounts.onCreateUser(function(options, user) {
if (options.profile)
user.profile = options.profile;
user.role = "basic"
return user;
});
Now thats is more easy, and with less code, if you are trying to create 2 differents roles like "Admin" and "Basic", just on the client side create a profile field named "profile.roles" and do a if statement on the onCreateUser.
return Accounts.createUser({email: adminData.email, password : adminData.password, roles: adminData.roles});
This part returns the userId once it is created, it doesn't return any errors when it fails.
When it fails, the returned value will be undefined
Also, in the server, we cannot use callbacks with Accounts.createUser
If you want find the errors, you have to use Accounts.createUser in client side.
Coming to this late, but on the server side, you can assign the createUser to a variable and it will return the new user’s _id; then you can check if that exists. For example (server side only):
let email = 'foo#bar.com';
let password = 'bar';
let profile = {firstName: 'foo', lastName: 'bar'};
let newId = Accounts.createUser({
password: password,
email: email,
profile: profile
});
if (!newId) {
// New _id did not get created, reason is likely EMail Already Exists
throw new Meteor.Error(403, "Cannot create user: " + error.reason);
}
else {
// Stuff here to do after creating the user
}
The Meteor.Error line will be passed back as an error in the callback on the client side, so you can reflect that error to the browser.

Resources