{"id":48573430,"url":"https://github.com/llybin/drf-recaptcha","last_synced_at":"2026-04-08T15:34:54.491Z","repository":{"id":36466632,"uuid":"225683219","full_name":"llybin/drf-recaptcha","owner":"llybin","description":"Django REST framework reCAPTCHA","archived":false,"fork":false,"pushed_at":"2025-09-15T11:15:58.000Z","size":111,"stargazers_count":95,"open_issues_count":2,"forks_count":16,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-05T20:14:54.344Z","etag":null,"topics":["django","django-rest-framework","python","recaptcha","recaptcha-v2","recaptcha-v3"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/drf-recaptcha/","language":"Python","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/llybin.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["llybin"]}},"created_at":"2019-12-03T17:57:00.000Z","updated_at":"2025-09-15T11:16:01.000Z","dependencies_parsed_at":"2024-06-18T20:04:43.375Z","dependency_job_id":"b359a836-6e99-4ab7-af35-e052a317f23d","html_url":"https://github.com/llybin/drf-recaptcha","commit_stats":{"total_commits":59,"total_committers":7,"mean_commits":8.428571428571429,"dds":0.1694915254237288,"last_synced_commit":"84bade5b93fe2383cd3eb56e06a2d05823156693"},"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"purl":"pkg:github/llybin/drf-recaptcha","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llybin%2Fdrf-recaptcha","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llybin%2Fdrf-recaptcha/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llybin%2Fdrf-recaptcha/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llybin%2Fdrf-recaptcha/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/llybin","download_url":"https://codeload.github.com/llybin/drf-recaptcha/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/llybin%2Fdrf-recaptcha/sbom","scorecard":{"id":595929,"data":{"date":"2025-08-11","repo":{"name":"github.com/llybin/drf-recaptcha","commit":"9b89c69f72c7718a30e3f86602a053612dde93ad"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.9,"checks":[{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":3,"reason":"Found 9/30 approved changesets -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: topLevel 'contents' permission set to 'read': .github/workflows/publish.yml:8","Warn: no topLevel permission defined: .github/workflows/ruff.yaml:1","Warn: no topLevel permission defined: .github/workflows/tests.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/llybin/drf-recaptcha/publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/publish.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/llybin/drf-recaptcha/publish.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/publish.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/llybin/drf-recaptcha/publish.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ruff.yaml:7: update your workflow using https://app.stepsecurity.io/secureworkflow/llybin/drf-recaptcha/ruff.yaml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ruff.yaml:8: update your workflow using https://app.stepsecurity.io/secureworkflow/llybin/drf-recaptcha/ruff.yaml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ruff.yaml:9: update your workflow using https://app.stepsecurity.io/secureworkflow/llybin/drf-recaptcha/ruff.yaml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tests.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/llybin/drf-recaptcha/tests.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/tests.yml:29: update your workflow using https://app.stepsecurity.io/secureworkflow/llybin/drf-recaptcha/tests.yml/master?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/publish.yml:24","Warn: pipCommand not pinned by hash: .github/workflows/publish.yml:25","Warn: pipCommand not pinned by hash: .github/workflows/tests.yml:34","Warn: pipCommand not pinned by hash: .github/workflows/tests.yml:35","Warn: pipCommand not pinned by hash: .github/workflows/tests.yml:45","Info:   0 out of   5 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   3 third-party GitHubAction dependencies pinned","Info:   0 out of   5 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/publish.yml:11"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":2,"reason":"SAST tool is not run on all commits -- score normalized to 2","details":["Warn: 3 commits out of 14 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":3,"reason":"7 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2025-47 / GHSA-7xr5-9hcq-chf9","Warn: Project is vulnerable to: PYSEC-2024-156 / GHSA-8498-2h75-472j","Warn: Project is vulnerable to: PYSEC-2025-37 / GHSA-8j24-cjrq-gr2m","Warn: Project is vulnerable to: PYSEC-2024-157 / GHSA-m9g8-fxxm-xg86","Warn: Project is vulnerable to: PYSEC-2025-13 / GHSA-p3fp-8748-vqfq","Warn: Project is vulnerable to: PYSEC-2025-1 / GHSA-qcgg-j2x8-h9g8","Warn: Project is vulnerable to: PYSEC-2025-14 / GHSA-wqfg-m96j-85vm"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-20T23:09:05.216Z","repository_id":36466632,"created_at":"2025-08-20T23:09:05.216Z","updated_at":"2025-08-20T23:09:05.216Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31562691,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["django","django-rest-framework","python","recaptcha","recaptcha-v2","recaptcha-v3"],"created_at":"2026-04-08T15:34:54.404Z","updated_at":"2026-04-08T15:34:54.472Z","avatar_url":"https://github.com/llybin.png","language":"Python","funding_links":["https://github.com/sponsors/llybin"],"categories":[],"sub_categories":[],"readme":"# Django REST reCAPTCHA\n\n**Django REST reCAPTCHA v2 and v3 field serializer**\n\n[![Donate](https://img.shields.io/github/sponsors/llybin?style=flat-square)](https://github.com/sponsors/llybin)\n[![CI](https://github.com/llybin/drf-recaptcha/workflows/tests/badge.svg)](https://github.com/llybin/drf-recaptcha/actions)\n[![Codacy Badge](https://api.codacy.com/project/badge/Grade/a9b44d24cba74c75bca6472b2ee8da67)](https://www.codacy.com/app/llybin/drf-recaptcha?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=llybin/drf-recaptcha\u0026amp;utm_campaign=Badge_Grade)\n[![Codacy Badge](https://api.codacy.com/project/badge/Coverage/a9b44d24cba74c75bca6472b2ee8da67)](https://www.codacy.com/app/llybin/drf-recaptcha?utm_source=github.com\u0026amp;utm_medium=referral\u0026amp;utm_content=llybin/drf-recaptcha\u0026amp;utm_campaign=Badge_Coverage)\n[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)\n[![PyPI](https://img.shields.io/pypi/v/drf-recaptcha)](https://pypi.org/project/drf-recaptcha/)\n[![PyPI - Downloads](https://img.shields.io/pypi/dm/drf-recaptcha)](https://pypi.org/project/drf-recaptcha/)\n[![PyPI - License](https://img.shields.io/pypi/l/drf-recaptcha)](https://pypi.org/project/drf-recaptcha/)\n\n\n## Requirements\n\n* Python: 3.10, 3.11, 3.12, 3.13\n* Django: 4.2, 5.0, 5.1, 5.2\n* DRF: 3.14, 3.15, 3.16\n\n## Installation\n\n1. [Sign up for reCAPTCHA](https://www.google.com/recaptcha/)\n2. Install with `pip install drf-recaptcha`\n3. Add `\"drf_recaptcha\"` to your `INSTALLED_APPS` settings.\n4. Set in settings `DRF_RECAPTCHA_SECRET_KEY`\n\n```python\nINSTALLED_APPS = [\n    ...,\n    \"drf_recaptcha\",\n    ...,\n]\n\n...\n\nDRF_RECAPTCHA_SECRET_KEY = \"YOUR SECRET KEY\"\n```\n\n## Usage\n\n```python\nfrom rest_framework.serializers import Serializer, ModelSerializer\nfrom drf_recaptcha.fields import ReCaptchaV2Field, ReCaptchaV3Field\nfrom feedback.models import Feedback\n\n\nclass V2Serializer(Serializer):\n    recaptcha = ReCaptchaV2Field()\n    ...\n\n\nclass GetOTPView(APIView):\n    def post(self, request):\n        serializer = V2Serializer(data=request.data, context={\"request\": request})\n        serializer.is_valid(raise_exception=True)\n        ...\n\n\nclass V3Serializer(Serializer):\n    recaptcha = ReCaptchaV3Field(action=\"example\")\n    ...\n\n\nclass V3WithScoreSerializer(Serializer):\n    recaptcha = ReCaptchaV3Field(\n        action=\"example\",\n        required_score=0.6,\n    )\n    ...\n\n\nclass GetReCaptchaScore(APIView):\n    def post(self, request):\n        serializer = V3WithScoreSerializer(data=request.data, context={\"request\": request})\n        serializer.is_valid()\n        score = serializer.fields['recaptcha'].score\n        ...\n\n\nclass FeedbackSerializer(ModelSerializer):\n    recaptcha = ReCaptchaV2Field()\n\n    class Meta:\n        model = Feedback\n        fields = (\"phone\", \"full_name\", \"email\", \"comment\", \"recaptcha\")\n\n    def validate(self, attrs):\n        attrs.pop(\"recaptcha\")\n        ...\n        return attrs\n\n\nclass DynamicContextSecretKey(APIView):\n    def post(self, request):\n        if request.platform == \"android\":\n            recaptcha_secret_key = \"SPECIAL_FOR_ANDROID\"\n        else:\n            recaptcha_secret_key = \"SPECIAL_FOR_IOS\"\n        serializer = WithReCaptchaSerializer(\n            data=request.data,\n            context={\n                \"request\": request,\n                \"recaptcha_secret_key\": recaptcha_secret_key,\n            },\n        )\n        serializer.is_valid(raise_exception=True)\n        ...\n\n\nclass DynamicContextSecretKey(GenericAPIView):\n    serializer_class = WithReCaptchaSerializer\n\n    def get_serializer_context(self):\n        if self.request.platform == \"android\":\n            recaptcha_secret_key = \"SPECIAL_FOR_ANDROID\"\n        else:\n            recaptcha_secret_key = \"SPECIAL_FOR_IOS\"\n        context = super().get_serializer_context()\n        context.update({\"recaptcha_secret_key\": recaptcha_secret_key})\n        return context\n\n\nclass MobileSerializer(Serializer):\n    recaptcha = ReCaptchaV3Field(secret_key=\"SPECIAL_MOBILE_KEY\", action=\"feedback\")\n    ...\n```\n\n## Settings\n\n`DRF_RECAPTCHA_SECRET_KEY` - set your Google reCAPTCHA secret key. Type: str.\n\n`DRF_RECAPTCHA_DEFAULT_V3_SCORE` - by default: `0.5`. Type: float.\n\n`DRF_RECAPTCHA_ACTION_V3_SCORES` - by default: `{}`. Type: dict. You can define specific score for each action e.g.\n`{\"login\": 0.6, \"feedback\": 0.3}`\n\n`DRF_RECAPTCHA_DOMAIN` - by default: `www.google.com`. Type: str.\n\n`DRF_RECAPTCHA_PROXY` - by default: `{}`. Type: dict. e.g.\n`{'http': 'http://127.0.0.1:8000', 'https': 'https://127.0.0.1:8000'}`\n\n`DRF_RECAPTCHA_VERIFY_REQUEST_TIMEOUT` - by default: `10`. Type: int.\n\n### Priority of secret_key value\n\n1. settings `DRF_RECAPTCHA_SECRET_KEY`\n2. the argument `secret_key` of field\n3. request.context[\"recaptcha_secret_key\"]\n\n### Silence the check error\n\nIf you need to disable the error, you can do so using the django settings.\n\n```python\nSILENCED_SYSTEM_CHECKS = ['drf_recaptcha.checks.recaptcha_system_check']\n```\n\n## reCAPTCHA v3\n\nValidation is passed if the score value returned by Google is greater than or equal to required score.\n\nRequired score value: `0.0 - 1.0`\n\n### Priority of score value\n\nIf not defined or zero in current item then value from next item.\n\n1. Value for action in settings `DRF_RECAPTCHA_ACTION_V3_SCORES`\n2. Value in argument `required_score` of field\n3. Default value in settings `DRF_RECAPTCHA_DEFAULT_V3_SCORE`\n4. Default value `0.5`\n\n## Testing\n\nSet `DRF_RECAPTCHA_TESTING=True` in settings, no request to Google, no warnings, `DRF_RECAPTCHA_SECRET_KEY` is not\nrequired, set returning verification result in setting below.\n\n`DRF_RECAPTCHA_TESTING_PASS=True|False` - all responses are pass, default `True`.\n\nUse `from django.test import override_settings`\n\n## Credits\n\n[django-recaptcha](https://github.com/praekelt/django-recaptcha)\n\nreCAPTCHA copyright 2012 Google.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fllybin%2Fdrf-recaptcha","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fllybin%2Fdrf-recaptcha","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fllybin%2Fdrf-recaptcha/lists"}