Skip to content

Consider adding special treatment for Recaptcha field used by Flask-WTF #408

Description

@marcsello

First of all, I'm not sure if this issue should be addressed in Flask-WTF or Bootstrap-Flask so I opened the issue for both projects. Here is the Flask-WTF side issue: pallets-eco/flask-wtf#695

Not so long ago, this commit has been added to Flask-WTF: pallets-eco/flask-wtf@adf674f

What it does is basically if a class is present at widget rendering, then it ignores the RECAPTCHA_DIV_CLASS config (or even the default value). This config used to be the way to define the class which the h-captcha and re-captcha scripts are looking for when they inject their iframes.

Bootstrap-Flask seem to treat captcha fields as regular fields, this it adds the form-control class to it. (see.:

{%- else -%}
{% if field.errors %}
{{ field(class="form-control is-invalid%s" % extra_classes, **kwargs)|safe }}
{% else %}
{{ field(class="form-control%s" % extra_classes, **kwargs)|safe }}
{% endif %}
{%- endif %}
). This causes the class required by captcha above not to be added.

Since the class is not present, h-captcha does not work.

Now one way to "fix" this is to add class="h-captcha" to all templates, but that's not always possible, especially when templates are coming from a third party library. Also I don't think that's very elegant either, having to put functionality related code to templates.

How Bootstrap-Flask could fix the situation: Add special handling to fields with the type of RecaptchaField (as it is already done for other types like SelectField, DecimalRangeField, etc.) and does not pass any class= to it. This would allow the re-captcha field to use the configured value. And also not having form-control on the captcha field, removes the ugly border around it. (That wasn't present when the config would override all classes before)

Reproduce

repro.py:

from flask import Flask, render_template
from flask_bootstrap import Bootstrap5
from flask_wtf import RecaptchaField, FlaskForm


class CaptchaForm(FlaskForm):
    captcha = RecaptchaField()


app = Flask(__name__)

app.config['SECRET_KEY'] = 'hunter2'
app.config['RECAPTCHA_PUBLIC_KEY'] = '00000000-0000-0000-0000-000000000000'
app.config['RECAPTCHA_PRIVATE_KEY'] = 'ES_000000000000000000000000000000000'
app.config['RECAPTCHA_DIV_CLASS'] = 'h-captcha'
app.config['RECAPTCHA_SCRIPT'] = "https://js.hcaptcha.com/1/api.js"
app.config['RECAPTCHA_VERIFY_SERVER'] = "https://api.hcaptcha.com/siteverify"

Bootstrap5(app)


@app.route("/")
def captcha():
    form = CaptchaForm()
    return render_template("repro.html.j2", form=form)


app.run(debug=True)

templates/repro.html.j2

{% from 'bootstrap5/form.html' import render_form %}
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    {{ bootstrap.load_css() }}
</head>
<body>
{{ render_form(form) }}
{{ bootstrap.load_js() }}
</body>
</html>

Result:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/css/bootstrap.min.css" integrity="sha384-SgOJa3DmI69IUzQ2PVdRZhwQ+dy64/BUtbMJw1MZ8t5HZApcHrRKUc4W0kG879m7" crossorigin="anonymous">
</head>
<body>

    <form action="" method="post"
            class="form" role="form">
        <input id="csrf_token" name="csrf_token" type="hidden" value="IjFkYzZlY2RiMDdlOTU3OGFkMjliNGM1NWRhNzE3ZWZmMzkzZDY4MDUi.aoCPjA.3iwUH6fnmrg79yshyg3cw0oTE9Q">
    <div class="mb-3"><label class="form-label" for="captcha"></label>            
<script src='https://js.hcaptcha.com/1/api.js' async defer></script>
<div class="form-control" data-sitekey="c832d69a-2e52-482c-9566-169ee5d6da56" id="captcha"></div>            
        </div>      
    </form>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js" integrity="sha384-I7E8VVD/ismYTF4hNIPjVp/Zjvgyol6VFvRkX/vR+Vc4jQkC+hVqc2pM8ODewa9r" crossorigin="anonymous"></script>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.5/dist/js/bootstrap.min.js" integrity="sha384-VQqxDN0EQCkWoxt/0vsQvZswzTHUVOImccYmSyhJTp7kGtPed0Qcx8rK9h9YEgx+" crossorigin="anonymous"></script>
</body>
</html>

As you can see, the class="h-captcha" is not added to the div.

Environment:

  • Python version: 3.14
  • Flask-WTF version: 1.3.0
  • Flask version: 3.1.3
  • Bootstrap-Flask version: 2.5.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingform

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions