Skip to content

Commit 7294674

Browse files
rokostikgreenberga
andauthored
Support risk intelligence (#16)
* add risk intelligence on siteverify * add risk intelligence /retrieve * bump required python version * fix actions * use importlib.metadata instead of deprecated pkg_resources * bump version * format * fix tests * support new response format * support optional sitekey * save raw risk intelligence response * always use latest version in examples and add comment * Bump pydantic version * Fix deprecation warnings from pydantic bump These validators should now be instance methods rather than class methods. * Ensure response.success==False if there is an error * require >= for requests * unify the example * reformat example --------- Co-authored-by: Aaron Greenberg <aaron@friendlycaptcha.com>
1 parent 0e9bbb4 commit 7294674

15 files changed

Lines changed: 1199 additions & 199 deletions

File tree

.github/workflows/integration.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- name: Set up Python
1818
uses: actions/setup-python@v2
1919
with:
20-
python-version: 3.7
20+
python-version: "3.10"
2121

2222
- name: Run the SDK testserver
2323
run: |

.github/workflows/unit.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
- name: Set up Python
1717
uses: actions/setup-python@v4
1818
with:
19-
python-version: 3.9
19+
python-version: "3.10"
2020

2121
- name: Install Dependencies
2222
working-directory: ./

README.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ For a more detailed example, take a look at the [example](./example) directory.
2121
To start using the client:
2222

2323
```python
24-
from friendly_client import FriendlyCaptchaClient
24+
from friendly_captcha_client.client import FriendlyCaptchaClient
2525

2626
client = FriendlyCaptchaClient(
2727
api_key="YOUR_API_KEY",
@@ -71,14 +71,30 @@ print(result.should_accept) # False
7171
print(result.was_able_to_verify) # False
7272
```
7373

74+
### Risk Intelligence Data Retrieval
75+
76+
Use `retrieve_risk_intelligence` to retrieve risk intelligence data from a token.
77+
78+
```python
79+
result = client.retrieve_risk_intelligence("RISK_INTELLIGENCE_TOKEN_HERE")
80+
if not result.was_able_to_retrieve:
81+
# handle request/client error, inspect result.error
82+
return
83+
if not result.is_valid:
84+
# handle invalid token, inspect result.error
85+
return
86+
print(result.data.risk_intelligence) # The risk intelligence data
87+
```
88+
7489
### Configuration
7590

7691
The client offers several configuration options:
7792

7893
- **api_key**: Your Friendly Captcha API key.
79-
- **sitekey**: Your Friendly Captcha sitekey.
94+
- **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.
8095
- **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.
81-
- **siteverify_endpoint**: (Optional) The endpoint URL for the site verification API. Shorthands `eu` or `global` are also accepted. Default is `global`.
96+
- **api_endpoint**: (Optional) Base API endpoint (for example `https://eu.frcapi.com`). Shorthands `eu` or `global` are also accepted. Default is `global`.
97+
- **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`.
8298
- **verbose**: (Optional) Default is False. Turn on basic logging.
8399
- 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.
84100

example/README.md

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,15 @@
11
# Friendly Captcha FastAPI Example
22

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

56
### Requirements
67

7-
- Python 3.9+
8+
- Python 3.10+
89
- Your Friendly Captcha API key and sitekey.
910

1011
### Start the application
1112

12-
- Clone the repository:
13-
14-
```bash
15-
git clone <repository_url>
16-
cd <repository_directory>
17-
```
18-
1913
- Set up a virtual environment (recommended):
2014

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

27-
- Setup env variables and start the application
21+
- Set environment variables and start the application
2822

29-
> 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.
23+
> 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.
24+
> For the frontend `data-api-endpoint`, use the base endpoint (for example `http://localhost:8182`), not `/api/v2/captcha`.
3025
3126
```bash
32-
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
27+
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
3328
```
3429

35-
# Usage
30+
## Usage
3631

3732
Navigate to http://localhost:8000/ in your browser.
38-
Fill out the form and submit. The Friendly Captcha verification will protect the form from bots.
33+
Fill out the form and submit. The backend verifies the captcha and also retrieves risk intelligence data when a risk intelligence token is available.

example/main.py

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
1+
import json
12
import os
23

3-
from fastapi import FastAPI, Request, Form
4+
from fastapi import FastAPI, Form, Request
45
from fastapi.templating import Jinja2Templates
5-
from friendly_captcha_client.client import FriendlyCaptchaClient, FriendlyCaptchaResult
66

7-
app = FastAPI()
7+
from friendly_captcha_client.client import (
8+
FriendlyCaptchaClient,
9+
FriendlyCaptchaResult,
10+
RiskIntelligenceRetrieveResult,
11+
)
812

13+
app = FastAPI()
914
templates = Jinja2Templates(directory="./templates/")
1015

1116
FRC_SITEKEY = os.getenv("FRC_SITEKEY")
1217
FRC_APIKEY = os.getenv("FRC_APIKEY")
1318

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

1824
if not FRC_SITEKEY or not FRC_APIKEY:
@@ -24,11 +30,42 @@
2430
frc_client = FriendlyCaptchaClient(
2531
api_key=FRC_APIKEY,
2632
sitekey=FRC_SITEKEY,
27-
siteverify_endpoint=FRC_SITEVERIFY_ENDPOINT, # Optional, defaults to "global"
33+
api_endpoint=FRC_API_ENDPOINT, # Optional, defaults to "global"
2834
strict=False,
2935
)
3036

3137

38+
def retrieve_risk_intelligence_if_available(token: str) -> None:
39+
token = (token or "").strip()
40+
if not token:
41+
print("No risk intelligence token found in form data, skipping retrieval.")
42+
return
43+
44+
result: RiskIntelligenceRetrieveResult = frc_client.retrieve_risk_intelligence(
45+
token
46+
)
47+
if not result.was_able_to_retrieve:
48+
print("Failed to retrieve risk intelligence:", result.error)
49+
return
50+
51+
if not result.is_valid:
52+
print("Risk intelligence token is invalid:", result.error)
53+
return
54+
55+
if result.data is None:
56+
print("Risk intelligence retrieval succeeded, but no data was returned.")
57+
return
58+
59+
if result.data.risk_intelligence_raw is None:
60+
print("Token was valid, but risk intelligence data was not returned.")
61+
return
62+
63+
print("Risk Intelligence Data:")
64+
print(json.dumps(result.data.risk_intelligence_raw, indent=2))
65+
print("Token data:")
66+
print(result.data.token)
67+
68+
3269
@app.get("/")
3370
def read_root(request: Request):
3471
return templates.TemplateResponse(
@@ -48,15 +85,18 @@ def post_form(
4885
subject: str = Form(None),
4986
message: str = Form(None),
5087
frc_captcha_response: str = Form(..., alias="frc-captcha-response"),
88+
frc_risk_intelligence_token: str = Form("", alias="frc-risk-intelligence-token"),
5189
):
90+
retrieve_risk_intelligence_if_available(frc_risk_intelligence_token)
91+
5292
result: FriendlyCaptchaResult = frc_client.verify_captcha_response(
5393
frc_captcha_response
5494
)
5595

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

61101
if result.is_client_error:
62102
# Something is wrong with our configuration, check your API key!

example/requirements.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,7 @@ fastapi==0.101.1
22
uvicorn==0.26.0
33
jinja2==3.1.3
44
python-multipart==0.0.6
5-
friendly-captcha-client==0.0.2
5+
6+
# To use the example outside of this repository, replace the line below with the latest
7+
# `friendly-captcha-client==x.y.z` version.
8+
-e ..

example/templates/demo.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,8 @@
4747
}
4848
</style>
4949

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

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

7170
<div class="frc-captcha" data-sitekey="{{ sitekey }}" {% if widget_endpoint
7271
%}data-api-endpoint="{{ widget_endpoint }}" {% endif %}></div>
72+
<div class="frc-risk-intelligence" data-sitekey="{{ sitekey }}" {% if widget_endpoint
73+
%}data-api-endpoint="{{ widget_endpoint }}" {% endif %}></div>
7374
<input style="margin-top: 1em" type="submit" value="Submit" />
7475
</div>
7576
</form>

0 commit comments

Comments
 (0)