How can I implement new reCaptcha Enterprise
within django on the form without using old django-recaptcha
?
Ive deleted django-recaptcha = "==2.0.6"
that used v2 or v3 reCaptcha not Enterprise (. But I`ve used logic of integration reCaptcha from there).
For It:
google-cloud-recaptcha-enterprise = "==1.4.1"
, provided credentials via string that obtain from google-console as key.json and set It to env;
got credentials
RECAPTCHA_CREDENTIALS_JSON = ast.literal_eval(
os.getenv("RECAPTCHA_CREDENTIALS_JSON", "{}",)
.replace("\r", "\\r")
.replace("\n", "\\n")
)
RECAPTCHA_CREDENTIALS_JSON should be provided as string like:
RECAPTCHA_CREDENTIALS_JSON="{ "type": "service_account", "project_id":..., "private_key": "..." ...., }"
got sitekey
for Enterprise RECAPTCHA_PUBLIC_KEY
;
RECAPTCHA_PUBLIC_KEY = os.getenv("RECAPTCHA_PUBLIC_KEY", "...")
added to INSTALLED_APPS "django.forms",
;
added to settings FORM_RENDERER = "django.forms.renderers.TemplatesSetting"
;
In Forms.py:
from google.cloud import recaptchaenterprise_v1
RECAPTCHA_ACTION = "login"
if settings.RECAPTCHA_CREDENTIALS_JSON and settings.RECAPTCHA_PUBLIC_KEY:
client = recaptchaenterprise_v1.RecaptchaEnterpriseServiceClient.from_service_account_info(
settings.RECAPTCHA_CREDENTIALS_JSON
)
else:
client = None
Method that check result for captcha-field:
def create_assessment(token):
"""Create an assessment to analyze the risk of a UI action.
Args:
projectID: GCloud Project ID
recaptchaSiteKey: Site key obtained by registering a domain/app to use recaptcha services.
token: The token obtained from the client on passing the recaptchaSiteKey.
recaptchaAction: Action name corresponding to the token.
Return:
bool: True if successful.
"""
project_id = "....."
recaptcha_site_key = settings.RECAPTCHA_PUBLIC_KEY
recaptcha_action = RECAPTCHA_ACTION
# Set the properties of the event to be tracked.
event = recaptchaenterprise_v1.Event(expected_action=recaptcha_action)
event.site_key = recaptcha_site_key
event.token = token
assessment = recaptchaenterprise_v1.Assessment()
assessment.event = event
project_name = f"projects/{project_id}"
# Build the assessment request.
request = recaptchaenterprise_v1.CreateAssessmentRequest()
request.assessment = assessment
request.parent = project_name
response = client.create_assessment(request)
# Check if the token is valid.
if not response.token_properties.valid:
logger.info(
"The CreateAssessment call failed because the token was "
+ "invalid for for the following reasons: "
+ str(response.token_properties.invalid_reason)
)
return None
return True
The method above receive token
that we should get from "widget". Widget code:
class ReCaptchaBase(forms.widgets.Widget):
"""
Base widget to be used for Google ReCAPTCHA.
public_key -- String value: can optionally be passed to not make use of the
project wide Google Site Key.
"""
recaptcha_response_name = "g-recaptcha-response"
template_name = "oauth2_provider/widget_v2_checkbox.html"
def value_from_datadict(self, data, files, name):
return data.get(self.recaptcha_response_name, None)
def build_attrs(self, base_attrs, extra_attrs=None):
attrs = super(ReCaptchaBase, self).build_attrs(base_attrs, extra_attrs)
attrs["data-callback"] = base_attrs.get("data-callback", "onloadCallback")
attrs["sitekey"] = base_attrs.get("sitekey", settings.RECAPTCHA_PUBLIC_KEY)
attrs["theme"] = base_attrs.get("theme", "light")
attrs["action"] = base_attrs.get("action", RECAPTCHA_ACTION)
return attrs
value_from_datadict
: helps me to get client
token;build_attrs
: attributes that passed to the template;Add the field to the our form, Ive been rewriting the PasswordResetForm. Ive added it like a CharFiled within our widget.
class ReCaptchaField(forms.CharField):
widget = ReCaptchaBase()
def validate(self, value):
if not create_assessment(value):
raise forms.ValidationError(
self.error_messages["required"], code="required"
)
return value
Form:
class CustomPasswordResetForm(PasswordResetForm):
"""Reset password form override"""
def __init__(self, *args, **kwargs):
super(CustomPasswordResetForm, self).__init__(*args, **kwargs)
if not settings.RECAPTCHA_CREDENTIALS_JSON or not settings.RECAPTCHA_PUBLIC_KEY:
self.fields.pop("captcha")
captcha = ReCaptchaField()
__init__
: delete captcha whenever credentials unprovidedAdd captcha block
to the form in main template password_reset_form.html
:
{% block captcha %}
<div class="justify-center">
<div class="w-full flex mt-3 justify-center">
{{ form.captcha }}
</div>
<div class="w-full flex mt-3 justify-center">
<p>{{ form.errors.captcha }}</p>
</div>
</div>
{% endblock captcha %}
template for widget widget_v2_checkbox.html
:
<script src="https://www.google.com/recaptcha/enterprise.js?onload=onloadCallback&render=explicit" async defer></script>
<script type="text/javascript">
// Submit function to be called, after reCAPTCHA was successful.
var onloadCallback = function () {
grecaptcha.enterprise.render('id_captcha', {
'sitekey': '{{ widget.attrs.sitekey }}',
'action': '{{ widget.attrs.action }}',
'theme': '{{ widget.attrs.theme }}',
});
};
</script>
<div class="captcha" id="id_captcha"
{% for name, value in widget.attrs.items %}
{% if value is not False %} {{name}}
{% if value is not True %}="{{ value|stringformat:'s' }}"
{% endif %}{% endif %}
{% endfor %}>
</div>