totaljs / framework

Node.js framework
http://www.totaljs.com
Other
4.36k stars 450 forks source link

ReCaptcha v3 and Middlewere #710

Closed fgnm closed 5 years ago

fgnm commented 5 years ago

Hi, I would like to use ReCaptcha v3 with Total.js (https://www.npmjs.com/package/express-recaptcha). Could you point me the right direction? I know that total middleware should be similar to express, but I've not understand how integrate ReCaptcha as middleware.

I've got stuck here definitions/recaptchav3.js:

var Recaptcha = require('express-recaptcha').RecaptchaV3;

var recaptcha = new Recaptcha('SITE_KEY', 'SECRET_KEY', {callback:'cb'});

F.middleware('recaptchav3_render', function($) {
    var captcha = recaptcha.renderWith({'hl':$.language});
    //How to pass captcha to html layout if $.controller is null?
    $.next();
});

F.middleware('recaptchav3_verify', function($) {
    recaptcha.verify($.req, function(error, data){
        if (!$.req.recaptcha.error) {
            $.next();
        } else {
           $.cancel();
        }
    });
});

F.use('recaptchav3_verify');
F.use('recaptchav3_render');

If you can help me you can also add an example on how to implement recaptcha v3, I think that could be useful for many users. Many Thanks

molda commented 5 years ago

Look at the source code of the Recaptcha here https://github.com/pdupavillon/express-recaptcha/blob/master/src/v3.ts

I'm sure you can write a module for Total.js from scratch in less then 100 lines of code. The verify function is just a request to the google server and the render function only creates a script tag which you insert into your html.

petersirka commented 5 years ago

Here is a simple solution: https://blog.totaljs.com/blogs/tutorials/20161211-how-to-verify-recaptcha-with-help-of-total-js/

fgnm commented 5 years ago

So that's it!

This is v3 middleware

F.middleware('recaptchav3_verify', function($) {
    RESTBuilder.make(function(builder) {
        builder.url('https://www.google.com/recaptcha/api/siteverify');
        builder.set('secret', CONF.recaptchav3_secret);
        builder.set('response', $.body.captcha);
        builder.set('remoteip', $.req.ip);
        builder.urlencoded();
        builder.exec(function(err, response) {
            if (err || !response.success || response.score < 0.15) {
                $.cancel();
            } else {
                $.next();
            }
        });
    });
});

And if I want protect a resource I do something like that:

ROUTE('POST /register', json_register_form, ['#recaptchav3_verify', 'unauthorize', 30000]);

On front-end I've just included the standard captchav3 code, and get token before do any request:

<script src="//www.google.com/recaptcha/api.js?render=@{'%recaptchav3_key'}"></script>
<script>
grecaptcha.ready(function () {
        grecaptcha.execute("@{'%recaptchav3_key'}", { action: 'register' }).then(function (token) {
            $(":input[type=text]").each(function () { $(this).val($.trim($(this).val())); });
            $.post('/register', $('#msform').serialize() + '&captcha=' + token, function (res) {
                hideLoader();
                var data = JSON.parse(res);

                var err = $('.error');
                if (data.error != null) {
                    err.empty();
                    err.show().html(data.error);
                    validating = false;
                    return;
                };

                $(location).attr('href', '/');
            });
        });
    });
</script>
petersirka commented 5 years ago

Great!

My best practices:

And maybe cleaner condition:

if (response.success && response.score > 0.14)
    $.next();
else
    $.cancel();