Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: 3.7
python-version: "3.10"

- name: Run the SDK testserver
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/unit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: 3.9
python-version: "3.10"

- name: Install Dependencies
working-directory: ./
Expand Down
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ For a more detailed example, take a look at the [example](./example) directory.
To start using the client:

```python
from friendly_client import FriendlyCaptchaClient
from friendly_captcha_client.client import FriendlyCaptchaClient

client = FriendlyCaptchaClient(
api_key="YOUR_API_KEY",
Expand Down Expand Up @@ -71,14 +71,30 @@ print(result.should_accept) # False
print(result.was_able_to_verify) # False
```

### Risk Intelligence Data Retrieval

Use `retrieve_risk_intelligence` to retrieve risk intelligence data from a token.

```python
result = client.retrieve_risk_intelligence("RISK_INTELLIGENCE_TOKEN_HERE")
if not result.was_able_to_retrieve:
# handle request/client error, inspect result.error
return
if not result.is_valid:
# handle invalid token, inspect result.error
return
print(result.data.risk_intelligence) # The risk intelligence data
```

### Configuration

The client offers several configuration options:

- **api_key**: Your Friendly Captcha API key.
- **sitekey**: Your Friendly Captcha sitekey.
- **sitekey**: (Optional) Your Friendly Captcha sitekey. Configure this if you want to ensure that a captcha solution or risk intelligence token was generated from a specific sitekey.
- **strict**: (Optional) In case the client was not able to verify the captcha response at all (for example if there is a network failure or a mistake in configuration), by default the `verify_captcha_response` returns `True` regardless. By passing `strict=True`, it will return `False` instead: every response needs to be strictly verified.
- **siteverify_endpoint**: (Optional) The endpoint URL for the site verification API. Shorthands `eu` or `global` are also accepted. Default is `global`.
- **api_endpoint**: (Optional) Base API endpoint (for example `https://eu.frcapi.com`). Shorthands `eu` or `global` are also accepted. Default is `global`.
- **siteverify_endpoint**: (Optional,Deprecated) Kept for backwards compatibility, use `api_endpoint` instead. Accepts a full siteverify URL or shorthands `eu`/`global`; path is stripped and converted to `api_endpoint`.
- **verbose**: (Optional) Default is False. Turn on basic logging.
- Error Handling: The client has built-in error handling mechanisms. In case of unexpected responses or errors from the Friendly Captcha API, the client will log the error and provide a default response.

Expand Down
21 changes: 8 additions & 13 deletions example/README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,15 @@
# Friendly Captcha FastAPI Example

This application integrates Friendly Captcha for form submissions using FastAPI.
It verifies captcha responses and retrieves risk intelligence (if enabled on the application) from the same form flow.

### Requirements

- Python 3.9+
- Python 3.10+
- Your Friendly Captcha API key and sitekey.

### Start the application

- Clone the repository:

```bash
git clone <repository_url>
cd <repository_directory>
```

- Set up a virtual environment (recommended):

```bash
Expand All @@ -24,15 +18,16 @@ source venv/bin/activate # On Windows, use `venv\Scripts\activate`
pip install -r requirements.txt
```

- Setup env variables and start the application
- Set environment variables and start the application

> NOTE: `FRC_SITEVERIFY_ENDPOINT` and `FRC_WIDGET_ENDPOINT` are optional. If not set, the default values will be used. You can also use `global` or `eu` as shorthands for both.
> NOTE: `FRC_API_ENDPOINT` and `FRC_WIDGET_ENDPOINT` are optional. If not set, default values are used. You can also use `global` or `eu` as shorthands for both.
> For the frontend `data-api-endpoint`, use the base endpoint (for example `http://localhost:8182`), not `/api/v2/captcha`.

```bash
FRC_APIKEY=<your API key> FRC_SITEKEY=<your sitekey> FRC_SITEVERIFY_ENDPOINT=<siteverify endpoint> FRC_WIDGET_ENDPOINT=<widget endpoint> uvicorn main:app --reload --port 8000
FRC_APIKEY=<your API key> FRC_SITEKEY=<your sitekey> FRC_API_ENDPOINT=<api endpoint> FRC_WIDGET_ENDPOINT=<widget endpoint> uvicorn main:app --reload --port 8000
```

# Usage
## Usage

Navigate to http://localhost:8000/ in your browser.
Fill out the form and submit. The Friendly Captcha verification will protect the form from bots.
Fill out the form and submit. The backend verifies the captcha and also retrieves risk intelligence data when a risk intelligence token is available.
54 changes: 47 additions & 7 deletions example/main.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
import json
import os

from fastapi import FastAPI, Request, Form
from fastapi import FastAPI, Form, Request
from fastapi.templating import Jinja2Templates
from friendly_captcha_client.client import FriendlyCaptchaClient, FriendlyCaptchaResult

app = FastAPI()
from friendly_captcha_client.client import (
FriendlyCaptchaClient,
FriendlyCaptchaResult,
RiskIntelligenceRetrieveResult,
)

app = FastAPI()
templates = Jinja2Templates(directory="./templates/")

FRC_SITEKEY = os.getenv("FRC_SITEKEY")
FRC_APIKEY = os.getenv("FRC_APIKEY")

# Optionally we can pass in custom endpoints to be used, such as "eu".
FRC_SITEVERIFY_ENDPOINT = os.getenv("FRC_SITEVERIFY_ENDPOINT")
FRC_API_ENDPOINT = os.getenv("FRC_API_ENDPOINT")
# Optional: frontend widget endpoint used for data-api-endpoint.
FRC_WIDGET_ENDPOINT = os.getenv("FRC_WIDGET_ENDPOINT")

if not FRC_SITEKEY or not FRC_APIKEY:
Expand All @@ -24,11 +30,42 @@
frc_client = FriendlyCaptchaClient(
api_key=FRC_APIKEY,
sitekey=FRC_SITEKEY,
siteverify_endpoint=FRC_SITEVERIFY_ENDPOINT, # Optional, defaults to "global"
api_endpoint=FRC_API_ENDPOINT, # Optional, defaults to "global"
strict=False,
)


def retrieve_risk_intelligence_if_available(token: str) -> None:
token = (token or "").strip()
if not token:
print("No risk intelligence token found in form data, skipping retrieval.")
return

result: RiskIntelligenceRetrieveResult = frc_client.retrieve_risk_intelligence(
token
)
if not result.was_able_to_retrieve:
print("Failed to retrieve risk intelligence:", result.error)
return

if not result.is_valid:
print("Risk intelligence token is invalid:", result.error)
return

if result.data is None:
print("Risk intelligence retrieval succeeded, but no data was returned.")
return

if result.data.risk_intelligence_raw is None:
print("Token was valid, but risk intelligence data was not returned.")
return

print("Risk Intelligence Data:")
print(json.dumps(result.data.risk_intelligence_raw, indent=2))
print("Token data:")
print(result.data.token)


@app.get("/")
def read_root(request: Request):
return templates.TemplateResponse(
Expand All @@ -48,15 +85,18 @@ def post_form(
subject: str = Form(None),
message: str = Form(None),
frc_captcha_response: str = Form(..., alias="frc-captcha-response"),
frc_risk_intelligence_token: str = Form("", alias="frc-risk-intelligence-token"),
):
retrieve_risk_intelligence_if_available(frc_risk_intelligence_token)

result: FriendlyCaptchaResult = frc_client.verify_captcha_response(
frc_captcha_response
)

if not result.was_able_to_verify:
# In this case we were not actually able to verify the response embedded in the form, but we may still want to accept it.
# It could mean there is a network issue or that the service is down. In those cases you generally want to accept submissions anyhow
# That's why we use `shouldAccept()` below to actually accept or reject the form submission. It will return true in these cases.
# It could mean there is a network issue or that the service is down. In those cases you generally want to accept submissions anyhow.
# That's why we use `should_accept` below to actually accept or reject the form submission. It will return true in these cases.

if result.is_client_error:
# Something is wrong with our configuration, check your API key!
Expand Down
5 changes: 4 additions & 1 deletion example/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ fastapi==0.101.1
uvicorn==0.26.0
jinja2==3.1.3
python-multipart==0.0.6
friendly-captcha-client==0.0.2

# To use the example outside of this repository, replace the line below with the latest
# `friendly-captcha-client==x.y.z` version.
-e ..
7 changes: 4 additions & 3 deletions example/templates/demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,8 @@
}
</style>

<script type="module" src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.1.8/site.min.js" async defer></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.1.8/site.compat.min.js" async
defer></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk/site.min.js" async defer></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk/site.compat.min.js" async defer></script>

<!-- You can change the data-api-endpoint via this tag. More info here https://developer.friendlycaptcha.com/docs/sdk/configuration -->
<!-- <meta name="frc-api-endpoint" content="."> -->
Expand All @@ -70,6 +69,8 @@ <h1>Friendly Captcha Python SDK form</h1>

<div class="frc-captcha" data-sitekey="{{ sitekey }}" {% if widget_endpoint
%}data-api-endpoint="{{ widget_endpoint }}" {% endif %}></div>
<div class="frc-risk-intelligence" data-sitekey="{{ sitekey }}" {% if widget_endpoint
%}data-api-endpoint="{{ widget_endpoint }}" {% endif %}></div>
<input style="margin-top: 1em" type="submit" value="Submit" />
</div>
</form>
Expand Down
Loading
Loading