Cool, thanks for bringing the discussion here. I have a basic idea that you might be able to use
along with a login hook, though the implementation probably depends on the exact policy that you're trying to enforce. Curious to hear if anyone else has ideas. Pseudocode:
Client code on login:
Meteor.call("solveCaptcha", <captcha answer>, function (err) {
if (! err) Meteor.loginWithPassword(...);
});
Server code:
Meteor.methods({
solveCaptcha: function (answer) {
// check captcha answer here
if (answerIsCorrect) {
} else {
// throw an error, I suppose
}
}
});
Accounts.validateLoginAttempt(function (attempt) {
return true;
} else {
// sleep for some amount of time, and then throw an error?
}
});
// Make sure 'solvedCaptchas' gets cleaned up
Meteor.server.onConnection(function (conn) {
conn.onClose(function () {
});
});
Now, the big caveat here is that there is per-connection state about whether the client has solved a captcha, so if the client gets disconnected between the 'solveCaptcha' method call and the 'loginWithPassword' call, then the login will fail, claiming that the client hasn't solved a captcha even if they just did! One possible answer might be that this is a rare enough occurrence that you don't care, and in the unlikely event that the client does get disconnected at exactly that moment, their login will fail and you can just display a nice error message saying, "Sorry, something went wrong, please solve the captcha again."
If you do care about that scenario of the client getting disconnected between the two method calls, then there's probably some trickery you can do with the `Accounts.connection.onReconnect` callback to make sure that if the connection goes down, the client re-establishes itself as a legitimate captcha-solving connection automatically when it comes back up. (For example, the 'solveCaptcha' method could return a token proving that the client solved a captcha at time t, and the client provides that token on reconnect to get its new connection in 'solvedCaptchas'.)
Emily