Skip to content

Commit c2fd2f1

Browse files
authored
Add cf turnstile to faucet-fe (#2277)
1 parent 30e944a commit c2fd2f1

File tree

4 files changed

+61
-24
lines changed

4 files changed

+61
-24
lines changed

apps/faucet/.env.sample

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ NAMADA_INTERFACE_FAUCET_API_ENDPOINT=/api/v1/faucet
77
# Specify a proxy port, if proxy is needed (defaults to 9000)
88
NAMADA_INTERFACE_PROXY_PORT=1234
99

10+
# Optional: Cloudflare Turnstile site key for captcha
11+
# Get this from https://dash.cloudflare.com/ -> Turnstile
12+
# Must match the secret key configured in the backend
13+
NAMADA_INTERFACE_TURNSTILE_SITEKEY=0x4AAAAAAASomePublicSiteKey

apps/faucet/public/index.html

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,22 @@
11
<!DOCTYPE html>
22
<html lang="en" style="height: 100%">
3-
<head>
4-
<meta charset="utf-8" />
5-
<meta name="”robots”" content="”noindex,nofollow”" />
6-
<link rel="icon" href="/assets/favicon-32x32.png" />
7-
<meta name="viewport" content="width=device-width" />
8-
<meta name="theme-color" content="#000000" />
9-
<meta name="description" content="Namada Interface" />
10-
<link rel="apple-touch-icon" href="/assets/apple-touch-icon.png" />
11-
<link rel="manifest" href="/manifest.json" />
12-
<title>Namada Faucet</title>
13-
</head>
143

15-
<body>
16-
<noscript>You need to enable JavaScript to run this app.</noscript>
17-
<div id="root"></div>
18-
</body>
19-
</html>
4+
<head>
5+
<meta charset="utf-8" />
6+
<meta name="”robots”" content="”noindex,nofollow”" />
7+
<link rel="icon" href="/assets/favicon-32x32.png" />
8+
<meta name="viewport" content="width=device-width" />
9+
<meta name="theme-color" content="#000000" />
10+
<meta name="description" content="Namada Interface" />
11+
<link rel="apple-touch-icon" href="/assets/apple-touch-icon.png" />
12+
<link rel="manifest" href="/manifest.json" />
13+
<title>Namada Faucet</title>
14+
<script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
15+
</head>
16+
17+
<body>
18+
<noscript>You need to enable JavaScript to run this app.</noscript>
19+
<div id="root"></div>
20+
</body>
21+
22+
</html>

apps/faucet/src/App/Faucet.tsx

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,16 @@ import {
2929
PreFormatted,
3030
} from "./Faucet.components";
3131

32+
declare global {
33+
interface Window {
34+
turnstile: {
35+
ready: (cb: () => void) => void;
36+
execute: (container: string, params: { sitekey: string; callback: (token: string) => void; "error-callback": (errorCode: string) => void }) => void;
37+
reset: (container: string) => void;
38+
};
39+
}
40+
}
41+
3242
enum Status {
3343
PendingPowSolution,
3444
PendingTransfer,
@@ -188,15 +198,30 @@ export const FaucetForm: React.FC<Props> = ({ isTestnetLive }) => {
188198
setStatusText(undefined);
189199

190200
try {
191-
const { challenge, tag } = await api
192-
.challenge()
193-
.catch(({ message, code }) => {
194-
throw new Error(
195-
`Unable to request challenge: ${code} - ${message}`
196-
);
197-
});
201+
const { challenge, tag } = await api.challenge().catch(({ message, code }) => {
202+
throw new Error(`Unable to request challenge: ${code} - ${message}`);
203+
});
198204

199205
const solution = await postPowChallenge({ challenge, difficulty });
206+
207+
// Only attempt captcha if sitekey is configured
208+
const sitekey = process.env.NAMADA_INTERFACE_TURNSTILE_SITEKEY;
209+
let captcha_token: string | undefined;
210+
211+
if (sitekey && sitekey.trim() !== "") {
212+
captcha_token = await new Promise<string>((resolve, reject) => {
213+
if (window.turnstile) {
214+
window.turnstile.execute("#turnstile-widget", {
215+
sitekey,
216+
callback: (t: string) => resolve(t),
217+
"error-callback": (errorCode: string) => reject(new Error(`Turnstile error: ${errorCode}`)),
218+
});
219+
} else {
220+
reject(new Error("Turnstile not loaded but sitekey is configured"));
221+
}
222+
});
223+
}
224+
200225
const submitData: Data = {
201226
solution,
202227
tag,
@@ -206,6 +231,7 @@ export const FaucetForm: React.FC<Props> = ({ isTestnetLive }) => {
206231
token: sanitizedToken,
207232
amount: amount * 1_000_000,
208233
},
234+
...(captcha_token && { captcha_token }),
209235
};
210236

211237
await submitFaucetTransfer(submitData);
@@ -316,7 +342,7 @@ export const FaucetForm: React.FC<Props> = ({ isTestnetLive }) => {
316342
error={
317343
amount && amount > withdrawLimit ?
318344
`Amount must be less than or equal to ${withdrawLimit}`
319-
: ""
345+
: ""
320346
}
321347
/>
322348
</InputContainer>
@@ -363,6 +389,9 @@ export const FaucetForm: React.FC<Props> = ({ isTestnetLive }) => {
363389
Get Testnet Tokens
364390
</ActionButton>
365391
</ButtonContainer>
392+
{process.env.NAMADA_INTERFACE_TURNSTILE_SITEKEY && (
393+
<div id="turnstile-widget"></div>
394+
)}
366395
</FaucetFormContainer>
367396
);
368397
};

apps/faucet/src/utils/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export type Data = {
2727
tag: string;
2828
challenge: unknown;
2929
transfer: TransferDetails;
30+
captcha_token?: string;
3031
};
3132

3233
export type TransferResponse = {

0 commit comments

Comments
 (0)