[Solved] onCreateUser hook not working

I use this hook pretty extensively to: E.g. Seed a new account with some data, add a new account to Stripe etc.

So far, everything has worked fine - I do what needs to be done onCreateUser and then return the ‘user’ object as per the docs.

However, since updating to 1.8.2, suddenly all the code on the onCreateUser hook has started being ignored. It doesn’t run on new account creation at all.

To test things out, I removed all the logic in the hook and ran some tests:

Test 1 - Run come console logs to see if the code is running on new signups created:

…/server/accounts.js

...
Accounts.onCreateUser(
	(
		{ email, ...otherData },
		user
	) => {
		console.log('On create user hook test, does this log');
		console.log(email);
		console.log(otherData);
		console.log(user);
		return user;
	}
);

Outcome - no console output. User is created (basic user)

Test 2 - Same code as above but don’t return the user object at the end of the hook as should be done. The expected outcome here is for a new account creation attempt to fail.


Accounts.onCreateUser(
	(
		{ email, ...otherData },
		user
	) => {
		console.log('On create user hook test, does this log');
		console.log(email);
		console.log(otherData);
		console.log(user);
		// return user;
	}
);

Outcome - A user account is actually created without any error. Not sure why.

Test 3 - To ensure that the file where this hook is getting declared is loading correctly, test out other code in the file.

/server/accounts.js

Accounts.urls.resetPassword = function reset(token) {
	return Meteor.absoluteUrl(`reset-password/${token}`);
};


Accounts.onCreateUser(
	(
		{ email, ...otherData },
		user
	) => {
		console.log('On create user hook test, does this log');
		console.log(email);
		console.log(otherData);
		console.log(user);
		return user;
	}
);

Test ran to see what account reset url is generated. Outcome - the custom URL specified for reset passwords gets sent out as expected, so the code is loading fine.


Does anyone have any ideas as to why onCreateUser has suddenly stopped working? And/or guidance on any extra tests to run to try and find the source of this issue?

Thanks!

Just turned on our debugger and it was running as expected. Running 1.8.2 in production

1 Like

Cheers mate. I guess that rules out 1.8.2 being the issue here (I’ll change the title).

I’m still in the dark as to what the issue may be though :confused: Why would the onCreateUser hook be getting ignored.

Can you post your list of packages?

Maybe one of them is monkey-patching Accounts and changing the default behaviour?

1 Like
# Meteor packages used by this project

disable-oplog@1.0.7
cultofcoders:redis-oplog
meteor-base@1.4.0             # Packages every Meteor app needs to have
mobile-experience@1.0.5       # Packages for a great mobile UX
reactive-var@1.0.11            # Reactive variable for tracker
tracker@1.2.0                 # Meteor's client-side reactive programming library

es5-shim@4.8.0                # ECMAScript 5 compatibility for older browsers

mdg:validated-method
static-html
react-meteor-data
alanning:roles
check@1.3.1

accounts-password@1.5.1
random@1.1.0
underscore@1.0.10
ddp-rate-limiter@1.0.7
standard-minifier-css@1.5.4
shell-server@0.4.0
mongo@1.7.0
ecmascript@0.13.0
standard-minifier-js@2.5.0
natestrauser:publish-performant-counts
http@1.4.2
aldeed:collection2@3.0.0
typescript

With regards to monkey patching - I hijack the forgot password flow (to send out emails using postmark as opposed to the inbuilt Meteor email system). That’s been working fine for years (and remained untouched). That said, I’ll try disabling that part of the code to see if that is suddenly being an issue.

1 Like

I’ve just tested 1.8.2 and my onCreateUser callback is still being called.

The Accounts.onCreateUser() callback is registered in this code in packages/accounts-base.

Do you see the error "Can only call onCreateUser once"?

You could try console.log(Accounts._onCreateUserHook) before and after registering your own function to see if it is set to your own function:

const myCallback = () => console.log('On create user hook test, does this log');
console.log('before', Accounts._onCreateUserHook);
Accounts.onCreateUser(myCallback);
console.log('after', Accounts._onCreateUserHook);
if (Accounts._onCreateUserHook != myCallback) console.warn("My callback wasn't registered!!')

Or copy the accounts-base folder into your local packages folder and put some logging in that function to check that your callback is being registered. If the function is being monkey-patched then console.trace() can tell you where it’s being intercepted (if the patch calls the original method).

Let us know how you get on…

2 Likes

This is definitely the easiest way to drop a debugger in there to see what’s really happening. Good advice

1 Like

Or copy the accounts-base folder into your local packages folder and put some logging in that function to check that your callback is being registered. If the function is being monkey-patched then console.trace() can tell you where it’s being intercepted (if the patch calls the original method).

:exploding_head:

Didn’t even think of that, that’s awesome I’ll try that out (and I’m glad I’ve run into this issue, if just to expand my debugging capability).

You could try console.log(Accounts._onCreateUserHook) before and after registering your own function to see if it is set to your own function:

I’m not actually registering a function as such. it’s just a validated method that generates and sends out the reset password token/email as opposed to using the Accounts built in one. (I imagine that doesn’t really count as monkey patching the Accounts system).

// Allow reset password email to be sent via Postmark
export const resetPassword = new ValidatedMethod({
	name: 'reset-password',
	validate: new SimpleSchema({
		email: String,
	}).validator(),
	run({ email }) {
		if (!Meteor.isSimulation && Meteor.isServer) {
			const { sendResetPwEmail } = require('../../helpers/server/emailNotifications');
			const user = Accounts.findUserByEmail(email);
			if (!user) {
				// TODO: Send an email here rather than throwing an error
				throw new Meteor.Error('no-such-user', `No user exists with this username`);
			}
			// Create a password reset token
			// Reference 1: https://github.com/meteor/meteor/blob/49c9d07be833561a789f57581416638bca4d249d/packages/accounts-password/password_server.js#L541-L554
			// Reference 2: https://github.com/poetic/accounts-passwordless/blob/master/server/send-login-email.js
			const tokenRecord = {
				token: Random.secret(),
				email,
				when: new Date(),
			};

			Meteor.users.update({ _id: user._id }, { $set: { 'services.password.reset': tokenRecord } });

			Meteor._ensure(user, 'services', 'password').reset = tokenRecord;

			// Send the email
			const resetUrl = Meteor.absoluteUrl(`reset-password/${tokenRecord.token}`);
			sendResetPwEmail({
				to: user.emails[0].address,
				vars: {
					resetUrl,
				},
			});
		}
	},
});

Although, rereading that method, I’m generating the reset url myself as well, as opposed to my earlier post that quotes a function I have within accounts.js on the server:

Accounts.urls.resetPassword = function reset(token) {
	return Meteor.absoluteUrl(`reset-password/${token}`);
};

This opens up the possibility that my accounts.js file is not getting loaded at all. Adding some console.logs to Accounts.js doesn’t log anything at all, so I’m thinking that that file is no longer ‘active’ so to speak.

To my knowledge (and past experience), this should automatically be getting bundled into the server, is it possible that this is not happening

Regardless, thank you so much @wildhart for the suggestions!

Ok I have the culprit code here - it’s within package.json:

    "mainModule": {
      "client": "./client/main.js",
      "server": "./server/main.js"
    },

Which I added to package.json as a part of this: https://github.com/meteor/meteor/pull/10603#issuecomment-559321665

It seems that having that there, explicitly, ends up so that all server folder files (such as my accounts.js) are not being loaded.

I imagine there is a better way to fix this than to just remove that part of package.json? Are there any benefits/drawbacks to having or not having that?

Thanks again to all here who got me thinking in the right direction :+1:

1 Like

Well… it seems the package.json file is just like that per-default now, when using Meteor’s react template (Meteor v2.1.1).

Just for the sake of “closure”: I just imported “account-creation.js” in the main.js file. Not sure this is the right way to do it though, but it works.