[Solved] Custom login Accounts.registerLoginHandler Error

Meteor Version 1.8.2

Server

Accounts.registerLoginHandler('ldap', (loginRequest) => {

  if (!loginRequest.ldap) {
    return undefined
  }
  console.log(loginRequest);
});

Client

var loginRequest = {
      ldap: true,
      email: "test@domain.com",
      pass: "password",
}
Accounts.callLoginMethod({
      methodArguments: [loginRequest],
      userCallback: function(result){console.log(result);}
});

Result Error

Exception while invoking method 'login' { Error: Match error: Unknown key in field ldap
I20191219-09:44:52.158(7)?     at check (packages/check/match.js:36:17)
I20191219-09:44:52.159(7)?     at MethodInvocation.Accounts.registerLoginHandler.options (packages/accounts-password/password_server.js:286:3)
I20191219-09:44:52.159(7)?     at tryLoginMethod (packages/accounts-base/accounts_server.js:460:31)
I20191219-09:44:52.159(7)?     at tryLoginMethod (packages/accounts-base/accounts_server.js:1294:14)
I20191219-09:44:52.159(7)?     at AccountsServer._runLoginHandlers (packages/accounts-base/accounts_server.js:458:22)

How to config Accounts.registerLoginHandler
Thank you

Login handlers must return either undefined or a “login method result object”

  // @param handler {Function} A function that receives an options object
  // (as passed as an argument to the `login` method) and returns one of:
  // - `undefined`, meaning don't handle;
  // - a login method result object

Which the definition of what that should look like is here:

  // Login handlers and service specific login methods such as
  // `createUser` internally return a `result` object containing these
  // fields:
  //
  //   type:
  //     optional string; the service name, overrides the handler
  //     default if present.
  //
  //   error:
  //     exception; if the user is not allowed to login, the reason why.
  //
  //   userId:
  //     string; the user id of the user attempting to login (if
  //     known), required for an allowed login.
  //
  //   options:
  //     optional object merged into the result returned by the login
  //     method; used by HAMK from SRP.
  //
  //   stampedLoginToken:
  //     optional object with `token` and `when` indicating the login
  //     token is already present in the database, returned by the
  //     "resume" login handler.
  //
  // For convenience, login methods can also throw an exception, which
  // is converted into an {error} result.  However, if the id of the
  // user attempting the login is known, a {userId, error} result should
  // be returned instead since the user id is not captured when an
  // exception is thrown.
  //
  // This internal `result` object is automatically converted into the
  // public {id, token, tokenExpires} object returned to the client.

  // Try a login method, converting thrown exceptions into an {error}
  // result.  The `type` argument is a default, inserted into the result
  // object if not explicitly returned.
2 Likes

The docs or guide could really use a couple of pages explaining all this rather than needing to look at comments in the code

What’s the solution? Can someone provide code sample on how to solve it? Stuck from few days.

import { Meteor } from "meteor/meteor";
import { Accounts } from "meteor/accounts-base";
import { Promise } from "meteor/promise";
import md5 from "md5";
import mysql from "mysql2";

const DB = mysql.createPool(Meteor.settings.mysql);
const Helper = {};
Helper.number = s => /^\d+$/.test(s);

Accounts.registerLoginHandler("mysql", function(loginRequest) {
    let sql = "";
    let params = [];
    if (loginRequest.type == "username") {
        if (!loginRequest.user && !loginRequest.pass) {
            return undefined;
        }
        let password = md5(loginRequest.pass);
        sql = `SELECT * FROM members WHERE member_username=? AND member_password=?`;
        params = [loginRequest.user, password];
    } else if (loginRequest.type == "member_id") {
        if (!Helper.number(loginRequest.member_id)) {
            return undefined;
        }
        sql = `SELECT * FROM members WHERE member_id=?`;
        params = [loginRequest.member_id];
    } else {
        return undefined;
    }

    let member = Promise.await(
        new Promise((resolve, reject) => {
            DB.query(sql, params, function(e, r) {
                if (e) resolve(false);
                else resolve(r[0]);
            });
        })
    );
    if (!member) {
        return {
            error: new Meteor.Error(
                400,
                "Invalid Username/Password"
            )
        };
    }
    let userId = null;
    let user = Meteor.users.findOne({ 'profile.member_id': member.member_id });
    if (!user) {
        userId = Meteor.users.insert({
            username: member.member_username,
            profile: {
                member_id: member.member_id,
                name: member.member_name
            }
        });
        if (loginRequest.type == "username") {
            Accounts.setPassword(userId, loginRequest.pass);
        }
    } else {
        userId = user._id;
    }
    let stampedToken = Accounts._generateStampedLoginToken();
    let hashStampedToken = Accounts._hashStampedToken(stampedToken);
    Meteor.users.update(userId, {
        $push: { "services.resume.loginTokens": hashStampedToken }
    });
    return {
        userId: userId,
        token: stampedToken.token
    };
});
Accounts.callLoginMethod({
   methodArguments: [{
      type:'username',
      user:'test', // cannot use key username
      pass:'12345', // cannot use key password
   }],
   userCallback(error) {
      console.log('error',error);
   },
});

Client call cannot use object key “username and password

1 Like