Help with DDP Rate limiter

I’m trying to put a DDP rate limiter on the number of login attempts coming in from the client to the server. I’ve gone through the official documentation but I’m unable to verify if any of it even works.

I’ve added the package: ddp-rate-limiter

My server code is:

Meteor.startup(function() {

    var preventBruteForeLogin= {

        type: 'method',
        name: 'Meteor.loginWithPassword'
    }

    DDPRateLimiter.addRule(preventBruteForeLogin, 1, 2000);
    DDPRateLimiter.setErrorMessage("slow down");
});

My understanding with the above is that it has added a rate limiting rule on Meteor.loginWithPassword method that it only allows one attempt every 2 seconds. However, given the little information available in the documentation and elsewhere on the net, I’m unable to figure out if it’s actually working or if I’ve done it wrong. I’ve also gone through MC’s blog on this and frankly I don’t understand the coffee script code. Can someone guide me through this?

PS - Posted it on SO, in case someone wants to help.

Thanks.

I think the principle’s correct, although the docs suggest that the login method name is 'login', not 'Meteor.loginWithPassword'.

BTW, that’s not Coffee - it’s JavaScript.

1 Like

Whoops. JS arrow functions.

Anyway, I replaced ‘Meteor.loginWithPassword’ with ‘login’. How exactly do I know that this works? I’ll need to console.log, but log what exactly? How do I throw the message back to the client?

There’s a callback you can use which is run when the rule is executed (by which I assume it means triggered). The code suggests it has two parameters. I’m guessing reply is the error message.

You could console.log in that callback - and/or use throw new MeteorError(), which should make it back to the client - these rules should be running within the context of a Method or publication after all.

Thanks Rob, I am now able to get a log of the success /err message via the callback. However I still am not sure how to send it back to the client. Based on what you say about it having to run within the context of the method, How exactly do I define this within the user login method? All I do is call Meteor.loginWithPassword(username, pwd, callback). There is nowhere that I get to define this method since it’s part of the accounts package. Or is my understanding wrong?

1 Like

I think the point is that the rate limiter “inserts itself” into methods and publications and so is automatically part of that context. In which case, throw new Meteor.Error() will get sent back. The accounts package should catch and report those errors.

I did try this approach but i end up with an error:

DDPRateLimiter.addRule(preventBruteForeLogin, 1, 2000, function(err,resp){
            console.log(err);
            console.log(resp);
            if(err){
                throw new Meteor.Error(err)

            }
    });

I20170713-17:34:26.514(5.5)? { allowed: true, timeToReset: 2000, numInvocationsLeft: 0 }
I20170713-17:34:26.515(5.5)? { userId: null,
I20170713-17:34:26.516(5.5)?   clientAddress: '127.0.0.1',
I20170713-17:34:26.516(5.5)?   type: 'method',
I20170713-17:34:26.517(5.5)?   name: 'login',
I20170713-17:34:26.517(5.5)?   connectionId: 'Mxuf9MHjDT5nqd6eC' }
I20170713-17:34:26.518(5.5)? Exception in callback of async function: Error: [[object Object]]
I20170713-17:34:26.518(5.5)?     at server/startups/00c_DDPRateLimiter.js:15:23
I20170713-17:34:26.518(5.5)?     at runWithEnvironment (packages/meteor.js:1176:24)
I20170713-17:34:26.519(5.5)?     at Object.callback (packages/meteor.js:1189:14)
I20170713-17:34:26.520(5.5)?     at [object Object]._executeCallback (packages/rate-limit.js:128:22)
I20170713-17:34:26.520(5.5)?     at packages/rate-limit.js:203:12
I20170713-17:34:26.521(5.5)?     at Array.forEach (native)
I20170713-17:34:26.521(5.5)?     at Function._.each._.forEach (packages/underscore.js:139:11)
I20170713-17:34:26.521(5.5)?     at [object Object].RateLimiter.check (packages/rate-limit.js:170:5)
I20170713-17:34:26.521(5.5)?     at Object.DDPRateLimiter._check (packages/ddp-rate-limiter.js:115:22)
I20170713-17:34:26.522(5.5)?     at packages/ddp-server/livedata_server.js:704:48
1 Like

Hmm. That’s rather like the “must run in a fiber” error. However, the callback is run within a fiber, so maybe this is simpler than it appears. Have you tried to break the rate limit and see if the error is reported in the client anyway?

Well. I feel silly now. It was throwing the error perfectly after all. I concole’d the error in the client and I see this

errorClass {error: "too-many-requests", reason: "Error, too many requests. Please slow down. You must wait 2 seconds before trying again.".......}

Thanks Rob! I appricate the quick help! :slight_smile:

1 Like

I’m chanting a new Meteor mantra:

“Meteor makes it simple. Don’t look for complications where there aren’t any.”

:slight_smile:

I’ll have to agree to you more since I just noticed an answer on the SO question I’d posted. Looks like meteor accounts inherently has it’s own rate limiter throwing the error. I guess I’ll leave this part alone and let it do it’s thing instead of adding complications :smiley:

Does Meteor do rate limiting for custom login handlers as well? E.g.

Accounts.registerLoginHandler("pinLogin", loginRequest => ...)

Thanks.