I'm getting the error "SignUps Forbidden" when i try to create a user account. Any ideas why?
My packages:
useraccounts:materialize
materialize:materialize
accounts-password
accounts-facebook
service-configuration
accounts-google
accounts-twitter
kadira:blaze-layout
msavin:mongol
kadira:flow-router
kevohagan:sweetalert
Client Code:
Template.register.events({
'click #register-button': function(e, t) {
e.preventDefault();
// Retrieve the input field values
var email = $('#email').val(),
firstName = $('#first-name').val(),
lastName = $('#last-name').val(),
password = $('#password').val(),
passwordAgain = $('#password-again').val();
// Trim Helper
var trimInput = function(val) {
return val.replace(/^\s*|\s*$/g, "");
}
var email = trimInput(email);
// If validation passes, supply the appropriate fields to the
// Meteor.loginWithPassword() function.
Accounts.createUser({
email: email,
firstName: firstName,
lastName: lastName,
password: password
}, function(error) {
if (error) {
return swal({
title: error.reason,
text: "Please try again",
showConfirmButton: true,
type: "error"
});
} else {
FlowRouter.go('/');
}
});
return false;
}
});
Server code
Accounts.onCreateUser(function(options, user) {
user.profile = options.profile || {};
user.profile.firstName = options.firstName;
user.profile.lastName = options.lastName;
user.profile.organization = ["Org"];
user.roles = ["User"];
return user;
});
UPDATE:
Here is a link to the repo
The problem seems to be on .....meteor\local\build\programs\server\packages. If i switch the value to false it's useless because it resets on every build.
// Client side account creation is disabled by default:
// the methos ATCreateUserServer is used instead!
// to actually disable client side account creation use:
//
// AccountsTemplates.config({
// forbidClientAccountCreation: true
// });
Accounts.config({
forbidClientAccountCreation: true
});
I had to remove the useraccounts:materialize in order to solve this problem
I don't think that the current accepted answer is the right one.
If you want to keep your packages yet override the setting you can change the value of Accounts._options.forbidClientAccountCreation in your code.
Set it to true if you want to prevent the account creation, or to false otherwise.
Related
I have a button that inserts new user to Meteor.users().
In the server I have this method:
Meteor.methods({
'addUser': function(user) {
return Accounts.createUser(user)
}
})
And in the client (After button is clicked):
var newUser = {
email: t.find('#email').value,
password: t.find('#pwd').value,
profile: { name: t.find('#name').value, group: t.find('#userType').value },
roles: checkedRoles // I can successfully console.log(checkedRoles) which is an array of strings.
}
Meteor.call('addUser', newUser, function(error){
if(error){
sweetAlert(error)
} else {
sweetAlert('User Successfully Added')
}
})
Using the above code, the user is added but without the roles field.
My question is, how can I add the roles field to the newly added user?
Use alanning:roles package:
meteor add alanning:roles
then (in your server side method):
const userId = Accounts.createUser(user);
Roles.addUsersToRoles(userId, user.roles);
on the client site I have an extraSignupField -> name. This is stored in users.profile.name. Here is the code:
Meteor.startup(function() {
Accounts.ui.config({
passwordSignupFields: 'EMAIL_ONLY'
});
AccountsEntry.config({
homeRoute: '/',
dashboardRoute: '/dashboard',
profileRoute: '/profile',
language: 'de',
showSignupCode: false,
extraSignUpFields: [{
field: "name",
label: "Name",
type: "text",
required: true
}]
});
});
On the server site I run onCreateUser, this gets fired but it only sets the values which at mentioned there. NO users.profile entry is created when onCreateUser is in place.
Here is the server code:
Meteor.startup(function() {
AccountsEntry.config({
signupCode: null
});
Accounts.onCreateUser(function (options, user) {
user.username = options.email;
return user;
});
});
My goal is to keep the data from users.profile AND the username entry. Unfortunately I got stuck at this point.
Thanks a lot
Michael
When you write your own Accounts.onCreateUser, it overrides the default. In the default, options.profile is copied to user.profile. You can restore default behavior by making this copy in your own version of the function. You see this in the docs.
Accounts.onCreateUser( function (options, user) {
if (options.profile) user.profile = options.profile;
user.username = options.email;
return user;
}
we are using this code to login with google:
var googleOptions = {
scope: 'email'
};
$scope.auth.$authWithOAuthPopup("google", googleOptions).then(function(authData) {
console.log(authData.google.email);
var userSigninIdentifier = authData.google.id;
console.log("userSigninIdentifier:" + userSigninIdentifier);
if ($scope.googleRef.$getRecord(userSigninIdentifier) == null) {
console.warn("new user, registering...");
$scope.register(authProvider, authData);
} else {
$scope.profileID = $scope.googleRef.$getRecord(userSigninIdentifier).profileID;
$firebase(ref.child("users").child("signin").child("google").child(userSigninIdentifier)).$update({
token: authData.token,
expires: authData.expires,
AccessToken: authData.google.accessToken
});
$firebase(ref.child("users").child("data").child($scope.profileID)).$update({
displayName: authData.google.displayName,
email: authData.google.email,
picture: authData.google.cachedUserProfile.picture
});
console.log("Logged in as:", authData.uid);
$state.go('app.home');
}
}).catch(function(error) {
console.error("Authentication failed google:", error);
});
Unfortunately, the options don't work. And since the options don't work, we can't access the users email adress. How do we use AngularFire's $authWithOAuthPopup method with parameters?
Edit: this (in a slightly different form) works perfectly with facebook login
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
}
});
Using Meteor accounts (and accounts-ui) is there an easy way to make new user sign-ups invitation only? For example by providing an invitation link or an invitation code.
The only thing related I could find in the Meteor documentation is Meteor.sendEnrollmentEmail but it doesn't solve my problem.
You can do this with the built in package, but I found it alot easier and powerful to roll a simple implementation.
You'll need to:
Create a collection, eg UserInvitations to contain the invites to become a user.
Create UI for making UserInvitations / insert some using meteor mongo
Using iron-router or similar create a route, eg:
Router.map ->
#route 'register',
path: '/register/:invitationId'
template: 'userRegistration'
data: ->
return {
invitationId: #params.invitationId
}
onBeforeAction: ->
if Meteor.userId()?
Router.go('home')
return
When the form in userRegistration is submitted - call
Accounts.createUser({invitationId: Template.instance().data.invitationId /*,.. other fields */})
On the server, make an Accounts.onCreateUser hook to pass through the invitationId from options to the user
Accounts.onCreateUser(function(options, user){
user.invitationId = options.invitationId
return user;
});
Also, on the server make an Accounts.validateNewUser hook to check the invitationId and mark the invitation as used
Accounts.validateNewUser(function(user){
check(user.invitationId, String);
// validate invitation
invitation = UserInvitations.findOne({_id: user.invitationId, used: false});
if (!invitation){
throw new Meteor.Error(403, "Please provide a valid invitation");
}
// prevent the token being re-used.
UserInvitations.update({_id: user.invitationId, used: false}, {$set: {used: true}});
return true
});
Now, only users that have a valid unused invitationId can register.
EDIT: Oct 2014 - Updated to use meteor 0.9.x API's
To do it with the built in stuff, you can plumb together the existing Accounts.sendEnrollmentEmail - however it's a little more complicated than the other solution given.
Using the example code below, call the enroll method as such:
Meteor.call('enroll', 'john.smith', 'js#harvard.edu', {name: 'John Smith'});
Meteor will then email the user a link (You can configure the template with Accounts.emailTemplates)
When they click the link, meteor calls the function passed to Accounts.onEnrollmentLink - in this case you can take them to a password setup page; but you have to mess around with their done callback.
Modify the following code, where it says INSERT XXX HERE ; then in your code call SomeGlobalEnrollmentObjectThing.cancel() if the user cancels, or SomeGlobalEnrollmentObjectThing.complete(theUsersNewPassword) if they submit the new password.
if (Meteor.isServer){
Meteor.methods({
"enroll": function(username, email, profile){
var userId;
check(username, String);
check(email, String); // Or email validator
check(profile, {
name: String
}); // your own schema
// check that the current user is privileged (using roles package)
if (!Roles.isInRole(this.userId, 'admin')){
throw new Meteor.Error(403);
}
userId = Accounts.createUser({
username: username,
email: email,
profile: profile
});
Accounts.sendEnrollmentEmail(userId);
}
});
} else {
// uses `underscore`, `reactive-var` and `tracker` packages
function Enrollment(){
this.computation = null;
this.token = new ReactiveVar(null);
this.password = new ReactiveVar(null);
this.cancelled = new ReactiveVar(false);
this.done = null;
this._bind();
}
_.extend(Enrollment.prototype, {
_bind: function(){
Accounts.onEnrollmentLink(_.bind(this.action, this));
},
reset: function(){
this.token.set(null);
this.password.set(null);
this.cancelled.set(false);
this.done = null;
if (this.computation !== null){
this.computation.stop();
this.computation = null;
}
},
cancel: function(){
this.cancelled.set(true);
},
complete: function(password){
this.password.set(password);
},
action: function(token, done){
this.reset();
this.token.set(token);
this.done = done;
this.computation = Tracker.autorun(_.bind(this._computation, this));
// --- INSERT REDIRECT LOGIC HERE [TAKE TO PASSWORD SETUP PAGE]--- //
},
_computation: function(){
var password;
if (this.cancelled.get()){
this.reset();
this.done();
// --- INSERT REDIRECT LOGIC HERE [USER CANCELLED]--- //
} else {
password = this.password.get();
if (password !== null){
Accounts.resetPassword(this.token.get(), password, _.bind(this._complete, this));
}
}
},
_complete: function(err){
// TODO - check if we were reset before callback completed
this.reset();
this.done();
if (err){
// --- INSERT REDIRECT LOGIC HERE [RESET FAILED] --- //
} else {
// --- INSERT REDIRECT LOGIC HERE [SUCCESS] --- //
}
}
});
SomeGlobalEnrollmentObjectThing = new Enrollment();
}
I have created a specific solution to this, since all the other solutions only allow you to explicitly create password-based accounts. The t3db0t:accounts-invite package allows account creation with any service only when you allow them, such as with an 'accept invitation' route. Live demo here.