@friendlycaptcha/server-sdk 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "friendly-captcha-example",
3
+ "version": "1.0.0",
4
+ "description": "Friendly Captcha V2 Javascript SDK integration example",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "private": true,
8
+ "scripts": {
9
+ "start": "node --import tsx src/index.ts"
10
+ },
11
+ "author": "Friendly Captcha GmbH",
12
+ "license": "MIT",
13
+ "dependencies": {
14
+ "@friendlycaptcha/server-sdk": "^0.1.0",
15
+ "ejs": "^3.1.9",
16
+ "express": "^4.18.2"
17
+ },
18
+ "devDependencies": {
19
+ "@types/express": "^4.17.21",
20
+ "@types/node": "^20.11.5",
21
+ "tsx": "^4.7.0",
22
+ "typescript": "^5.3.3"
23
+ }
24
+ }
@@ -0,0 +1,84 @@
1
+ import express, { Request, Response } from "express";
2
+ import { FriendlyCaptchaClient } from "@friendlycaptcha/server-sdk";
3
+
4
+ type FormMessage = {
5
+ subject: string;
6
+ message: string;
7
+ };
8
+
9
+ const app = express();
10
+ const port = process.env.PORT || 3000;
11
+
12
+ const FRC_SITEKEY = process.env.FRC_SITEKEY;
13
+ const FRC_APIKEY = process.env.FRC_APIKEY;
14
+
15
+ // Optionally we can pass in custom endpoints to be used, such as "eu".
16
+ const FRC_SITEVERIFY_ENDPOINT = process.env.FRC_SITEVERIFY_ENDPOINT;
17
+ const FRC_WIDGET_ENDPOINT = process.env.FRC_WIDGET_ENDPOINT;
18
+
19
+ if (!FRC_SITEKEY || !FRC_APIKEY) {
20
+ console.error(
21
+ "Please set the FRC_SITEKEY and FRC_APIKEY environment values before running this example to your Friendly Captcha sitekey and API key respectively.",
22
+ );
23
+ process.exit(1);
24
+ }
25
+
26
+ const frcClient = new FriendlyCaptchaClient({
27
+ apiKey: FRC_APIKEY,
28
+ sitekey: FRC_SITEKEY,
29
+ siteverifyEndpoint: FRC_SITEVERIFY_ENDPOINT, // optional, defaults to "global"
30
+ });
31
+
32
+ app.set("view engine", "ejs");
33
+ app.use(express.urlencoded({ extended: true }));
34
+
35
+ app.get("/", (req: Request, res: Response) => {
36
+ res.render("index", {
37
+ message: "",
38
+ sitekey: FRC_SITEKEY,
39
+ widgetEndpoint: FRC_WIDGET_ENDPOINT,
40
+ });
41
+ });
42
+
43
+ app.post("/", async (req: Request, res: Response) => {
44
+ const formData = req.body;
45
+ const formMessage = formData as FormMessage;
46
+
47
+ const frcCaptchaResponse = formData["frc-captcha-response"];
48
+ const result = await frcClient.verifyCaptchaResponse(frcCaptchaResponse);
49
+ if (!result.wasAbleToVerify()) {
50
+ // In this case we were not actually able to verify the response embedded in the form, but we may still want to accept it.
51
+ // It could mean there is a network issue or that the service is down. In those cases you generally want to accept submissions anyhow.
52
+ // That's why we use `shouldAccept()` below to actually accept or reject the form submission. It will return true in these cases.
53
+
54
+ if (result.isClientError()) {
55
+ // Something is wrong with our configuration, check your API key!
56
+ // Send yourself an alert to fix this! Your site is unprotected until you fix this.
57
+ console.error("CAPTCHA CONFIG ERROR: ", result.getErrorCode(), result.getResponseError());
58
+ } else {
59
+ console.error("Failed to verify captcha response: ", result.getErrorCode(), result.getResponseError());
60
+ }
61
+ }
62
+
63
+ if (!result.shouldAccept()) {
64
+ res.render("index", {
65
+ message: "❌ Anti-robot check failed, please try again.",
66
+ sitekey: FRC_SITEKEY,
67
+ widgetEndpoint: FRC_WIDGET_ENDPOINT,
68
+ });
69
+ return;
70
+ }
71
+
72
+ // The captcha was OK, process the form.
73
+ formMessage; // Normally we would use the form data in `formMessage` here and submit it to our database.
74
+
75
+ res.render("index", {
76
+ message: "✅ Your message has been submitted successfully.",
77
+ sitekey: FRC_SITEKEY,
78
+ widgetEndpoint: FRC_WIDGET_ENDPOINT,
79
+ });
80
+ });
81
+
82
+ app.listen(port, () => {
83
+ console.log(`Server running at http://localhost:${port}`);
84
+ });
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es6",
4
+ "module": "commonjs",
5
+ "outDir": "./dist",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "forceConsistentCasingInFileNames": true
10
+ },
11
+ "include": ["src/**/*.ts"],
12
+ "exclude": ["node_modules"]
13
+ }
@@ -0,0 +1,86 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+
4
+ <head>
5
+ <meta charset="utf-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
7
+ <meta http-equiv="X-UA-Compatible" content="ie=edge" />
8
+ <title>Friendly Captcha Javascript SDK example</title>
9
+ <style>
10
+ * {
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans",
16
+ "Helvetica Neue", sans-serif;
17
+ display: flex;
18
+ align-items: center;
19
+ justify-content: center;
20
+ height: 100vh;
21
+ width: 100vw;
22
+ background-color: #f2f2f2;
23
+ }
24
+
25
+ h1 {
26
+ font-size: 1.4em;
27
+ }
28
+
29
+ main {
30
+ width: 100%;
31
+ max-width: 680px;
32
+ }
33
+
34
+ .message {
35
+ width: 100%;
36
+ padding: 0.5em 2em;
37
+ margin: 1.5em 0;
38
+ border-radius: 8px;
39
+ }
40
+
41
+ .message h2 {
42
+ margin-bottom: 0;
43
+ }
44
+
45
+ label {
46
+ display: block;
47
+ }
48
+ </style>
49
+
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>
53
+
54
+ <!-- You can change the data-api-endpoint via this tag. More info here https://developer.friendlycaptcha.com/docs/sdk/configuration -->
55
+ <!-- <meta name="frc-api-endpoint" content="."> -->
56
+ </head>
57
+
58
+ <body>
59
+ <main>
60
+ <h1>Friendly Captcha JavaScript SDK form</h1>
61
+ <% if (message) { %>
62
+ <p>
63
+ <%= message %>
64
+ </p>
65
+ <% } %>
66
+ <form method="POST">
67
+ <div class="form-group">
68
+ <label>Subject:</label><br />
69
+ <input type="text" name="subject" /><br />
70
+ <label>Message:</label><br />
71
+ <textarea name="message"></textarea><br />
72
+ <div class="frc-captcha" data-sitekey="<%= sitekey %>" <% if (widgetEndpoint) { %> data-api-endpoint="<%=
73
+ widgetEndpoint %>" <% } %>></div>
74
+ <input style="margin-top: 1em" type="submit" value="Submit" />
75
+ </div>
76
+ </form>
77
+ </main>
78
+
79
+ <script>
80
+ if (window.history.replaceState) {
81
+ window.history.replaceState(null, null, window.location.href);
82
+ }
83
+ </script>
84
+ </body>
85
+
86
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@friendlycaptcha/server-sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Serverside client SDK for the Friendly Captcha V2 API",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -12,7 +12,8 @@
12
12
  "build:docs": "rm -rf build/docs && api-documenter markdown --output-folder docs --input temp/",
13
13
  "test": "ava test/**/*.ts --timeout=1m",
14
14
  "api-extractor": "api-extractor",
15
- "fmt": "prettier src/**/*.ts test/**/*.ts package.json --write"
15
+ "fmt": "prettier src/**/*.ts test/**/*.ts example/**/*.ts package.json --write",
16
+ "fmt:ci": "prettier src/**/*.ts test/**/*.ts example/**/*.ts package.json --check"
16
17
  },
17
18
  "author": "Friendly Captcha GmbH",
18
19
  "license": "MIT",
package/src/api/index.ts CHANGED
@@ -1 +1 @@
1
- export * from "./types";
1
+ export * from "./types.js";
@@ -84,7 +84,10 @@ export class FriendlyCaptchaClient {
84
84
  * * `sitekey`: The sitekey to use for this request. Defaults to the sitekey passed to the constructor (if any).
85
85
  * @returns A promise that always resolves to a `VerifyResult` object, which contains the fields `shouldAccept()` and `wasAbleToVerify()`. This promise never rejects.
86
86
  */
87
- public verifyCaptchaResponse(response: string, opts: { timeout?: number, sitekey?: string } = {}): Promise<VerifyResult> {
87
+ public verifyCaptchaResponse(
88
+ response: string,
89
+ opts: { timeout?: number; sitekey?: string } = {},
90
+ ): Promise<VerifyResult> {
88
91
  const siteverifyRequest: SiteverifyRequest = {
89
92
  response,
90
93
  };
@@ -105,7 +108,7 @@ export class FriendlyCaptchaClient {
105
108
  const headers = {
106
109
  "Content-Type": "application/json",
107
110
  Accept: "application/json",
108
- "X-Frc-Sdk": "friendly-captcha-javascript-sdk@" + SDK_VERSION,
111
+ "Frc-Sdk": "friendly-captcha-javascript-sdk@" + SDK_VERSION,
109
112
  "X-Api-Key": this.apiKey,
110
113
  };
111
114
 
@@ -1,33 +1,33 @@
1
1
  /**
2
2
  * Failed to encode the captcha response. This means the captcha response was invalid and should never be accepted.
3
- *
3
+ *
4
4
  * @public
5
5
  */
6
6
  export const FAILED_TO_ENCODE_ERROR_CODE = "failed_to_encode_request";
7
7
  /**
8
- *
8
+ *
9
9
  * The request couldn't be made, perhaps there is a network outage, DNS issue, or the API is unreachable.
10
- *
10
+ *
11
11
  * @public
12
12
  */
13
13
  export const REQUEST_FAILED_ERROR_CODE = "request_failed";
14
14
  /**
15
- *
15
+ *
16
16
  * The request couldn't be made, perhaps there is a network outage, DNS issue, or the API is unreachable.
17
- *
17
+ *
18
18
  * @public
19
19
  */
20
20
  export const REQUEST_FAILED_TIMEOUT_ERROR_CODE = "request_failed_due_to_timeout";
21
21
  /**
22
22
  * An error occured on the client side that could've been prevented. This generally means your configuration is wrong.
23
- *
23
+ *
24
24
  * Check your API key and sitekey.
25
25
  * @public
26
26
  */
27
27
  export const FAILED_DUE_TO_CLIENT_ERROR_CODE = "request_failed_due_to_client_error";
28
28
  /**
29
29
  * The response from the Friendly Captcha API could not be decoded.
30
- *
30
+ *
31
31
  * @public
32
32
  */
33
33
  export const FAILED_TO_DECODE_RESPONSE_ERROR_CODE = "verification_response_could_not_be_decoded";
@@ -1,3 +1,3 @@
1
1
  export * from "./client.js";
2
2
  export * from "./result.js";
3
- export * from "./errors.js";
3
+ export * from "./errors.js";
@@ -1,4 +1,4 @@
1
- import type { SiteverifyErrorResponseErrorData, SiteverifyResponse } from "../api";
1
+ import type { SiteverifyErrorResponseErrorData, SiteverifyResponse } from "../api/index.js";
2
2
  import {
3
3
  FAILED_DUE_TO_CLIENT_ERROR_CODE,
4
4
  FAILED_TO_DECODE_RESPONSE_ERROR_CODE,
@@ -10,14 +10,13 @@ import {
10
10
 
11
11
  /**
12
12
  * The result of a captcha siteverify request.
13
- *
13
+ *
14
14
  * @public
15
15
  */
16
16
  export class VerifyResult {
17
-
18
17
  private strict: boolean;
19
18
  /**
20
- * The HTTP status code of the response.
19
+ * The HTTP status code of the response.
21
20
  * `-1` if there was no response.
22
21
  */
23
22
  public status: number = -1;
@@ -40,7 +39,7 @@ export class VerifyResult {
40
39
  }
41
40
 
42
41
  /**
43
- * @returns Whether the captcha should be accepted.
42
+ * @returns Whether the captcha should be accepted.
44
43
  * Note that this does not necessarily mean it was verified.
45
44
  */
46
45
  public shouldAccept(): boolean {
@@ -96,7 +95,9 @@ export class VerifyResult {
96
95
  * Something went wrong making the request to the Friendly Captcha API, perhaps there is a network connection issue?
97
96
  */
98
97
  public isRequestOrTimeoutError(): boolean {
99
- return this.clientErrorType === REQUEST_FAILED_ERROR_CODE || this.clientErrorType === REQUEST_FAILED_TIMEOUT_ERROR_CODE;
98
+ return (
99
+ this.clientErrorType === REQUEST_FAILED_ERROR_CODE || this.clientErrorType === REQUEST_FAILED_TIMEOUT_ERROR_CODE
100
+ );
100
101
  }
101
102
 
102
103
  /**
@@ -139,7 +140,7 @@ export class VerifyResult {
139
140
  * If this is false, you should notify yourself and use `getErrorCode()` and `getResponseError()` to see what is wrong.
140
141
  */
141
142
  public wasAbleToVerify(): boolean {
142
- // If we failed to encode, we actually consider `wasAbleToVerify` to be true. This is because we don't want to
143
+ // If we failed to encode, we actually consider `wasAbleToVerify` to be true. This is because we don't want to
143
144
  // alert on failed encoding: an attacker could send such malformed data that it fails to encode.
144
145
  if (this.isEncodeError()) {
145
146
  return true;
@@ -1,3 +1,3 @@
1
1
  // This file is auto-generated by scripts/updateVersion.mjs
2
2
  // Do not edit this file directly
3
- export const SDK_VERSION = "0.1.0";
3
+ export const SDK_VERSION = "0.1.2";
@@ -34,15 +34,14 @@ test("unencodable response gets rejected", async (t) => {
34
34
  });
35
35
 
36
36
  test("request failure gets accepted", async (t) => {
37
- const client = new FriendlyCaptchaClient({ apiKey: "my-api-key", siteverifyEndpoint: "http://localhost:9999" }); // Assumes nothing is listening on port 9999
38
- const result = await client.verifyCaptchaResponse("something");
39
-
40
- t.false(result.isEncodeError());
41
- t.false(result.isClientError());
42
- t.true(result.isRequestOrTimeoutError());
43
- t.false(result.isDecodeError());
44
-
45
- t.true(result.shouldAccept());
46
- t.false(result.shouldReject());
47
- });
48
-
37
+ const client = new FriendlyCaptchaClient({ apiKey: "my-api-key", siteverifyEndpoint: "http://localhost:9999" }); // Assumes nothing is listening on port 9999
38
+ const result = await client.verifyCaptchaResponse("something");
39
+
40
+ t.false(result.isEncodeError());
41
+ t.false(result.isClientError());
42
+ t.true(result.isRequestOrTimeoutError());
43
+ t.false(result.isDecodeError());
44
+
45
+ t.true(result.shouldAccept());
46
+ t.false(result.shouldReject());
47
+ });