""" 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()