Meteor showing Accounts.createUser errors - meteor

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.

Related

Undefined Firebase Custom Claims

I want to add a custom claim to the idtoken on registration here is my cloud function for that:
exports.processSignUp = functions.auth.user().onCreate((newUser) => {
let customClaims = {};
if (newUser.role === "reader") {
customClaims.reader = true
} else if (newUser.role === "writer") {
customClaims.writer = true
}
return admin
.auth()
.setCustomUserClaims(newUser.uid, customClaims)
.then(() => {
return {
message: `success`,
};
});
});
(The newUser object contains a role key that can hold either a string value of writer or reader)
whenever I create a new user the cloud function runs with status ok but later on when I receive the decoded id token there aren't any claims in it so I assume it was null to begin with. I have no idea where my mistake is and couldn't find any documentation that could explain it. Would very much appreciate any help :))
The newUser parameter in the function is of type auth.UserRecord which does not have any custom properties. It only has these properties.
It seems to me that you want to add role - either writer or reader - to a newly created user. Using a custom callable cloud function rather than authentication trigger would be better to avoid race conditions involved in other workarounds.
exports.createNewUser = functions.https.onCall((data, context) => {
const userEmail = data.email;
const userPassword = data.password
const role = data.role
if (!["reader", "writer"].includes(role)) {
return {error: "Invalid Role"}
}
return admin.auth().createUser({ email: userEmail, password: userPassword }).then((newUser) => {
return admin.auth().setCustomUserClaims(newUser.uid, {role: role}).then(() => {
return {data: "New user created successfully"}
})
})
});
This will create a new user with role you need. To call this function from your app, follow this code on the client:
const createNewUser = firebase.functions().httpsCallable('createNewUser');
createNewUser({ email: "userEmail", password: "userPassword", role: "userRole" })
.then((result) => {
// Read result of the Cloud Function.
const response = result.data.data;
});
Please let me know if you need more clarification.

Firebase Ionic - Returning Error

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.

Put the email address from Meteor.loginWithGoogle into a Session variable

I'm using Meteor.loginWithGoogle in my app. I'm trying to get the email address of the google user to put it into a Session variable.
Template.login.events({
'click #google-login': function(event){
Meteor.loginWithGoogle({}, function(err){
if ( err ) {
throw new Meteor.Error("Google login failed");
} else {
const emailAddress = ?; // how do I get this from google?
Session.set('email',emailAddress);
Router.go('/profile');
}
});
}
});
I'm not sure whether i understood your question, but i guess what you're trying to ask is: "After a user performed a loginWithGoogle, how can i get his email address, and set it into his Session?"
After a login, Meteor.user() holds the current user document. Having that in mind:
const currentUser = Meteor.user();
const userGoogleServiceMain = currentUser.services.google.email;
With that, you can have:
Template.login.events({
'click #google-login': function(event){
Meteor.loginWithGoogle({}, function(err){
if ( err ) {
throw new Meteor.Error("Google login failed");
} else {
const currentUser = Meteor.user();
const emailAddress = currentUser.services.google.email;
Session.set('email',emailAddress);
Router.go('/profile');
}
});
}
});
You may find more details about that in: Meteor documentation and http://cs.wellesley.edu/~mashups/pages/meteor6.html

adding a field to a user after created

This code attempts to add a field to a user which already exist in meteor users.
The error I am getting is
Exception while invoking method 'logMeIn' Error: insert requires an argument
Which I don't understand, how can it be fixed? Thanks
/////////////////////////////////////
// client code
/////////////////////////////////////
Template.login.events({
'click #logMe': function() {
var username = $('#id').val();
var password = $('#pin').val();
Meteor.call('logMeIn', [username,password], function (err, data) { //create new user
if ( err ) {
if (err.message.match(/username already exists/i)) {
Meteor.loginWithPassword(username+password,password)
}
} else {
console.log('new user created');
}
});
}
});
/////////////////////////////////////
// server code
/////////////////////////////////////
Meteor.methods({
logMeIn: function (credentials) {
//do work , if logged in, do next line
var idPin = credentials[0] + credentials[1];
Accounts.createUser({username: idPin, password: credentials[1]});
}
});
Accounts.onCreateUser(function (options, user) {
user.menuGroup = 'a';
});
You need to return the user on the Account.onCreatedUser (documentation here). Also, additional data of the user should be put under the profile branch (check the documentation in here)
Accounts.onCreateUser(function (options, user) {
if (options.profile) {
user.profile = options.profile;
}
if (user['profile'] == null) {
user['profile'] = {};
}
user['profile']['menuGroup'] = 'a';
return user;
});

Firebase profile integrations

What I am trying to do here is to implement a functionality on the start-up. I want my user's firebase authentication email variable to set a variable that represents the current user logged into my app?
With the following code the line that sets the user variable works after I click log in but not on page load! The console logs work perfectly on start-up but not the setting of user to the email...
crossfitApp.controller('globalIdCtrl', ["$scope", 'defautProfileData',
function ($scope, defautProfileData) {
var dataRef = new Firebase("https://glowing-fire-5401.firebaseIO.com");
//defautProfileData.country;
$scope.authenticated = {
currentUser: 10007,
emailAddress: "",
settings: "",
};
$scope.auth = new FirebaseSimpleLogin(dataRef, function (error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
//Not working
$scope.authenticated.currentUser = user.id;
console.log('User ID: ' + user.id + ', ProvideFr: ' + user.provider + user);
console.log(user);
} else {
console.log($scope.auth);
alert('deuces');
//!Trigger not logged in
}
});
}
]); //GlobaldCtrl
The callback to FirebaseSimpleLogin is not invoked inside the scope of Angular's HTML compiler. Normally, whenever you invoke ng-click, ng-submit, et al, Angular fires $scope.$apply(), which checks for any changes to the bound JavaScript variables and applies those to the DOM elements.
When an event outside of Angular changes a variable, you need to let Angular know by manually triggering a $apply event. The safest way to accomplish this is to use $timeout:
angular.controller('MyCtrl', function($scope, $timeout) {
$scope.auth = new FirebaseSimpleLogin(dataRef, function (error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
$timeout(function() {
$scope.currentUser = user.uid;
});
} else {
console.log('not logged in');
}
});
In general, prefer user.uid to user.id, as it is unique across providers.
A library like AngularFire can save you a lot of trouble, as it abstracts a lot of the complexities of integrating Firebase and Angular.

Resources