75 lines
2.9 KiB
TypeScript
75 lines
2.9 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { createCaptchaEngine, MemoryCaptchaStore, type CaptchaChallengeGenerator } from "@wrnexus/captcha/server";
|
|
import { ManagedCaptchaService } from "../src/service.ts";
|
|
|
|
let seed = 11;
|
|
function randomBytes(length: number): Uint8Array {
|
|
const output = new Uint8Array(length);
|
|
for (let index = 0; index < length; index += 1) {
|
|
seed = (seed * 1103515245 + 12345) >>> 0;
|
|
output[index] = seed & 255;
|
|
}
|
|
return output;
|
|
}
|
|
|
|
const generator: CaptchaChallengeGenerator = {
|
|
type: "number",
|
|
generate: () => ({
|
|
type: "number",
|
|
presentation: "visual",
|
|
prompt: "Enter 7",
|
|
answer: "7",
|
|
answerKind: "text",
|
|
inputMode: "numeric",
|
|
}),
|
|
};
|
|
|
|
describe("managed CAPTCHA service", () => {
|
|
test("creates a project and completes the public-to-server flow", async () => {
|
|
const service = new ManagedCaptchaService({
|
|
adminToken: "a-long-admin-token-for-managed-captcha-tests",
|
|
baseUrl: "https://captcha.test",
|
|
randomBytes,
|
|
engineFactory: (project) => createCaptchaEngine({
|
|
secret: project.engineSecret,
|
|
store: new MemoryCaptchaStore(),
|
|
generators: [generator],
|
|
defaultType: "number",
|
|
minCompletionMs: 0,
|
|
randomBytes,
|
|
}),
|
|
});
|
|
const project = await service.createProject({ name: "Test", allowedHostnames: ["app.test"] });
|
|
const created = await service.handle(new Request("https://captcha.test/v1/challenges", {
|
|
method: "POST",
|
|
headers: { origin: "https://app.test", "content-type": "application/json" },
|
|
body: JSON.stringify({ siteKey: project.siteKey, action: "signup", type: "number" }),
|
|
}));
|
|
expect(created.status).toBe(201);
|
|
const challenge = await created.json() as { id: string; provider: string };
|
|
expect(challenge.provider).toBe("wrnexus-managed");
|
|
|
|
const solved = await service.handle(new Request("https://captcha.test/v1/solve", {
|
|
method: "POST",
|
|
headers: { origin: "https://app.test", "content-type": "application/json" },
|
|
body: JSON.stringify({ challengeId: challenge.id, action: "signup", answer: "7" }),
|
|
}));
|
|
expect(solved.status).toBe(200);
|
|
const solution = await solved.json() as { responseToken: string };
|
|
|
|
const verified = await service.handle(new Request("https://captcha.test/v1/verify", {
|
|
method: "POST",
|
|
headers: { authorization: `Bearer ${project.secretKey}`, "content-type": "application/json" },
|
|
body: JSON.stringify({ responseToken: solution.responseToken, action: "signup", hostname: "app.test" }),
|
|
}));
|
|
expect(verified.status).toBe(200);
|
|
|
|
const replay = await service.handle(new Request("https://captcha.test/v1/verify", {
|
|
method: "POST",
|
|
headers: { authorization: `Bearer ${project.secretKey}`, "content-type": "application/json" },
|
|
body: JSON.stringify({ responseToken: solution.responseToken, action: "signup", hostname: "app.test" }),
|
|
}));
|
|
expect(replay.status).toBe(400);
|
|
});
|
|
});
|