Search code examples
meteor

Meteor - return to client after successful login server-side


I wrote my custom method to login, but I'm blocked at the very last step: the effective login of the client.

I believe I'm correctly logged in server-side but no client side:

  • I got full & correct (timestamp coherent) LoginTokens (when & hashedToken) in DB.
  • In minimongo, I have access to all documents which I'm the owner of (this.userId).
  • The login attempt Accounts.validateLoginAttempt(function (attempt) is allowed, contains the right user and returns no error.
  • On client, on call's return, Meteor.loggingIn() is false and Meteor.user() is null
  • On server Accounts.onLogin(function(user) returns fine the user._id

So I assume it's an issue about the return to the client (like a user._id) - but I'm lost and think I need an experienced critic eye.

p.s.: I have accounts-base@1.4.0 & accounts-password@1.5.0

Login method (called normally from client)

Meteor.methods({

    logTwo (userfinal, passfinal) {
        
        // Consistency var check
        check(userfinal, String);
        const passwordValidator = {digest: String, algorithm: String};
        check(passfinal, passwordValidator);

        // check user
        const getUser = Accounts.findUserByEmail(userfinal);
        if (!getUser) {throw invalidLogin();}

        // check password
        const checkPassword = Accounts._checkPassword(getUser, passfinal);
        if (checkPassword.error) {throw invalidLogin();}

        // get user's id
        var userID = getUser._id

        // logic here
                
        console.log('code verified'); // rightly printed
        // below, I tried with or without methodArguments (this, 'login', {user: userfinal,password: passfinal},
        // and (this, 'login', '',
        Accounts._attemptLogin(this, 'login', {user: userfinal,password: passfinal}, {
            type: '2FALogin',
            userId: userID,
        });
    },
});

Accounts.validateLoginAttempt(function (attempt) {
    console.log(attempt); // rightly printed

    if (attempt.type === '2FALogin' && attempt.methodName === 'login') {
        console.log('allowed'); // rightly printed
        return true;
    }

    if (attempt.error) {
        console.log('login error: ' + attempt.error);
    }

});

return of Accounts.validateLoginAttempt(function (attempt) (console.log(attempt))

{ type: '2FALogin',
  allowed: true,
  methodName: 'login',
  methodArguments: 
   [ 'bob@bob.com',
     { digest: '70bd58ff28477...', // digest here ok
       algorithm: 'sha-256' } ],
  user: 
   { _id: '6i6vLjc8Ssg6SGJNf',
     createdAt: 2017-11-01T15:08:52.332Z,
     services: { password: [Object], resume: [Object] },
     emails: [ [Object], [Object] ],
     _loggedIn: true,
    },
  connection: 
   { id: 'xFLv3XZWztxsdxckM',
     close: [Function: close],
     onClose: [Function: onClose],
     clientAddress: '127.0.0.1',
     httpHeaders: 
      { 'x-forwarded-for': '127.0.0.1',
        'x-forwarded-proto': 'ws',
        host: 'localhost:3000',
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36',
        'accept-language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7,la;q=0.6' } } }

Solution

  • I figure how to manage it.

    1. Meteor.loginWithPassword was not an option, as it's not working inside a Meteor.call source
    2. I tried with success Meteor.connection.setUserId(response) on call's return, but nothing was stored in localStorage: so at every refresh, I was logged out.

    I needed Accounts.callLoginMethod, from accounts-base:

    login method which on success calls this.setUserId(id) and Accounts._setLoginToken on the server and returns an object with fields id (containing the user id), token (containing a resume token), and optionally tokenExpires.

    Also, in the method, I needed to return the function Accounts._attemptLogin (or nothing could be handled by the client).

    So, to resume:

    On client

    Accounts.callLoginMethod({
        methodName: 'logTwo',
        methodArguments: [
          {
            user: userfinal,
            password: passfinal
          },
        ],
        userCallback: function(error) {
          if (!error) {
            // handle return here
          } 
        }
    });
    

    On server

    Meteor.methods({
    
        logTwo (options) {
    
            // Consistency var check
            const passwordValidator = {digest: String, algorithm: String};
            check(options, {
                user: String,
                password: passwordValidator
            });
    
    
            // check user
            const getUser = Accounts.findUserByEmail(options.user);
            if (!getUser) {throw invalidLogin();}
    
            // check password
            const checkPassword = Accounts._checkPassword(getUser, options.password);
            if (checkPassword.error) {throw invalidLogin();}
    
            // get user's id
            var userID = getUser._id
    
            // logic here
    
            return Accounts._attemptLogin(this, 'login', '', {
                type: '2FALogin',
                userId: userID,
            });
        },
    });
    
    Accounts.validateLoginAttempt(function (options) {
    
        if (options.type === '2FALogin' && options.methodName === 'login') {
            return true;
        }
    
        if (options.error) {
            console.log('login error: ' + options.error);
        }
    
    });