{"id":15045091,"url":"https://github.com/karser/karserrecaptcha3bundle","last_synced_at":"2025-05-15T04:08:12.165Z","repository":{"id":43675862,"uuid":"176558384","full_name":"karser/KarserRecaptcha3Bundle","owner":"karser","description":"Google ReCAPTCHA v3 for Symfony","archived":false,"fork":false,"pushed_at":"2025-03-03T11:13:33.000Z","size":105,"stargazers_count":174,"open_issues_count":6,"forks_count":24,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-05-15T04:08:05.449Z","etag":null,"topics":["anti-spam","antispam","google-recaptcha","google-recaptcha-v3","googlerecaptcha","invisible-captcha","invisible-recaptcha","no-captcha","recaptcha","recaptcha-bundle","recaptcha-v3","recaptcha-v3-symfony","symfony-bundle","symfony-google-recaptcha","symfony-recaptcha"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/karser.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-03-19T16:47:42.000Z","updated_at":"2025-05-11T01:21:48.000Z","dependencies_parsed_at":"2024-01-22T07:48:00.779Z","dependency_job_id":"f31e3fa9-bd89-4ccc-af1d-e317279bd0c2","html_url":"https://github.com/karser/KarserRecaptcha3Bundle","commit_stats":{"total_commits":79,"total_committers":15,"mean_commits":5.266666666666667,"dds":"0.25316455696202533","last_synced_commit":"0288251442440c3cb940c7132a2e56995b9aceaa"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karser%2FKarserRecaptcha3Bundle","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karser%2FKarserRecaptcha3Bundle/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karser%2FKarserRecaptcha3Bundle/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karser%2FKarserRecaptcha3Bundle/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karser","download_url":"https://codeload.github.com/karser/KarserRecaptcha3Bundle/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254270656,"owners_count":22042860,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anti-spam","antispam","google-recaptcha","google-recaptcha-v3","googlerecaptcha","invisible-captcha","invisible-recaptcha","no-captcha","recaptcha","recaptcha-bundle","recaptcha-v3","recaptcha-v3-symfony","symfony-bundle","symfony-google-recaptcha","symfony-recaptcha"],"created_at":"2024-09-24T20:51:26.794Z","updated_at":"2025-05-15T04:08:07.154Z","avatar_url":"https://github.com/karser.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"KarserRecaptcha3Bundle\n======================\n\n[![Build Status](https://github.com/karser/KarserRecaptcha3Bundle/workflows/Code_Checks/badge.svg)](https://github.com/karser/KarserRecaptcha3Bundle/actions)\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/karser/KarserRecaptcha3Bundle/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/karser/KarserRecaptcha3Bundle/?branch=master)\n[![Code Coverage](https://scrutinizer-ci.com/g/karser/KarserRecaptcha3Bundle/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/karser/KarserRecaptcha3Bundle/?branch=master)\n[![Total Downloads](https://poser.pugx.org/karser/karser-recaptcha3-bundle/downloads)](https://packagist.org/packages/karser/karser-recaptcha3-bundle)\n\nreCAPTCHA v3 returns a score for each request without user friction. \nThe score is based on interactions with your site (1.0 is very likely a good interaction,\n0.0 is very likely a bot) and enables you to\ntake an appropriate action for your site.\n\n![image](https://user-images.githubusercontent.com/1675033/58698825-bbca8e00-83a4-11e9-9627-e3a2b1a6c074.png)\n\nGetting reCAPTCHA v3 key and secret\n--------------------------------\n\nRegister reCAPTCHA v3 keys [here](https://www.google.com/recaptcha/admin/create).\n\n![Image](https://github.com/user-attachments/assets/fa5ca3cd-e87a-485c-aa25-5b4800da5bde)\n\nInstallation\n------------\n\nWith [composer](https://getcomposer.org), require:\n\n`composer require karser/karser-recaptcha3-bundle`\n\nYou can quickly configure this bundle by using symfony/flex.\n\nConfiguration without symfony/flex: \n--------------------\n\n### 1. Register the bundle\n\n**Symfony 4/5/6/7 Version :**   \nRegister bundle into `config/bundles.php`:\n```php \nreturn [\n    //...\n    Karser\\Recaptcha3Bundle\\KarserRecaptcha3Bundle::class =\u003e ['all' =\u003e true],\n];\n```\n\n**Symfony 3 Version:**  \nRegister bundle into `app/AppKernel.php`:\n\n``` php\npublic function registerBundles()\n{\n    return array(\n        // ...\n        new Karser\\Recaptcha3Bundle\\KarserRecaptcha3Bundle(),\n    );\n}\n```\n\n### 2. Add configuration files\n\n```yaml\n# config/packages/karser_recaptcha3.yaml (or app/config/config.yml if using Symfony3)\n\nkarser_recaptcha3:\n    site_key: '%env(RECAPTCHA3_KEY)%'\n    secret_key: '%env(RECAPTCHA3_SECRET)%'\n    score_threshold: 0.5\n```\n\nAdd your site key and secret to your .env file:\n```\n###\u003e karser/recaptcha3-bundle ###\nRECAPTCHA3_KEY=my_site_key\nRECAPTCHA3_SECRET=my_secret\n###\u003c karser/recaptcha3-bundle ###\n```\n\nUsage\n-----\n\n### How to integrate re-captcha in Symfony form:\n\n```php\n\u003c?php\n\nuse Karser\\Recaptcha3Bundle\\Form\\Recaptcha3Type;\nuse Karser\\Recaptcha3Bundle\\Validator\\Constraints\\Recaptcha3;\n\nclass TaskType extends AbstractType\n{\n    public function buildForm(FormBuilderInterface $builder, array $options)\n    {\n        $builder-\u003eadd('captcha', Recaptcha3Type::class, [\n            'constraints' =\u003e new Recaptcha3(),\n            'action_name' =\u003e 'homepage',\n            'script_nonce_csp' =\u003e $nonceCSP,\n            'locale' =\u003e 'de',\n        ]);\n    }\n}\n```\nNotes:\n- The `action_name` parameter is [reCAPTCHA v3 action](https://developers.google.com/recaptcha/docs/v3#actions) which identifies the submission of this particular form in the Google reCAPTCHA dashboard, and confirming it is as expected in the backend is a recommended extra security step.\n- The `script_nonce_csp` parameter is optional. You must use the same nonce as in your Content-Security Policy header.\n- The `locale` parameter is optional. It defaults to English and controls the language on the reCaptcha widget.\n\n### How to use reCAPTCHA globally (meaning even in China):\n\nUse `'www.recaptcha.net'` host in your code when `'www.google.com'` is not accessible.\n\n```yaml\n# config/packages/karser_recaptcha3.yaml (or app/config/config.yml if using Symfony3)\n\nkarser_recaptcha3:\n    host: 'www.recaptcha.net' # default is 'www.google.com'\n```\n\n### How can I set the captcha language for different locales?\n\nYou can control the language in the small widget displayed by setting the locale in the options above.\n\nTo change the error messages, you should install the [Symfony Translation component](https://symfony.com/doc/current/translation.html).\n\nThen replace the validation text with the translation keys for the message and messageMissingValue options:\n```php\n$builder-\u003eadd('captcha', Recaptcha3Type::class, [\n     'constraints' =\u003e new Recaptcha3 ([\n         'message' =\u003e 'karser_recaptcha3.message',\n         'messageMissingValue' =\u003e 'karser_recaptcha3.message_missing_value',\n     ]),\n]);\n````\nAdd English, Spanish, or any other translation:\n```\n# translations/validators/validators.en.yaml\nkarser_recaptcha3.message: 'Your computer or network may be sending automated queries'\nkarser_recaptcha3.message_missing_value: 'The captcha value is missing'\n\n# translations/validators/validators.es.yaml\nkarser_recaptcha3.message: 'Es posible que su computadora o red esté enviando consultas automatizadas'\nkarser_recaptcha3.message_missing_value: 'Falta el valor de captcha'\n```\n\n### How to get the ReCaptcha score:\n\nInject the Recaptcha3Validator and call `getLastResponse()-\u003egetScore()` after the form was submitted:\n```php\n\u003c?php\n\nuse Karser\\Recaptcha3Bundle\\Validator\\Constraints\\Recaptcha3Validator;\n\nclass TaskController extends AbstractController\n{\n    public function new(Request $request, Recaptcha3Validator $recaptcha3Validator): Response\n    {\n        //...\n        $form-\u003ehandleRequest($request);\n        if ($form-\u003eisSubmitted() \u0026\u0026 $form-\u003eisValid()) {\n            //...\n            $score = $recaptcha3Validator-\u003egetLastResponse()-\u003egetScore();\n            //...\n        }\n        //...\n    }\n}\n```\n\n### How to integrate re-captcha in API method:\n\nThe idea is to require the frontend to submit the captcha token, so it will be validated on server side.\n\nFirst you need to add the captcha field to your transport entity:\n```php\n\u003c?php\n\nnamespace App\\Dto;\n\nfinal class UserSignupRequest\n{\n    /** @var string|null */\n    public $email;\n\n    /** @var string|null */\n    public $captcha;\n}\n```\n\nAnd to add the validation constraint:\n\n```yaml\n#config/validator/validation.yaml\nApp\\Dto\\UserSignupRequest:\n    properties:\n        email:\n            - NotBlank: ~\n            - Email: { mode: strict }\n        captcha:\n            - Karser\\Recaptcha3Bundle\\Validator\\Constraints\\Recaptcha3: ~\n```\n\n\nOn frontend part you need to submit the captcha token along with email.\nYou can obtain the captcha token either *on page load* or *on form submit*.\n\n```html\n\u003cscript src=\"https://www.google.com/recaptcha/api.js?render=\u003csiteKey\u003e\"\u003e\u003c/script\u003e\n\n\u003cscript\u003e\nconst siteKey = '*****************-**-******-******';\n\n//either on page load\ngrecaptcha.ready(function() {\n    grecaptcha.execute(siteKey, {\n        action: 'homepage'\n    }).then(function(token) {\n        //the token will be sent on form submit\n        $('[name=\"captcha\"]').val(token);\n        //keep in mind that token expires in 120 seconds so it's better to add setTimeout.\n    });\n});\n\n//or on form post:\ngrecaptcha.ready(function() {\n    grecaptcha.execute(siteKey, {\n        action: 'homepage'\n    }).then(function(token) {\n        //submit the form\n        return http.post(url, {email, captcha: token});\n    });\n});\n\u003c/script\u003e\n```\n\n### How to show errors from the captcha's response\n\nJust add the `{{ errorCodes }}` variable to the message template:\n```\n$formBuilder-\u003eadd('captcha', Recaptcha3Type::class, [\n    'constraints' =\u003e new Recaptcha3(['message' =\u003e 'There were problems with your captcha. Please try again or contact with support and provide following code(s): {{ errorCodes }}']),\n])\n```\n\n### How to deal with functional and e2e testing:\n\nRecaptcha won't allow you to test your app efficiently unless you disable it for the environment you are testing against.\n\n```yaml\n# app/config/config.yml (or config/packages/karser_recaptcha3.yaml if using Symfony4)\nkarser_recaptcha3:\n    enabled: '%env(bool:RECAPTCHA3_ENABLED)%'\n```\n\n```bash\n#.env.test or an environment variable\nRECAPTCHA3_ENABLED=0\n```\n\n### How to set the threshold from PHP dynamically rather from the .yaml config or .env?\n\nYou should inject `@karser_recaptcha3.google.recaptcha` in your service and call `setScoreThreshold` method.\n```yaml\n#services.yaml\nApp\\Services\\YourService:\n    arguments: ['@karser_recaptcha3.google.recaptcha']\n```\n\n```php\n#App/Services/YourService.php\n\nuse Karser\\Recaptcha3Bundle\\ReCaptcha\\ReCaptcha;\n\nclass YourService {\n    private $reCaptcha;\n\n    public function __construct(ReCaptcha $reCaptcha) {\n        $this-\u003ereCaptcha = $reCaptcha;\n    }\n\n    public function yourMethod() {\n        $this-\u003ereCaptcha-\u003esetScoreThreshold(0.7);\n    }\n}\n```\n\n### How to resolve IP propertly when behind Cloudflare:\n\nFrom the [Cloudflare docs](https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-):\nTo provide the client (visitor) IP address for every request to the origin, Cloudflare adds the CF-Connecting-IP header.\n```\n\"CF-Connecting-IP: A.B.C.D\"\n```\n\nSo you can implement custom IP resolver which attempts to read the `CF-Connecting-IP` header or fallbacks with the internal IP resolver:\n \n```php\n\u003c?php declare(strict_types=1);\n\nnamespace App\\Service;\n\nuse Karser\\Recaptcha3Bundle\\Services\\IpResolverInterface;\nuse Symfony\\Component\\HttpFoundation\\RequestStack;\n\nclass CloudflareIpResolver implements IpResolverInterface\n{\n    /** @var IpResolverInterface */\n    private $decorated;\n\n    /** @var RequestStack */\n    private $requestStack;\n\n    public function __construct(IpResolverInterface $decorated, RequestStack $requestStack)\n    {\n        $this-\u003edecorated = $decorated;\n        $this-\u003erequestStack = $requestStack;\n    }\n\n    public function resolveIp(): ?string\n    {\n        return $this-\u003edoResolveIp() ?? $this-\u003edecorated-\u003eresolveIp();\n    }\n\n    private function doResolveIp(): ?string\n    {\n        $request = $this-\u003erequestStack-\u003egetCurrentRequest();\n        if ($request === null) {\n            return null;\n        }\n        return $request-\u003eserver-\u003eget('HTTP_CF_CONNECTING_IP');\n    }\n}\n```\n\nHere is the service declaration. It decorates the internal resolver:\n```yaml\n#services.yaml\nservices:\n    App\\Service\\CloudflareIpResolver:\n        decorates: 'karser_recaptcha3.ip_resolver'\n        arguments:\n            $decorated: '@App\\Service\\CloudflareIpResolver.inner'\n            $requestStack: '@request_stack'\n```\n\n### Symfony HttpClient integration\n\nIf you have a dependency on `symfony/http-client` in your application then it will be automatically wired\nto use via `RequestMethod/SymfonyHttpClient`.\n\nTroubleshooting checklist\n-------------------------\n\n### Make sure you setup recaptcha key/secret of version 3.\nAlso, make sure you added the domain you use in the [recaptcha settings](https://www.google.com/recaptcha/admin).\nUsually dev domain differs from the production one, so better to double check.\n![image](https://user-images.githubusercontent.com/1675033/71197630-bbd7a000-229a-11ea-9421-7205d2e6f52c.png)\n\n### Make sure you are seeing this in the html of your rendered form\n```\n\u003cinput type=\"hidden\" id=\"form_captcha\" name=\"form[captcha]\" /\u003e\u003cscript\u003e\n    var recaptchaCallback_form_captcha = function() {\n    grecaptcha.execute('\u003cYOUR-RECAPTCHA-KEY\u003e', {action: 'landing'}).then(function(token) {\n    document.getElementById('form_captcha').value = token;\n    });\n    };\n    \u003c/script\u003e\u003cscript src=\"https://www.google.com/recaptcha/api.js?render=\u003cYOUR-RECAPTCHA-KEY\u003e\u0026onload=recaptchaCallback_form_captcha\" async defer\u003e\u003c/script\u003e \n\u003c/form\u003e\n```\n\n### Make sure you don't have javascript errors in the browser console\n\n\nTesting\n-------\n\n```\ncomposer update\nvendor/bin/phpunit\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarser%2Fkarserrecaptcha3bundle","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarser%2Fkarserrecaptcha3bundle","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarser%2Fkarserrecaptcha3bundle/lists"}