Add reCAPTCHA feature
This commit is contained in:
110
recaptcha_helper.py
Normal file
110
recaptcha_helper.py
Normal file
@@ -0,0 +1,110 @@
|
||||
"""
|
||||
reCAPTCHA Helper Module
|
||||
Provides verification functionality for Google reCAPTCHA v2
|
||||
"""
|
||||
|
||||
import os
|
||||
import requests
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ReCaptcha:
|
||||
"""Helper class for reCAPTCHA verification"""
|
||||
|
||||
def __init__(self, app=None):
|
||||
self.site_key = None
|
||||
self.secret_key = None
|
||||
self.enabled = False
|
||||
|
||||
if app is not None:
|
||||
self.init_app(app)
|
||||
|
||||
def init_app(self, app):
|
||||
"""Initialize reCAPTCHA with Flask app configuration"""
|
||||
self.site_key = os.getenv('RECAPTCHA_SITE_KEY', '')
|
||||
self.secret_key = os.getenv('RECAPTCHA_SECRET_KEY', '')
|
||||
self.enabled = os.getenv('RECAPTCHA_ENABLED', 'true').lower() == 'true'
|
||||
|
||||
# Store in app config for template access
|
||||
app.config['RECAPTCHA_SITE_KEY'] = self.site_key
|
||||
app.config['RECAPTCHA_ENABLED'] = self.enabled
|
||||
|
||||
if self.enabled and (not self.site_key or not self.secret_key):
|
||||
logger.warning("reCAPTCHA is enabled but keys are not configured properly")
|
||||
|
||||
def verify(self, response_token, remote_ip=None):
|
||||
"""
|
||||
Verify a reCAPTCHA response token
|
||||
|
||||
Args:
|
||||
response_token (str): The g-recaptcha-response from the form
|
||||
remote_ip (str, optional): The user's IP address
|
||||
|
||||
Returns:
|
||||
tuple: (success: bool, error_message: str or None)
|
||||
"""
|
||||
# If reCAPTCHA is disabled, always return success
|
||||
if not self.enabled:
|
||||
return True, None
|
||||
|
||||
# Check if we have the required configuration
|
||||
if not self.secret_key:
|
||||
logger.error("reCAPTCHA secret key is not configured")
|
||||
return False, "reCAPTCHA is not properly configured"
|
||||
|
||||
# Check if response token was provided
|
||||
if not response_token:
|
||||
return False, "Please complete the reCAPTCHA challenge"
|
||||
|
||||
# Prepare the verification request
|
||||
verify_url = 'https://www.google.com/recaptcha/api/siteverify'
|
||||
payload = {
|
||||
'secret': self.secret_key,
|
||||
'response': response_token
|
||||
}
|
||||
|
||||
if remote_ip:
|
||||
payload['remoteip'] = remote_ip
|
||||
|
||||
try:
|
||||
# Make the verification request
|
||||
response = requests.post(verify_url, data=payload, timeout=10)
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
|
||||
if result.get('success'):
|
||||
logger.info("reCAPTCHA verification successful")
|
||||
return True, None
|
||||
else:
|
||||
error_codes = result.get('error-codes', [])
|
||||
logger.warning(f"reCAPTCHA verification failed: {error_codes}")
|
||||
|
||||
# Map error codes to user-friendly messages
|
||||
error_messages = {
|
||||
'missing-input-secret': 'reCAPTCHA configuration error',
|
||||
'invalid-input-secret': 'reCAPTCHA configuration error',
|
||||
'missing-input-response': 'Please complete the reCAPTCHA challenge',
|
||||
'invalid-input-response': 'reCAPTCHA verification failed. Please try again.',
|
||||
'bad-request': 'reCAPTCHA verification failed. Please try again.',
|
||||
'timeout-or-duplicate': 'reCAPTCHA expired. Please try again.'
|
||||
}
|
||||
|
||||
# Get the first error message or use a default
|
||||
error_code = error_codes[0] if error_codes else 'unknown'
|
||||
error_message = error_messages.get(error_code, 'reCAPTCHA verification failed. Please try again.')
|
||||
|
||||
return False, error_message
|
||||
|
||||
except requests.RequestException as e:
|
||||
logger.error(f"reCAPTCHA verification request failed: {str(e)}")
|
||||
return False, "Unable to verify reCAPTCHA. Please try again later."
|
||||
except Exception as e:
|
||||
logger.error(f"Unexpected error during reCAPTCHA verification: {str(e)}")
|
||||
return False, "An error occurred during verification. Please try again."
|
||||
|
||||
|
||||
# Create a singleton instance
|
||||
recaptcha = ReCaptcha()
|
||||
Reference in New Issue
Block a user