Accounts.setPassword not working [Solved]

Hi,

Having an issue with Accounts.setPassword on the server. Keep getting the below error, from console:

methods.js:189 n2ntQAJpKA8L9frRY
methods.js:190 pwd2
validated-method.js:75 Uncaught TypeError: _accountsBase.Accounts.setPassword is not a function 

The code is in a methods.js, and is initialized into server on startup:

console.log(id);
console.log(valueString);
Accounts.setPassword(id, valueString, { logout: false });

Any ideas? Thanks in advance.

Tat

OK,

Can someone explain this to me. It has been a frustrating day. Support type person is logged into app. He wants to create a user.

(1) Call Accounts.createUser on client.
(2) In utils.js on server,

import { Accounts } from 'meteor/accounts-base';
import { Roles } from 'meteor/alanning:roles';

import { Entities } from '../entities/entities.js';

Accounts.onCreateUser((options, user) => {
  // We still want the default hook's 'profile' behavior.
  if (options.profile) {
    user.profile = options.profile;
  }

  // THIS DOES NOT SEEM TO WORK.
  // I AM TRYING TO ADD A ROLE, AND THEN DO SOME OTHER STUFF.
  if (options.homeEntity) {
    const entity = Entities.findOne({ _id: options.homeEntity });
    Roles.addUsersToRoles(user._id, entity.entityDefaultGroup, entity._id);
  }

  // I DO NOT WANT THIS USER LOGGED IN - HOW THE F*&K DO I STOP THE LOGIN
  return user;
});

Assistance appreciated… Also, why does console.log not work in the Accounts.onCreateUser function?

Tat

Have you done meteor add accounts-password?

Does options.homeEntity exist?

When you call Accounts.createUser on the client, it logs the user in:

On the client, this function logs in as the newly created user on successful completion. On the server, it returns the newly created user id.

Are you saying the function isn’t being called, or that it is, but you can’t see the log?

@robfallows,

First of, apologies on the caps + language, no excuse to be a prick. That being said,

(1) I have done meteor add accounts-base and accounts-password.
(2) Accounts.createUser does not make a lot of sense to me. As far as I can tell, it is never meant to be used on the Server, with exception of fixtures. I currently use it on server to create users, but its behavior seems odd.
(3) The way i read it, accounts.createUser is meant to always be used on the client. To modify this behavior, add in Accounts.onCreateUser in a utils.js (or something) somewhere on the server. This suppresses the normal callback of Accounts.createUser, so we can do other stuff, including, not logging in the user if required.

The thing is, i can’t get any of this to work. There is also the mystery of import { AccountsServer } from 'meteor/accounts-base', which is used somehow.

The odd bit is, for the life of me I cannot get any console.log statement in Accounts.onCreateUser in utils.js to work. Nothing shows up… I even tried AccountsServer.onCreateUser, and no luck…

So clearly it is not firing, but not sure how to get it to fire. It is definately on the server side of the app…

Thanks so much.

Tat

:smile: I can’t say I was offended - code annotation should be exempt!

So, I’m trying to replicate your problem (not succeeded in getting it to fail so far). Here’s what I did:

Set up a new app and add in account-password.

meteor create play
cd play
meteor npm i
meteor add accounts-password

Set up server/main.js

import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';

Meteor.startup(() => {
  Accounts.onCreateUser(function(options, user) {
    if (options.profile) {
      user.profile = options.profile;
    } else {
      user.profile = {};
    }
    user.profile.thing = {a:1,b:2};
    console.log(user, options);
    return user;
  });
});

Set up client/main.html

<head>
  <title>play</title>
</head>

<body>
</body>

Start the app areate a new user through the Chrome console:

meteor&

> Accounts.createUser({username:'bobby',email:'bobby@bobson.com',password:'bobby'});

Observe console.log:

I20160722-11:01:39.106(1)? { createdAt: Fri Jul 22 2016 11:01:39 GMT+0100 (BST),
I20160722-11:01:39.107(1)?   _id: 'Kq6yop7MunkN25PPF',
I20160722-11:01:39.107(1)?   services: { password: { bcrypt: '$2a$10$e5EStGJHX.kY9j5iJm5qbun9iqstj.RmWgBG9Q8SXiOIdlib10bR2' } },
I20160722-11:01:39.108(1)?   username: 'bobby',
I20160722-11:01:39.108(1)?   emails: [ { address: 'bobby@bobson.com', verified: false } ],
I20160722-11:01:39.108(1)?   profile: { thing: { a: 1, b: 2 } } } { username: 'bobby',
I20160722-11:01:39.109(1)?   email: 'bobby@bobson.com',
I20160722-11:01:39.109(1)?   password:
I20160722-11:01:39.110(1)?    { digest: '69ff8042237aeef5ddf1ced10e851bc6104e1ebb15a304061dba43ea10106fa9',
I20160722-11:01:39.110(1)?      algorithm: 'sha-256' } }

Add accounts-ui

meteor add accounts-ui

Add loginButtons to client/main.html:

<head>
  <title>play</title>
</head>

<body>
  {{> loginButtons}}
</body>

Create a new user through the loginButtons ui:

Observe console.log:

I20160722-11:04:04.267(1)? { createdAt: Fri Jul 22 2016 11:04:04 GMT+0100 (BST),
I20160722-11:04:04.284(1)?   _id: 'RphM4g8DqAmBJgHfx',
I20160722-11:04:04.287(1)?   services: { password: { bcrypt: '$2a$10$0Oa7uWLsueuzJVdAGj9ZlO.yQxB4t4m3xyIDOg9.Vy15ithp1RdZq' } },
I20160722-11:04:04.289(1)?   emails: [ { address: 'betty@bobson.com', verified: false } ],
I20160722-11:04:04.291(1)?   profile: { thing: { a: 1, b: 2 } } } { email: 'betty@bobson.com',
I20160722-11:04:04.293(1)?   password:
I20160722-11:04:04.294(1)?    { digest: '25eb140a5d07827aa95cec021a994705e5d94188b416ba1b166c8a062b3f694f',
I20160722-11:04:04.295(1)?      algorithm: 'sha-256' } }

So, maybe the difference is in the location of your Accounts.onCreateUser - is your util.js executed in a Meteor.startup?

Hi @robfallows,

Ok - thank you so much for the assistance. I had my utils.js in a server folder, but had not imported it into my index.js.

Also, thank you for the patience. I did not end up using setPassword or onCreateUser. I used createUser on server (properly!) which worked fine. It turned out the main source of my grief was Roles.addUsersToRoles where i was not passing the user id in properly. Unfortunately for me, this threw a validated-methods error, which sent me all over the place as i thought it was my validation which had broken it.

Below, I was just doing Roles.addUsersToRoles(userId, [‘a’], ‘q6a’) which was incorrect.

Thanks so much.

Tat

For anyone else… on server…

export const globalAdminUserAdminInsert = new ValidatedMethod({
  name: 'globalAdmin.userAdmin.insert',
  validate: new SimpleSchema({
    email: { type: String },
    password: { type: String },
    profile: { type: Object },
    'profile.name': { type: Object },
    'profile.name.first': { type: String },
    'profile.name.last': { type: String },
    roles: { type: Object },
    'roles.a': { type: Boolean },
    'roles.b': { type: Boolean },
    'roles.c': { type: Boolean },
  }).validator(),
  run(user) {
    if (!Roles.userIsInRole(this.userId, ['a'], 'q6a')) {
      throw new Meteor.Error(
        'unauthorized',
        'Cannot insert users without proper access');
    }

    const userId = Accounts.createUser(user); // When used on server, this will give
    // userid back properly.

    if (user.roles) {
      if (user.roles.a) {
        Roles.addUsersToRoles({ _id: userId }, ['b'], 'q6a');
      }

      if (user.roles.b) {
        Roles.addUsersToRoles({ _id: userId }, ['b'], 'q6a');
      }

      if (user.roles.c) {
        Roles.addUsersToRoles({ _id: userId }, ['c'], 'q6a');
      }
    }
  },
});

@robfallows,

Mate I tried to go back and do the setPassword again, and it’s odd. I’m on server (definitely)

(1) this is my validated method…

import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import { Roles } from 'meteor/alanning:roles';

import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { rateLimit } from '../../modules/rate-limit.js';

export const globalAdminUserAdminUpdateString = new ValidatedMethod({
  name: 'globalAdmin.userAdmin.update.string',
  validate: new SimpleSchema({
    id: { type: String },
    valueField: { type: String },
    valueString: { type: String },
  }).validator(),
  run({ id, valueField, valueString }) {
    switch (valueField) {
      case ('firstName'):
        Meteor.users.update(
          id, { $set: { 'profile.name.first': valueString } }
        );
        break;
      case ('lastName'):
        Meteor.users.update(
          id, { $set: { 'profile.name.last': valueString } }
        );
        break;
      case ('password'): {
        // const userId = Meteor.users.findOne({ _id: id })._id;
        console.log(id); // Comes up fine
        console.log(valueString); // Comes up fine
        Accounts.setPassword(id, valueString); // This is not working.
        break;
      }
      default:
        throw new Meteor.Error(
          'globalAdmin.userAdmin.update.string.failure',
          `Method ${valueField} was not recognized...`);
    }
  },
});

rateLimit({
  methods: [
    globalAdminUserAdminUpdateString,
  ],
  limit: 5,
  timeRange: 1000,
});

Console:

methods.js:87 6HBsS2n3ToCtAxDtt
methods.js:88 sgeseg
validated-method.js:75 Uncaught TypeError: _accountsBase.Accounts.setPassword is not a function

The thing is, when i change it to import { Accounts } from ‘meteor/accounts-password’; the error changes and the id becomes undefined. Looking at the docs, it should definitely be account-base, and the setPassword should definitely be on the server.

Why is validated-method think Accounts.setPassword is not a function?

Any insight would be appreciated. Thanks so much.

Tat

I suspect it’s because the method (run) part of your validated method is running as a simulation on the client. You could try this to check that:

case ('password'): {
    if (Meteor.isServer) {
        // const userId = Meteor.users.findOne({ _id: id })._id;
        console.log(id); // Comes up fine
        console.log(valueString); // Comes up fine
        Accounts.setPassword(id, valueString); // This is not working.
        break;
    }
 }
   
1 Like

@robfallows - mate that worked perfectly. you’re a genius. Thank you so much for you assistance and patience. Thanks so much.

Tat

1 Like

For anyone else who looks at this: tl,dr;

Using a model where server side methods side within an imports directory, i.e. Top level view

-client
  -stylesheets
-imports
  -api //methods live here
  -modules
  -startup
    -client
    -server //includes an api.js which loads all the stuff in the -api folder
  -ui
-public
-server //main.js isonly file here and is simply import '/imports/startup/server';
-tests

In this specific scenario, for whatever reason validated methods get confused quite easily. Especially accounts-base methods, when evaluated by validated-methods will validate as if it was a client and not server. Hence Accounts. will play up, esp the server side functionality.

The fix is simple, in validated methods use the if (Meteor.isServer) so that method-validation knows what to do like below. Inside the isServer, stuff like setPassword works fine:

import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
import { Roles } from 'meteor/alanning:roles';

import { SimpleSchema } from 'meteor/aldeed:simple-schema';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { rateLimit } from '../../modules/rate-limit.js';

export const <say user insert method> = new ValidatedMethod({
  name: '<some name>',
  validate: new SimpleSchema({
    email: { type: String },
    password: { type: String },
    profile: { type: Object },
    'profile.name': { type: Object },
    'profile.name.first': { type: String },
    'profile.name.last': { type: String },
    role: { type: String },
    roleGroup: { type: String },
  }).validator(),
  run(user) {
    if (Meteor.isServer) {
      const userId = Accounts.createUser(user);
      Roles.addUsersToRoles({ _id: userId }, user.roleGroup, user.role);
    }
  },
});

rateLimit({
  methods: [
    <our-above-validated-method>,
  ],
  limit: 5,
  timeRange: 1000,
});

Note you should not use profile for anything serious, as it is open to everything by default. I only use it for first and last name as it is super convenient. If you have privacy concerns, or are required to do so by legislation, do not do this…

Thanks so much.

Tat

2 Likes