Skip to content
Open
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
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pip install friendly-captcha-client

Below are some basic examples of how to use the client.

For a more detailed example, take a look at the [example](./example) directory.
For a more detailed examples, take a look at the [example](./example) directory.

### Initialization

Expand Down Expand Up @@ -71,14 +71,26 @@ 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")
print(result.was_able_to_retrieve) # True/False
print(result.is_client_error) # True when the API responded with a client-side error
print(result.data) # The risk intelligence data
```

### Configuration

The client offers several configuration options:

- **api_key**: Your Friendly Captcha API key.
- **sitekey**: Your Friendly Captcha 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
7 changes: 4 additions & 3 deletions example/README.md → example/captcha/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This application integrates Friendly Captcha for form submissions using FastAPI.

### Requirements

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

### Start the application
Expand All @@ -26,10 +26,11 @@ pip install -r requirements.txt

- Setup env 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, the default values will be 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
Expand Down
5 changes: 3 additions & 2 deletions example/main.py → example/captcha/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
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,7 +25,7 @@
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,
)

Expand Down
9 changes: 9 additions & 0 deletions example/captcha/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fastapi==0.101.1
uvicorn==0.26.0
jinja2==3.1.3
python-multipart==0.0.6
# TODO uncomment and bump version when the new SDK version is released
# friendly-captcha-client==0.0.2

# TODO remove this line when the new SDK version is released
-e ../..
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 Down Expand Up @@ -81,4 +80,4 @@ <h1>Friendly Captcha Python SDK form</h1>
</script>
</body>

</html>
</html>
5 changes: 0 additions & 5 deletions example/requirements.txt

This file was deleted.

37 changes: 37 additions & 0 deletions example/risk-intelligence/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Friendly Captcha Python Risk Intelligence Example

This example demonstrates server-side risk intelligence retrieval with `retrieve_risk_intelligence`.

### Requirements

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

### Start the application

- Set up a virtual environment (recommended):

```bash
python -m venv venv
source venv/bin/activate # On Windows, use `venv\Scripts\activate`
pip install -r requirements.txt
```

- Set environment variables and start the application

> NOTE: `FRC_API_ENDPOINT` and `FRC_AGENT_ENDPOINT` are optional. If not set, default values are used. You can also use `global` or `eu` as shorthands.

```bash
FRC_APIKEY=<your API key> \
FRC_SITEKEY=<your sitekey> \
FRC_API_ENDPOINT=<api endpoint> \
FRC_AGENT_ENDPOINT=<agent endpoint> \
uvicorn main:app --reload --port 8000
```

## Usage

Navigate to http://localhost:8000/ in your browser.
The token generation starts automatically. Submit the form to retrieve the risk intelligence data server-side.
Tokens are cached in the browser for the duration of their validity period so refreshing the page does not regenerate the token.
You can regenerate the token by clicking the "Regenerate Token" button.
103 changes: 103 additions & 0 deletions example/risk-intelligence/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import json
import os

from fastapi import FastAPI, Form, Request
from fastapi.templating import Jinja2Templates

from friendly_captcha_client.client import (
FriendlyCaptchaClient,
RiskIntelligenceRetrieveResult,
)

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

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

# Optional: "global", "eu", or a full API base endpoint like "https://eu.frcapi.com".
FRC_API_ENDPOINT = os.getenv("FRC_API_ENDPOINT")
# Optional: SDK/agent endpoint used in the browser widget.
FRC_AGENT_ENDPOINT = os.getenv("FRC_AGENT_ENDPOINT")

if not FRC_SITEKEY or not FRC_APIKEY:
print(
"Please set FRC_SITEKEY and FRC_APIKEY before running this example to your Friendly Captcha sitekey and API key respectively."
)
exit(1)

frc_client = FriendlyCaptchaClient(
api_key=FRC_APIKEY,
sitekey=FRC_SITEKEY,
api_endpoint=FRC_API_ENDPOINT,
strict=False,
)


def _render_template(request: Request, **values):
return templates.TemplateResponse("demo.html", {"request": request, **values})


def _base_template_data():
return {
"message": "",
"sitekey": FRC_SITEKEY,
"agent_endpoint": FRC_AGENT_ENDPOINT or "",
"risk_token": "",
"token_timestamp": "",
"token_expires_at": "",
"token_num_uses": "",
"risk_intelligence_raw": "",
}


@app.get("/")
def read_root(request: Request):
return _render_template(request, **_base_template_data())


@app.post("/")
def post_form(
request: Request,
frc_risk_intelligence_token: str = Form("", alias="frc-risk-intelligence-token"),
):
data = _base_template_data()

risk_token = (frc_risk_intelligence_token or "").strip()
if not risk_token:
data["message"] = "No risk intelligence token found."
return _render_template(request, **data)

data["risk_token"] = risk_token

result: RiskIntelligenceRetrieveResult = frc_client.retrieve_risk_intelligence(
risk_token
)
if not result.was_able_to_retrieve:
data["message"] = "Risk intelligence retrieval failed: {}".format(result.error)
return _render_template(request, **data)

if result.data is None:
data["message"] = (
"Risk intelligence retrieve succeeded, but no data was returned."
)
return _render_template(request, **data)

if result.data.risk_intelligence is None:
data["message"] = (
"Token was valid, but risk intelligence data was not returned."
)
data["token_timestamp"] = result.data.details.timestamp
data["token_expires_at"] = result.data.details.expires_at
data["token_num_uses"] = result.data.details.num_uses
return _render_template(request, **data)

data["message"] = "Retrieved risk intelligence data successfully."
data["token_timestamp"] = result.data.details.timestamp
data["token_expires_at"] = result.data.details.expires_at
data["token_num_uses"] = result.data.details.num_uses
data["risk_intelligence_raw"] = json.dumps(
result.data.risk_intelligence.model_dump(by_alias=True),
indent=2,
)
return _render_template(request, **data)
9 changes: 9 additions & 0 deletions example/risk-intelligence/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fastapi==0.101.1
uvicorn==0.26.0
jinja2==3.1.3
python-multipart==0.0.6
# TODO uncomment and bump version when the new SDK version is released
# friendly-captcha-client==0.0.2

# TODO remove this line when the new SDK version is released
-e ../..
Loading