Meteor registering and assigning role error - meteor

I have a Meteor application and am trying to register a user and assign a role:
In the client registration file, I have a user with email, password, username, profile (firstname and lastname) and role:
Accounts.createUser(user, user.roles, function(error){
if(!error) {
FlowRouter.go('home');
console.log("Registration successful");
}
else {
FlashMessages.sendError(error.reason);
console.log("Registration not successful: " + error.reason);
}
});
And then in the server file I have:
Accounts.onCreateUser(function(options, user) {
if(options.roles) {
user.roles = options.roles;
Roles.addUsersToRoles(user._id, user.roles);
}
return user;
});
With this code, I always get an error message:
Exception in delivering result of invoking 'createUser': TypeError: options.userCallback.apply is not a function
at http://localhost:3000/packages/accounts-base.js?7dabd814506e384c709f8bf707377955f9814129:612:26
at http://localhost:3000/packages/underscore.js?46eaedbdeb6e71c82af1b16f51c7da4127d6f285:794:19
at loggedInAndDataReadyCallback (http://localhost:3000/packages/accounts-base.js?7dabd814506e384c709f8bf707377955f9814129:708:7)
at null._callback (http://localhost:3000/packages/meteor.js?9730f4ff059088b3f7f14c0672d155218a1802d4:999:22)
The user does get added successfully though!
EDIT: THE FIX
On server side:
Accounts.onCreateUser(function(options, user) {
if (options.profile) {
user.profile = options.profile;
}
if(options.roles) {
user.roles = options.roles;
Roles.addUsersToRoles(user._id, user.roles);
}
return user;
});
On client side:
Accounts.createUser(user, function(error){
if(!error) {
FlowRouter.go('home');
}
else {
FlashMessages.sendError(error.reason);
}
});

You are adding the roles to the user in onCreateUser. The user object has not been inserted in the collection at that point.
Another remark: you pass user.roles as your second parameter to createUser. According to Meteor docs you can only pass options and a callback. See http://docs.meteor.com/#/full/accounts_createuser
Maybe change to something like:
Accounts.createUser(user, function(error){
if(!error) {
Roles.addUsersToRoles(user._id, user.roles);
FlowRouter.go('home');
console.log("Registration successful");
} else {
FlashMessages.sendError(error.reason);
console.log("Registration not successful: " + error.reason);
}
});

Related

how to work with callback when calling Meteor methods?

I have noticed the callback functions never get executed all though the server method runs fine. Also from the Meteor documentation, i understand that when Meteor.Error is thrown it will notify the client but i don't see that working as well. Am i doing something fundamentally wrong?
Client
if (Meteor.isCordova) {
getContacts(function (contacts) {
$meteor.call('createContacts', contacts, function(err){
alert("in create contacts callback");
if(err && err.error === "not-logged-in"){
alert("error due to not-logged-in");
$ionicPopup.alert({
title: err.reason || "User not logged in",
template: 'Please try again after logged in',
okType: 'button-positive button-clear'
});
}
else if(err && err.error === "contacts-exists"){
$ionicPopup.alert({
title: err.reason || "Connections already exists",
template: 'Please try again after logged in',
okType: 'button-positive button-clear'
});
}
$meteor.call('createConnections');
});
});
}
function getContacts(success, error) {
function onSuccess(contacts) {
success && success(contacts);
};
var options = {};
options.multiple = true;
var fields = ["displayName", "name"];
navigator.contacts.find(fields, onSuccess, error, options);
}
Server
createContacts: function (contacts, callback) {
if (!this.userId) {
throw new Meteor.Error('not-logged-in',
'Must be logged in to update contacts')
}
var userId = this.userId, exist = Contacts.findOne({userId: userId});
log.debug("Is contacts for userId %s exist in database ? %s", userId, !! exist);
if (!exist) {
Contacts.insert({'userId': userId, 'contacts': contacts}, function () {
callback && callback();
});
} else {
log.debug("Contacts for user exists so throwing exception as contacts-exists");
var meteorErr = new Meteor.Error('contacts-exists', "Contacts are already exist");
callback && callback(meteorErr);
}
},
These callbacks are asynchronous. Your server-side method shouldn't invoke the callback function, nor even expect one as an argument.
You are right to pass the callback as the last argument to Meteor.call('createContacts'), but it is not up to the receiver of createContacts to determine when that callback should be invoked. In simple terms, from the client's point of view, the server's job is simply to return an 'OK' or an 'error' signal.
Remove any reference to the callback in the method definition (on the server), and expect the client to execute that callback when the server responds.
Try this:
Server
createContacts: function (contacts) {
if (!this.userId) {
throw new Meteor.Error('not-logged-in',
'Must be logged in to update contacts');
}
var userId = this.userId;
var exist = Contacts.findOne({userId: userId});
log.debug("Is contacts for userId %s exist in database ? %s", userId, !! exist);
if (!exist) {
Contacts.insert({'userId': userId, 'contacts': contacts});
} else {
log.debug("Contacts for user exists so throwing exception as contacts-exists");
throw new Meteor.Error('contacts-exists', "Contacts are already exist");
}
},
Client
$meteor.call('createContacts', contacts, function(err){
alert("in create contacts callback");
if(err && err.error === "not-logged-in"){
alert("error due to not-logged-in");
$ionicPopup.alert({
title: err.reason || "User not logged in",
template: 'Please try again after logged in',
okType: 'button-positive button-clear'
});
}
else if(err && err.error === "contacts-exists"){
$ionicPopup.alert({
title: err.reason || "Connections already exists",
template: 'Please try again after logged in',
okType: 'button-positive button-clear'
});
}
$meteor.call('createConnections');
});
Seems like i need to use .then() as documented in Angular-Meteor Document
Also changed the code as per amageddian

About custom email validation in Meteor

I tried to write my own custom auth form in meteor. for the email validation part, the system send out an email with the route and token appended on it. However, I want to get the token in the validation page, so I tired the following
Accounts.onEmailVerificationLink(function(token, done) {
console.log("hello");
Session.set(verifyEmailToken, token);
doneCallback = done;
});
Template.emailVerified.onCreated(function(){
console.log(Session.get(verifyEmailToken));
Accounts.verifyEmail(Session.get(verifyEmailToken),function(err){
if(err){
Session.set(ERROR_KEY,err.reason);
}else{
Session.set(SUCESS_KEY,"Your email has been verified, thank you!");
if (doneCallback) {
doneCallback();
}
}
});
});
But the Accounts.onEmailVerificationLink method doesn't seem like have been invoked. Did I miss something there? Any help is appreciated.
Try this:
Server code:
Accounts.urls.verifyEmail = function(token) {
return Meteor.absoluteUrl('verify/' + token);
};
Common/Both Router:
AccountController = RouteController.extend({
verifyEmail: function() {
Accounts.verifyEmail(this.params.token, function(err) {
if (err) {
// error
} else {
//
}
});
}
});
Router.map(function() {
return this.route('verifyEmail', {
controller: 'AccountController',
path: '/verify/:token',
action: 'verifyEmail'
});
});

Accounts.createUser on server does not return response to client

I'm attempting to use Meteorjs Accounts on the server to create a new user and then send them an email to set their initial password. The idea is that an admin user can add new users.
I can successfully add the new user (I can see the new user ID in the server console if I log it), but that ID is never returned to the client. This is my server-side
Meteor.methods({
createNewUser: function(email){
return Accounts.createUser({email: email});
}
});
And the relevant client-side JS:
if (isNotEmpty(email) && isEmail(email)) {
Meteor.call("createNewUser", email, function(ret){
if (typeof ret.message !== 'undefined') {
if (ret.message === 'Email already exists. [403]') {
alert("exists");
} else {
alert("not created");
}
} else {
Accounts.sendEnrollmentEmail(ret, function(err){
if (err){
alert("email didn't get sent");
} else {
alert('success');
}
});
}
});
}
I get this in my browser console:
Exception in delivering result of invoking 'createNewUser': TypeError: Cannot read property 'message' of undefined
It's probably worth noting that I also get the "exists" alert if I try to submit the same email address twice in a row, so the error is getting returned to the client just fine.
The first argument in callback is always error object.
error equals null if everything is fine.
Meteor.call('createNewUser', email, function( error, result ){
if( error ){
console.error("ERROR -> ", error )
}else{
console.log("User was created!")
}
})
but that ID is never returned to the client.
Thats because you don't have any console.log on the client. also the meteor call look incorrect.
if (isNotEmpty(email) && isEmail(email)) {
Meteor.call("createNewUser", email, function(err,result){
if (typeof ret.message !== 'undefined') {
if (ret.message === 'Email already exists. [403]') {
alert("exists");
} else {
console.log(result) //here for example you should get the id
}
} else {
Accounts.sendEnrollmentEmail(ret, function(err){
if (err){
alert("email didn't get sent");
} else {
alert('success');
}
});
}
});
}

How to verify if a Meteor.user username exists?

I'm building a messenger application, and before a conversation is created I want to verify if a user exists. If it does, then it will create the conversation. If not, then it should return an error. I've been working with this code on the server side but for some reason it won't work. I've tried many different tweaks, but this is basically my structure:
Meteor.methods({
createConversation: function(secondPerson) {
function doesUserExist(secondPerson) {
var userx = Meteor.users.findOne({username: secondPerson});
if (userx === secondPerson) {
return false;
} else {
return true;
}
}
if (doesUserExist()) {
Conversations.insert({
person1: Meteor.user().username,
person2: secondPerson
});
} else {
Conversations.insert({
person1: "didn't work"
});
}
}
});
The main point you were missing is that find returns a cursor, whereas findOne returns a document. Here is one way to implement the method:
Meteor.methods({
createConversation: function(username) {
check(username, String);
if (!this.userId) {
throw new Meteor.Error(401, 'you must be logged in!');
}
if (Meteor.users.findOne({username: username})) {
return Conversations.insert({
person1: Meteor.user().username,
person2: username
});
} else {
throw new Meteor.Error(403, username + " does not exist!");
}
}
});
Note the following features:
validates that username is a string
requires that the user be logged in to create a conversation
reduces the user existence check to a single line
returns the id of the new conversation
uses Meteor.Error with explanations which can be seen on the client
To use it just open your browser console and try making calls like:
Meteor.call('createConversation', 'dweldon', function(err, id){console.log(err, id);});

Firebase authentication on App initilisation

This is works:
console.log('User ID: ' + user.id + ', Provider: ' + user.provider);
but this one is not:
$scope.authenticated.currentUser = user.id;
My goal here is to take to take some of the authentication variables (Email+UserID) and then use them to access a profile node ON firebase. On initialization I want the username, email, and a few other things I need for the app.
crossfitApp.controller('globalIdCtrl', ["$scope",'defautProfileData','$q', function ($scope,defautProfileData,$q) {
var dataRef = new Firebase("https://glowing-fire-5401.firebaseIO.com");
$scope.authenticated={
currentUser: $scope.authemail,
emailAddress: "",
settings: "",
};
var chatRef = new Firebase('https://<YOUR-FIREBASE>.firebaseio.com');
var auth = new FirebaseSimpleLogin(chatRef, function(error, user) {
if (error) {
// an error occurred while attempting login
switch(error.code) {
case 'INVALID_EMAIL':
case 'INVALID_PASSWORD':
default:
}
} else if (user) {
// user authenticated with Firebase
console.log('User ID: ' + user.id + ', Provider: ' + user.provider);
$scope.authenticated.currentUser = user.id ;//
} else {
// user is logged out
}
});
}]); //GlobaldCtrl
Most likely, you're running into a problem with Angular's HTML Compiler.
Whenever you use an event like ng-click/ng-submit/etc, Angular fires $scope.$apply(), which checks for any changes to your $scope variables and applies them to the DOM.
Since FirebaseSimpleLogin is not part of Angular's purview, it has no idea that when the callback is fired, you've updated $scope.authenticated.currentUser. This would also explain why it works when you call auth.login(), since you're probably invoking that via an ng-click event somewhere, which would fire a digest check and discover the changes.
If this is indeed the case, you can correct this issue by alerting Angular that it needs to run $apply by using $timeout:
crossfitApp.controller('globalIdCtrl', ["$scope",'defautProfileData','$q', '$timeout', function ($scope,defautProfileData,$q, $timeout) {
/* ... */
var auth = new FirebaseSimpleLogin(chatRef, function(error, user) {
if (error) {
/* ... */
} else if (user) {
$timeout(function() {
$scope.authenticated.currentUser = user.id ;//
});
} else {
// user is logged out
}
});

Resources