Update docs

This commit is contained in:
2026-10-06 23:12:12 -03:00
parent 85722f1a53
commit 01f7228f6f
7 changed files with 107 additions and 111 deletions
+5 -5
View File
@@ -1,13 +1,13 @@
--- ---
title: Tune a Protection Profile title: Tune a Policy
description: description:
Learn how to tune your protection profiles to better protect your application while enhancing Learn how to tune your policies to better protect your application while enhancing your users'
your users' experiences. experiences.
--- ---
### Throttling Windows ### Throttling Windows
The first of all settings a protection profile has are the throttling windows. Throttling windows The first of all settings a policy has are the throttling windows. Throttling windows rate-limit
rate-limit challenge issuance. challenge issuance.
![Screenshot](/images/screenshot-dashboard-throttling-windows.png) ![Screenshot](/images/screenshot-dashboard-throttling-windows.png)
+12 -12
View File
@@ -37,11 +37,11 @@ Duckity serves challenges through the duckling API. It's separate from the manag
a specialized backend. The hosted endpoint of such version is at `https://api.duckity.com/d1`. a specialized backend. The hosted endpoint of such version is at `https://api.duckity.com/d1`.
Subsequent versions of this API will be named `d2`, `d3`, and so on. Subsequent versions of this API will be named `d2`, `d3`, and so on.
To get a challenge, you need to be able to make HTTP requests and a protection profile ID. Requests To get a challenge, you need to be able to make HTTP requests and a policy ID. Requests to get
to get challenges look like follows: challenges look like follows:
```http ```http
POST /d1/challenges/{protection_profile_id}/issue HTTP/1.1 POST /d1/challenges/{policy_id}/issue HTTP/1.1
Host: api.duckity.com Host: api.duckity.com
Accept: application/json Accept: application/json
``` ```
@@ -89,8 +89,8 @@ X-RateLimit-Penalty-Resets-In: 500
} }
``` ```
Other errors, like an invalid protection profile, will result in a response of similar body Other errors, like an invalid policy, will result in a response of similar body structure and with
structure and with the corresponding status code. the corresponding status code.
## Decoding the Challenge [step] ## Decoding the Challenge [step]
@@ -109,7 +109,7 @@ type Challenge = {
challenge_id: string; // The challenge's unique ID. 21-character NanoID using the default alphabet. challenge_id: string; // The challenge's unique ID. 21-character NanoID using the default alphabet.
ip: string; // The IP of the client the challenge was issued for. Only this client may submit a solution for validation. ip: string; // The IP of the client the challenge was issued for. Only this client may submit a solution for validation.
timestamp: number; // The milliseconds since UNIX epoch when the challenge was issued. timestamp: number; // The milliseconds since UNIX epoch when the challenge was issued.
protection_profile_id: string; // The ID of the protection profile this challenge was issued for. 21-character NanoID using the default alphabet. policy_id: string; // The ID of the policy this challenge was issued for. 21-character NanoID using the default alphabet.
n: number[]; // An array of 32-bit unsigned digits, most significant digit first. The Wesolowski VDF N parameter. n: number[]; // An array of 32-bit unsigned digits, most significant digit first. The Wesolowski VDF N parameter.
x: number[]; // An array of 32-bit unsigned digits, most significant digit first. The Wesolowski VDF X parameter. x: number[]; // An array of 32-bit unsigned digits, most significant digit first. The Wesolowski VDF X parameter.
t: number; // The Wesolowski VDF T parameter. t: number; // The Wesolowski VDF T parameter.
@@ -128,8 +128,8 @@ These code pieces implement the decoding as a reference.
ip: string; ip: string;
// The unix timestamp, in milliseconds, in which the challenge was issued. // The unix timestamp, in milliseconds, in which the challenge was issued.
timestamp: number; timestamp: number;
// The ID of the protection profile this challenge was issued for. // The ID of the policy this challenge was issued for.
protection_profile_id: string; policy_id: string;
// The unsigned 32-bit digits of the N Wesolowski VDF parameter, most significant digit first. // The unsigned 32-bit digits of the N Wesolowski VDF parameter, most significant digit first.
n: number[]; n: number[];
// The unsigned 32-bit digits of the X Wesolowski VDF parameter, most significant digit first. // The unsigned 32-bit digits of the X Wesolowski VDF parameter, most significant digit first.
@@ -172,8 +172,8 @@ These code pieces implement the decoding as a reference.
"""The IP of the client this challenge was issued for.""" """The IP of the client this challenge was issued for."""
timestamp: int timestamp: int
"""The UNIX timestamp in milliseconds at which this challenge was issued.""" """The UNIX timestamp in milliseconds at which this challenge was issued."""
protection_profile_id: str policy_id: str
"""The ID of the protection profile this challenge was issued for.""" """The ID of the policy this challenge was issued for."""
n: list[int] n: list[int]
"""The unsigned 32-bit digits of the N Wesolowski VDF parameter, most significant digit first.""" """The unsigned 32-bit digits of the N Wesolowski VDF parameter, most significant digit first."""
x: list[int] x: list[int]
@@ -210,7 +210,7 @@ These code pieces implement the decoding as a reference.
id=data["challenge_id"], id=data["challenge_id"],
ip=data["ip"], ip=data["ip"],
timestamp=data["timestamp"], timestamp=data["timestamp"],
protection_profile_id=data["protection_profile_id"], policy_id=data["policy_id"],
n=data["n"], n=data["n"],
x=data["x"], x=data["x"],
t=data["t"] t=data["t"]
@@ -1078,7 +1078,7 @@ the Duckling API to check whether the token is valid.
The request looks like follows: The request looks like follows:
```http ```http
POST /d1/{protection_profile_id}/validate HTTP/1.1 POST /d1/{policy_id}/validate HTTP/1.1
Host: api.duckity.com Host: api.duckity.com
Authorization: Bearer <application-id> Authorization: Bearer <application-id>
Accept: application/json Accept: application/json
+37 -41
View File
@@ -24,21 +24,21 @@ now have your first application created.
origins. If your application is not a web app, you can leave the CORS origins empty. origins. If your application is not a web app, you can leave the CORS origins empty.
</Callout> </Callout>
## Create a Protection Profile [step] ## Create a Policy [step]
Applications commonly have multiple features that need to be protected. For example, you may have a Applications commonly have multiple features that need to be protected. For example, you may have a
signup form and a login form with different protection needs. For each, you'll create one signup form and a login form with different protection needs. For each, you'll create one **policy**
**protection profile** with settings tuned to each feature's specific needs. with settings tuned to each feature's specific needs.
Each application needs at least one protection profile to work. To create your first one, press the Each application needs at least one policy to work. To create your first one, press the **+ Create
**+ Create Protection Profile** button. Policy** button.
Set the name to the name of the feature you'll be protecting, like "Sign-up Form" or "Add Friend". Set the name to the name of the feature you'll be protecting, like "Sign-up Form" or "Add Friend".
This is for you to recognize it later, it won't be displayed to your users. Once you're done filling This is for you to recognize it later, it won't be displayed to your users. Once you're done filling
it, press on **✓ Create Protection Profile**. it, press on **✓ Create Policy**.
Copy your protection profile's ID using the button at the top right corner, that's all you'll need Copy your policy's ID using the button at the top right corner, that's all you'll need to integrate
to integrate it into your application. it into your application.
<Callout> <Callout>
Don't worry about all the settings displayed for now, you'll learn to tune them later. Don't worry about all the settings displayed for now, you'll learn to tune them later.
@@ -46,9 +46,9 @@ to integrate it into your application.
## Install a Client SDK [step] ## Install a Client SDK [step]
To integrate your protection profile into your application, you'll need to install a client SDK in To integrate your policy into your application, you'll need to install a client SDK in your client
your client and validate solution tokens from your server. This is very simple to do and will take and validate solution tokens from your server. This is very simple to do and will take you little to
you little to no time. no time.
<Tabs items={["JavaScript", "React", "Python", "Rust", "Other"]} groupId="sdk-language"> <Tabs items={["JavaScript", "React", "Python", "Rust", "Other"]} groupId="sdk-language">
<Tab value="JavaScript"> <Tab value="JavaScript">
@@ -91,7 +91,7 @@ you little to no time.
```ts ```ts
async function handleSubmit(e) { async function handleSubmit(e) {
e.preventDefault(); e.preventDefault();
const solution: string = await duckity.solve(PROTECTION_PROFILE_ID); const solution: string = await duckity.solve(POLICY_ID);
// Send it to your backend. // Send it to your backend.
} }
``` ```
@@ -143,7 +143,7 @@ you little to no time.
import { useChallenge } from "@duckity/react"; import { useChallenge } from "@duckity/react";
function MyComponent() { function MyComponent() {
const duckity = useChallenge(process.env.NEXT_PUBLIC_DUCKITY_PROTECTION_PROFILE_ID); const duckity = useChallenge(process.env.NEXT_PUBLIC_DUCKITY_POLICY_ID);
} }
``` ```
</Tab> </Tab>
@@ -152,7 +152,7 @@ you little to no time.
import { useChallenge } from "@duckity/react"; import { useChallenge } from "@duckity/react";
function MyComponent() { function MyComponent() {
const duckity = useChallenge(import.meta.env.VITE_DUCKITY_PROTECTION_PROFILE_ID); const duckity = useChallenge(import.meta.env.VITE_DUCKITY_POLICY_ID);
} }
``` ```
</Tab> </Tab>
@@ -165,14 +165,14 @@ you little to no time.
export async function loader() { export async function loader() {
return json({ return json({
ENV: { ENV: {
DUCKITY_PROTECTION_PROFILE_ID: process.env.DUCKITY_PROTECTION_PROFILE_ID, DUCKITY_POLICY_ID: process.env.DUCKITY_POLICY_ID,
}, },
}); });
} }
function MyComponent() { function MyComponent() {
const data = useLoaderData<typeof loader>(); const data = useLoaderData<typeof loader>();
const duckity = useChallenge(data.ENV.DUCKITY_PROTECTION_PROFILE_ID); const duckity = useChallenge(data.ENV.DUCKITY_POLICY_ID);
} }
``` ```
</Tab> </Tab>
@@ -181,7 +181,7 @@ you little to no time.
import { useChallenge } from "@duckity/react"; import { useChallenge } from "@duckity/react";
function MyComponent() { function MyComponent() {
const duckity = useChallenge(process.env.GATSBY_DUCKITY_PROTECTION_PROFILE_ID); const duckity = useChallenge(process.env.GATSBY_DUCKITY_POLICY_ID);
} }
``` ```
</Tab> </Tab>
@@ -190,7 +190,7 @@ you little to no time.
import { useChallenge } from "@duckity/react"; import { useChallenge } from "@duckity/react";
function MyComponent() { function MyComponent() {
const duckity = useChallenge(process.env.EXPO_PUBLIC_DUCKITY_PROTECTION_PROFILE_ID); const duckity = useChallenge(process.env.EXPO_PUBLIC_DUCKITY_POLICY_ID);
} }
``` ```
</Tab> </Tab>
@@ -199,7 +199,7 @@ you little to no time.
import { useChallenge } from "@duckity/react"; import { useChallenge } from "@duckity/react";
function MyComponent() { function MyComponent() {
const duckity = useChallenge(import.meta.env.PUBLIC_DUCKITY_PROTECTION_PROFILE_ID); const duckity = useChallenge(import.meta.env.PUBLIC_DUCKITY_POLICY_ID);
} }
``` ```
@@ -270,14 +270,14 @@ you little to no time.
</Tab> </Tab>
</Tabs> </Tabs>
Solving a challenge only requires your protection profile ID. Solving a challenge only requires your policy ID.
<Tabs items={["Async", "Sync"]} groupId="python-sync" persist> <Tabs items={["Async", "Sync"]} groupId="python-sync" persist>
<Tab value="Async"> <Tab value="Async">
```py ```py
import duckity import duckity
solution: str = await duckity.solve(PROTECTION_PROFILE_ID) solution: str = await duckity.solve(POLICY_ID)
``` ```
The CPU-intensive part of solving the challenge is done in a The CPU-intensive part of solving the challenge is done in a
@@ -289,7 +289,7 @@ you little to no time.
import duckity import duckity
solution: str = asyncio.run(duckity.solve(PROTECTION_PROFILE_ID)) solution: str = asyncio.run(duckity.solve(POLICY_ID))
``` ```
The CPU-intensive part of solving the challenge is done in a The CPU-intensive part of solving the challenge is done in a
@@ -303,7 +303,7 @@ you little to no time.
```py ```py
import requests import requests
solution: str = asyncio.run(duckity.solve(PROTECTION_PROFILE_ID)) solution: str = asyncio.run(duckity.solve(POLICY_ID))
requests.post( requests.post(
"https://api.example.com/login", "https://api.example.com/login",
@@ -338,7 +338,7 @@ you little to no time.
To get and solve a challenge, use `duckity::solve()` like follows: To get and solve a challenge, use `duckity::solve()` like follows:
```rs ```rs
let solution: str = duckity::solve(PROTECTION_PROFILE_ID).await?; let solution: str = duckity::solve(POLICY_ID).await?;
``` ```
That's it! Once you have your solution token, you can send it over to your backend for That's it! Once you have your solution token, you can send it over to your backend for
@@ -410,7 +410,7 @@ Once you have a solution token in your server, validating it is quite easy.
solution, // The solution token submitted by the client. solution, // The solution token submitted by the client.
clientIp, // The IP address of the client that submitted the solution. clientIp, // The IP address of the client that submitted the solution.
applicationSecret, // Your application's secret. Get it from the dashboard. applicationSecret, // Your application's secret. Get it from the dashboard.
protectionProfileId, // The protection profile ID used to generate the challenge. policyId, // The policy ID used to generate the challenge.
); );
``` ```
@@ -449,10 +449,10 @@ Once you have a solution token in your server, validating it is quite easy.
solution: str # The solution token submitted by the client. solution: str # The solution token submitted by the client.
client_ip: str # The IP address of the client that submitted the solution. client_ip: str # The IP address of the client that submitted the solution.
application_secret: str # Your application's secret. Get it from the dashboard. application_secret: str # Your application's secret. Get it from the dashboard.
protection_profile_id: str # The protection profile ID used to generate the challenge. policy_id: str # The policy ID used to generate the challenge.
is_valid: bool = await duckity.validate( is_valid: bool = await duckity.validate(
solution, client_ip, application_secret, protection_profile_id solution, client_ip, application_secret, policy_id
) )
``` ```
</Tab> </Tab>
@@ -465,10 +465,10 @@ Once you have a solution token in your server, validating it is quite easy.
solution: str # The solution token submitted by the client. solution: str # The solution token submitted by the client.
client_ip: str # The IP address of the client that submitted the solution. client_ip: str # The IP address of the client that submitted the solution.
application_secret: str # Your application's secret. Get it from the dashboard. application_secret: str # Your application's secret. Get it from the dashboard.
protection_profile_id: str # The protection profile ID used to generate the challenge. policy_id: str # The policy ID used to generate the challenge.
is_valid: bool = asyncio.run( is_valid: bool = asyncio.run(
duckity.validate(solution, client_ip, application_secret, protection_profile_id) duckity.validate(solution, client_ip, application_secret, policy_id)
) )
``` ```
</Tab> </Tab>
@@ -493,7 +493,7 @@ Once you have a solution token in your server, validating it is quite easy.
solution, // The solution token submitted by the client. solution, // The solution token submitted by the client.
client_ip, // The IP address of the client that submitted the solution. client_ip, // The IP address of the client that submitted the solution.
APPLICATION_SECRET, // Your application's secret. Get it from the dashboard. APPLICATION_SECRET, // Your application's secret. Get it from the dashboard.
PROTECTION_PROFILE_ID // The protection profile ID used to generate the challenge. POLICY_ID // The policy ID used to generate the challenge.
) )
.await?; .await?;
``` ```
@@ -506,7 +506,7 @@ Once you have a solution token in your server, validating it is quite easy.
hand by making the HTTP request by hand instead. hand by making the HTTP request by hand instead.
```http ```http
POST /d1/challenges/{protection_profile_id}/validate HTTP/1.1 POST /d1/challenges/{policy_id}/validate HTTP/1.1
Host: api.duckity.com Host: api.duckity.com
Accept: application/json Accept: application/json
Content-Type: application/json Content-Type: application/json
@@ -548,8 +548,8 @@ Once you have a solution token in your server, validating it is quite easy.
Content-Type: application/json Content-Type: application/json
{ {
"title": "Protection Profile Not Found", "title": "Policy Not Found",
"message": "The protection profile ID you specified was not found." "message": "The policy ID you specified was not found."
} }
``` ```
</Tab> </Tab>
@@ -594,7 +594,7 @@ Once you have a solution token in your server, validating it is quite easy.
Note that status codes are related to server actions, not to client actions. An invalid Note that status codes are related to server actions, not to client actions. An invalid
solution token submitted by the client will return `200` because the token was validated solution token submitted by the client will return `200` because the token was validated
successfully (even if the result was that it was invalid), while a wrong application secret successfully (even if the result was that it was invalid), while a wrong application secret
or an incorrect protection profile ID will return `4xx` status codes. or an incorrect policy ID will return `4xx` status codes.
</Tab> </Tab>
</Tabs> </Tabs>
@@ -608,11 +608,7 @@ combine settings to produce a more powerful setup, check out the following guide
configuration by configuration to teach you how to configure the parameters you need to suit your configuration by configuration to teach you how to configure the parameters you need to suit your
application's needs. application's needs.
<Card <Card href="/guides/tune-a-policy" title="Tune a Policy" icon={<LiPencil />}>
href="/guides/tune-a-protection-profile" Learn how to tune your policies to better protect your application while enhancing your users'
title="Tune a Protection Profile" experiences.
icon={<LiPencil />}
>
Learn how to tune your protection profiles to better protect your application while enhancing
your users' experiences.
</Card> </Card>
+9 -9
View File
@@ -23,8 +23,8 @@ Before you can integrate Duckity into your application, you'll need to have the
1. An application, 1. An application,
2. CORS origins set up pointing to your application's origin(s), 2. CORS origins set up pointing to your application's origin(s),
3. At least one protection profile created in that application, and 3. At least one policy created in that application, and
4. The ID of the protection profiles to use 4. The ID of the policies to use
If you're missing either of those, head over to the [Duckity Dashboard](https://app.duckity.com) or If you're missing either of those, head over to the [Duckity Dashboard](https://app.duckity.com) or
read the [Quick Start](/quick-start) guide to learn how to set those up. read the [Quick Start](/quick-start) guide to learn how to set those up.
@@ -78,7 +78,7 @@ Once you have the SDK installed, you can request a challenge whenever you need i
import duckity from "@duckity/js"; import duckity from "@duckity/js";
/// Slow! Runs in a web worker, yet don't let it block code below unless necessary. /// Slow! Runs in a web worker, yet don't let it block code below unless necessary.
let solution: string = await duckity.solve(PROTECTION_PROFILE_ID); let solution: string = await duckity.solve(POLICY_ID);
``` ```
### Validate a Challenge [step] ### Validate a Challenge [step]
@@ -86,18 +86,18 @@ let solution: string = await duckity.solve(PROTECTION_PROFILE_ID);
Send the solution token to your backend server any way you want. A header or parameter in a JSON Send the solution token to your backend server any way you want. A header or parameter in a JSON
field will work. field will work.
Once in the server, you'll need the client's IP, their submitted solution token, the protection Once in the server, you'll need the client's IP, their submitted solution token, the policy's ID,
profile's ID, and the application secret. and the application secret.
```ts ```ts
import * as duckity from "@duckity/js"; import * as duckity from "@duckity/js";
let applicationSecret: string; let applicationSecret: string;
let protectionProfileId: string; let policyId: string;
let clientIp: string; let clientIp: string;
let solution: string; let solution: string;
let isValid = await duckity.validate(solution, clientIp, applicationSecret, protectionProfileId); let isValid = await duckity.validate(solution, clientIp, applicationSecret, policyId);
if (isValid) { if (isValid) {
// Keep processing the request. // Keep processing the request.
@@ -144,7 +144,7 @@ wait.
<script type="module"> <script type="module">
import * as duckity from "https://esm.sh/@duckity/js"; import * as duckity from "https://esm.sh/@duckity/js";
const PROTECTION_PROFILE_ID = "<your-protection-profile-id>"; const POLICY_ID = "<your-policy-id>";
const form = document.getElementById("form"); const form = document.getElementById("form");
const submitButton = document.getElementById("submit-button"); const submitButton = document.getElementById("submit-button");
@@ -153,7 +153,7 @@ wait.
// A Promise<string>, it will solve a challenge in the background while the user logs // A Promise<string>, it will solve a challenge in the background while the user logs
// in. Do not `await` it here, it will prevent the `form.addEventListener()` call from // in. Do not `await` it here, it will prevent the `form.addEventListener()` call from
// running until the challenge is solved, which takes some time. // running until the challenge is solved, which takes some time.
let solutionPromise = duckity.solve(PROTECTION_PROFILE_ID); let solutionPromise = duckity.solve(POLICY_ID);
form.addEventListener("submit", async (e) => { form.addEventListener("submit", async (e) => {
e.preventDefault(); e.preventDefault();
+20 -20
View File
@@ -19,8 +19,8 @@ event loop's version of it.
Before you can integrate Duckity into your application, you'll need to have the following: Before you can integrate Duckity into your application, you'll need to have the following:
1. An application, 1. An application,
2. At least one protection profile created in that application, and 2. At least one policy created in that application, and
3. The ID of the protection profiles to use 3. The ID of the policies to use
If you're missing either of those, head over to the [Duckity Dashboard](https://app.duckity.com) or If you're missing either of those, head over to the [Duckity Dashboard](https://app.duckity.com) or
read the [Quick Start](/quick-start) guide to learn how to set those up. read the [Quick Start](/quick-start) guide to learn how to set those up.
@@ -52,14 +52,14 @@ Install the SDK from PyPI using your favorite package manager:
### Solve a Challenge [step] ### Solve a Challenge [step]
Solving a challenge only requires a protection profile ID. Solving a challenge only requires a policy ID.
<Tabs items={["Async", "Sync"]} groupId="python-sync" persist> <Tabs items={["Async", "Sync"]} groupId="python-sync" persist>
<Tab value="Async" id="async"> <Tab value="Async" id="async">
```py ```py
import duckity import duckity
solution: str = await duckity.solve(PROTECTION_PROFILE_ID) solution: str = await duckity.solve(POLICY_ID)
``` ```
The CPU-intensive part of solving the challenge is done in a `ProcessPoolExecutor()`. The CPU-intensive part of solving the challenge is done in a `ProcessPoolExecutor()`.
@@ -71,7 +71,7 @@ Solving a challenge only requires a protection profile ID.
import duckity import duckity
solution: str = asyncio.run(duckity.solve(PROTECTION_PROFILE_ID)) solution: str = asyncio.run(duckity.solve(POLICY_ID))
``` ```
The CPU-intensive part of solving the challenge is done in a `ProcessPoolExecutor()`. The The CPU-intensive part of solving the challenge is done in a `ProcessPoolExecutor()`. The
@@ -90,7 +90,7 @@ To validate a solution token, you'll need 4 things:
1. The solution token, 1. The solution token,
2. The IP of the client that submitted the solution, 2. The IP of the client that submitted the solution,
3. The application's secret, and 3. The application's secret, and
4. The protection profile's ID. 4. The policy's ID.
Once you have them, you can validate a solution token as follows: Once you have them, you can validate a solution token as follows:
@@ -102,10 +102,10 @@ Once you have them, you can validate a solution token as follows:
solution: str solution: str
client_ip: str client_ip: str
application_secret: str application_secret: str
protection_profile_id: str policy_id: str
is_valid: bool = await duckity.validate( is_valid: bool = await duckity.validate(
solution, client_ip, application_secret, protection_profile_id solution, client_ip, application_secret, policy_id
) )
``` ```
</Tab> </Tab>
@@ -118,10 +118,10 @@ Once you have them, you can validate a solution token as follows:
solution: str solution: str
client_ip: str client_ip: str
application_secret: str application_secret: str
protection_profile_id: str policy_id: str
is_valid: bool = asyncio.run( is_valid: bool = asyncio.run(
duckity.validate(solution, client_ip, application_secret, protection_profile_id) duckity.validate(solution, client_ip, application_secret, policy_id)
) )
``` ```
</Tab> </Tab>
@@ -154,7 +154,7 @@ import duckity
async def login(): async def login():
solution_task = asyncio.create_task(duckity.solve(PROTECTION_PROFILE_ID)) solution_task = asyncio.create_task(duckity.solve(POLICY_ID))
email = await asyncio.to_thread(input, "Enter your email: ") email = await asyncio.to_thread(input, "Enter your email: ")
password = await asyncio.to_thread(getpass.getpass, "Enter your password (hidden): ") password = await asyncio.to_thread(getpass.getpass, "Enter your password (hidden): ")
@@ -191,7 +191,7 @@ from pydantic import BaseModel
dotenv.load_dotenv(override=True) dotenv.load_dotenv(override=True)
PROTECTION_PROFILE_ID = os.environ["DUCKITY_PROTECTION_PROFILE_ID"] POLICY_ID = os.environ["DUCKITY_POLICY_ID"]
APPLICATION_SECRET = os.environ["DUCKITY_APPLICATION_SECRET"] APPLICATION_SECRET = os.environ["DUCKITY_APPLICATION_SECRET"]
@@ -208,7 +208,7 @@ async def protected(request: Request, payload: RequestBodySchema):
payload.solution, payload.solution,
request.client.host, # Make sure to take into account X-Forwarded-For if behind a reverse proxy. request.client.host, # Make sure to take into account X-Forwarded-For if behind a reverse proxy.
APPLICATION_SECRET, APPLICATION_SECRET,
PROTECTION_PROFILE_ID POLICY_ID
) )
if not is_valid: if not is_valid:
@@ -240,7 +240,7 @@ dotenv.load_dotenv(override=True)
app = Flask(__name__) app = Flask(__name__)
PROTECTION_PROFILE_ID = os.environ["DUCKITY_PROTECTION_PROFILE_ID"] POLICY_ID = os.environ["DUCKITY_POLICY_ID"]
APPLICATION_SECRET = os.environ["DUCKITY_APPLICATION_SECRET"] APPLICATION_SECRET = os.environ["DUCKITY_APPLICATION_SECRET"]
@@ -257,7 +257,7 @@ async def protected():
solution, solution,
client_ip, client_ip,
APPLICATION_SECRET, APPLICATION_SECRET,
PROTECTION_PROFILE_ID, POLICY_ID,
) )
if not is_valid: if not is_valid:
@@ -300,7 +300,7 @@ async def protected(request):
solution, solution,
client_ip, client_ip,
settings.DUCKITY_APPLICATION_SECRET, settings.DUCKITY_APPLICATION_SECRET,
settings.DUCKITY_PROTECTION_PROFILE_ID, settings.DUCKITY_POLICY_ID,
) )
if not is_valid: if not is_valid:
@@ -339,7 +339,7 @@ async def protected(request, payload: RequestBodySchema):
payload.solution, payload.solution,
client_ip, client_ip,
settings.DUCKITY_APPLICATION_SECRET, settings.DUCKITY_APPLICATION_SECRET,
settings.DUCKITY_PROTECTION_PROFILE_ID, settings.DUCKITY_POLICY_ID,
) )
if not is_valid: if not is_valid:
@@ -378,7 +378,7 @@ class ProtectedView(APIView):
serializer.validated_data["solution"], serializer.validated_data["solution"],
client_ip, client_ip,
settings.DUCKITY_APPLICATION_SECRET, settings.DUCKITY_APPLICATION_SECRET,
settings.DUCKITY_PROTECTION_PROFILE_ID, settings.DUCKITY_POLICY_ID,
) )
if not is_valid: if not is_valid:
@@ -405,7 +405,7 @@ import strawberry
dotenv.load_dotenv(override=True) dotenv.load_dotenv(override=True)
PROTECTION_PROFILE_ID = os.environ["DUCKITY_PROTECTION_PROFILE_ID"] POLICY_ID = os.environ["DUCKITY_POLICY_ID"]
APPLICATION_SECRET = os.environ["DUCKITY_APPLICATION_SECRET"] APPLICATION_SECRET = os.environ["DUCKITY_APPLICATION_SECRET"]
@@ -430,7 +430,7 @@ class Mutation:
solution, solution,
client_ip, client_ip,
DUCKITY_APPLICATION_SECRET, DUCKITY_APPLICATION_SECRET,
DUCKITY_PROTECTION_PROFILE_ID, DUCKITY_POLICY_ID,
) )
if not is_valid: if not is_valid:
+9 -9
View File
@@ -21,8 +21,8 @@ Before you can integrate Duckity into your application, you'll need to have the
1. An application, 1. An application,
2. CORS origins set up pointing to your application's origin(s), 2. CORS origins set up pointing to your application's origin(s),
3. At least one protection profile created in that application, and 3. At least one policy created in that application, and
4. The ID of the protection profiles to use 4. The ID of the policies to use
If you're missing either of those, head over to the [Duckity Dashboard](https://app.duckity.com) or If you're missing either of those, head over to the [Duckity Dashboard](https://app.duckity.com) or
read the [Quick Start](/quick-start) guide to learn how to set those up. read the [Quick Start](/quick-start) guide to learn how to set those up.
@@ -55,7 +55,7 @@ Use the hook as follows:
import { useChallenge } from "@duckity/react"; import { useChallenge } from "@duckity/react";
function MyComponent() { function MyComponent() {
const duckity = useChallenge(process.env.NEXT_PUBLIC_DUCKITY_PROTECTION_PROFILE_ID); const duckity = useChallenge(process.env.NEXT_PUBLIC_DUCKITY_POLICY_ID);
} }
``` ```
</Tab> </Tab>
@@ -64,7 +64,7 @@ Use the hook as follows:
import { useChallenge } from "@duckity/react"; import { useChallenge } from "@duckity/react";
function MyComponent() { function MyComponent() {
const duckity = useChallenge(import.meta.env.VITE_DUCKITY_PROTECTION_PROFILE_ID); const duckity = useChallenge(import.meta.env.VITE_DUCKITY_POLICY_ID);
} }
``` ```
</Tab> </Tab>
@@ -77,14 +77,14 @@ Use the hook as follows:
export async function loader() { export async function loader() {
return json({ return json({
ENV: { ENV: {
DUCKITY_PROTECTION_PROFILE_ID: process.env.DUCKITY_PROTECTION_PROFILE_ID, DUCKITY_POLICY_ID: process.env.DUCKITY_POLICY_ID,
}, },
}); });
} }
function MyComponent() { function MyComponent() {
const data = useLoaderData<typeof loader>(); const data = useLoaderData<typeof loader>();
const duckity = useChallenge(data.ENV.DUCKITY_PROTECTION_PROFILE_ID); const duckity = useChallenge(data.ENV.DUCKITY_POLICY_ID);
} }
``` ```
</Tab> </Tab>
@@ -93,7 +93,7 @@ Use the hook as follows:
import { useChallenge } from "@duckity/react"; import { useChallenge } from "@duckity/react";
function MyComponent() { function MyComponent() {
const duckity = useChallenge(process.env.GATSBY_DUCKITY_PROTECTION_PROFILE_ID); const duckity = useChallenge(process.env.GATSBY_DUCKITY_POLICY_ID);
} }
``` ```
</Tab> </Tab>
@@ -102,7 +102,7 @@ Use the hook as follows:
import { useChallenge } from "@duckity/react"; import { useChallenge } from "@duckity/react";
function MyComponent() { function MyComponent() {
const duckity = useChallenge(process.env.EXPO_PUBLIC_DUCKITY_PROTECTION_PROFILE_ID); const duckity = useChallenge(process.env.EXPO_PUBLIC_DUCKITY_POLICY_ID);
} }
``` ```
</Tab> </Tab>
@@ -111,7 +111,7 @@ Use the hook as follows:
import { useChallenge } from "@duckity/react"; import { useChallenge } from "@duckity/react";
function MyComponent() { function MyComponent() {
const duckity = useChallenge(import.meta.env.PUBLIC_DUCKITY_PROTECTION_PROFILE_ID); const duckity = useChallenge(import.meta.env.PUBLIC_DUCKITY_POLICY_ID);
} }
``` ```
+15 -15
View File
@@ -19,8 +19,8 @@ in case you want to use the `core` module later on.
Before you can integrate Duckity into your application, you'll need to have the following: Before you can integrate Duckity into your application, you'll need to have the following:
1. An application, 1. An application,
2. At least one protection profile created in that application, and 2. At least one policy created in that application, and
3. The ID of the protection profiles to use 3. The ID of the policies to use
If you're missing either of those, head over to the [Duckity Dashboard](https://app.duckity.com) or If you're missing either of those, head over to the [Duckity Dashboard](https://app.duckity.com) or
read the [Quick Start](/quick-start) guide to learn how to set those up. read the [Quick Start](/quick-start) guide to learn how to set those up.
@@ -40,7 +40,7 @@ cargo add duckity
To get and solve a challenge, use `duckity::solve()` like follows: To get and solve a challenge, use `duckity::solve()` like follows:
```rs ```rs
let solution: str = duckity::solve(PROTECTION_PROFILE_ID).await?; let solution: str = duckity::solve(POLICY_ID).await?;
``` ```
That's it! Once you have your solution token, you can send it over to your backend for validation. That's it! Once you have your solution token, you can send it over to your backend for validation.
@@ -60,7 +60,7 @@ To validate a solution token, you'll need 4 things:
1. The solution token, 1. The solution token,
2. The IP of the client that submitted the solution, 2. The IP of the client that submitted the solution,
3. The application's secret, and 3. The application's secret, and
4. The protection profile's ID. 4. The policy's ID.
Once you have them, you can validate a solution token as follows: Once you have them, you can validate a solution token as follows:
@@ -69,7 +69,7 @@ let is_valid: bool = duckity::validate(
solution, solution,
client_ip, client_ip,
APPLICATION_SECRET, APPLICATION_SECRET,
PROTECTION_PROFILE_ID POLICY_ID
) )
.await?; .await?;
``` ```
@@ -95,7 +95,7 @@ challenge while the user is filling up their username and password. For example:
```rs ```rs
use std::io::{self, Write}; use std::io::{self, Write};
const PROTECTION_PROFILE_ID: &str = "<your-own-protection-profile-id>"; const POLICY_ID: &str = "<your-own-policy-id>";
struct Credentials { struct Credentials {
email: String, email: String,
@@ -104,7 +104,7 @@ struct Credentials {
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
let solution_task = tokio::spawn(duckity::solve(PROTECTION_PROFILE_ID).into_future()); let solution_task = tokio::spawn(duckity::solve(POLICY_ID).into_future());
let credentials = tokio::task::spawn_blocking(|| { let credentials = tokio::task::spawn_blocking(|| {
let mut email = String::new(); let mut email = String::new();
@@ -169,11 +169,11 @@ simple. For example, the following snippet shows how to solve and validate a cha
```rs ```rs
use async_compat::CompatExt; use async_compat::CompatExt;
const PROTECTION_PROFILE_ID: &str = "<insert-your-protection-profile-here>"; const POLICY_ID: &str = "<insert-your-policy-here>";
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
smol::block_on(async { smol::block_on(async {
let solution = duckity::solve(PROTECTION_PROFILE_ID).into_future().compat().await?; let solution = duckity::solve(POLICY_ID).into_future().compat().await?;
Ok(()) Ok(())
}) })
@@ -199,7 +199,7 @@ use tokio::net::TcpListener;
// In an actual application, make these two configurable. `clap` is a good tool for that. // In an actual application, make these two configurable. `clap` is a good tool for that.
const APPLICATION_SECRET: &str = "<your-application-secret>"; const APPLICATION_SECRET: &str = "<your-application-secret>";
const PROTECTION_PROFILE_ID: &str = "<your-protection-profile-id>"; const POLICY_ID: &str = "<your-policy-id>";
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
@@ -229,7 +229,7 @@ async fn handler(
payload.solution, payload.solution,
addr.ip(), addr.ip(),
APPLICATION_SECRET, APPLICATION_SECRET,
PROTECTION_PROFILE_ID, POLICY_ID,
) )
.await .await
.unwrap(); .unwrap();
@@ -256,7 +256,7 @@ use warp::{Filter, Reply};
// In an actual application, make these two configurable. `clap` is a good tool for that. // In an actual application, make these two configurable. `clap` is a good tool for that.
const APPLICATION_SECRET: &str = "<your-application-secret>"; const APPLICATION_SECRET: &str = "<your-application-secret>";
const PROTECTION_PROFILE_ID: &str = "<your-protection-profile-id>"; const POLICY_ID: &str = "<your-policy-id>";
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
@@ -287,7 +287,7 @@ async fn handler(
payload.solution, payload.solution,
addr.ip(), addr.ip(),
APPLICATION_SECRET, APPLICATION_SECRET,
PROTECTION_PROFILE_ID, POLICY_ID,
) )
.await .await
.unwrap(); .unwrap();
@@ -317,7 +317,7 @@ use serde::Deserialize;
// In an actual application, make these two configurable. `clap` is a good tool for that. // In an actual application, make these two configurable. `clap` is a good tool for that.
const APPLICATION_SECRET: &str = "<your-application-secret>"; const APPLICATION_SECRET: &str = "<your-application-secret>";
const PROTECTION_PROFILE_ID: &str = "<your-protection-profile-id>"; const POLICY_ID: &str = "<your-policy-id>";
#[actix_web::main] #[actix_web::main]
async fn main() -> std::io::Result<()> { async fn main() -> std::io::Result<()> {
@@ -342,7 +342,7 @@ async fn protected(req: HttpRequest, payload: Json<ProtectedRequestPayload>) ->
payload.solution.clone(), payload.solution.clone(),
addr.ip(), addr.ip(),
APPLICATION_SECRET, APPLICATION_SECRET,
PROTECTION_PROFILE_ID, POLICY_ID,
) )
.await .await
.unwrap(); .unwrap();