release: WRNexusJS 0.4.0
This commit is contained in:
@@ -2,7 +2,9 @@ import type { CaptchaProvider } from "../types.ts";
|
||||
|
||||
export function defineCaptchaProvider<T extends CaptchaProvider>(provider: T): T {
|
||||
if (!provider.name) throw new TypeError("Custom CAPTCHA provider requires a stable name");
|
||||
if (!provider.client?.responseField) throw new TypeError("Custom CAPTCHA provider requires client.responseField");
|
||||
if (typeof provider.verify !== "function") throw new TypeError("Custom CAPTCHA provider requires verify()");
|
||||
if (!provider.client?.responseField)
|
||||
throw new TypeError("Custom CAPTCHA provider requires client.responseField");
|
||||
if (typeof provider.verify !== "function")
|
||||
throw new TypeError("Custom CAPTCHA provider requires verify()");
|
||||
return provider;
|
||||
}
|
||||
|
||||
@@ -32,10 +32,14 @@ export class ManagedCaptchaProvider implements CaptchaProvider {
|
||||
}
|
||||
|
||||
async createChallenge(options: CreateCaptchaOptions): Promise<CaptchaChallenge> {
|
||||
return this.request<CaptchaChallenge>("/v1/challenges", {
|
||||
siteKey: this.options.siteKey,
|
||||
...options,
|
||||
}, false);
|
||||
return this.request<CaptchaChallenge>(
|
||||
"/v1/challenges",
|
||||
{
|
||||
siteKey: this.options.siteKey,
|
||||
...options,
|
||||
},
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
async verify(input: VerifyCaptchaInput): Promise<CaptchaVerificationResult> {
|
||||
@@ -64,6 +68,8 @@ export class ManagedCaptchaProvider implements CaptchaProvider {
|
||||
}
|
||||
}
|
||||
|
||||
export function managedCaptchaProvider(options: ManagedCaptchaProviderOptions): ManagedCaptchaProvider {
|
||||
export function managedCaptchaProvider(
|
||||
options: ManagedCaptchaProviderOptions,
|
||||
): ManagedCaptchaProvider {
|
||||
return new ManagedCaptchaProvider(options);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,10 @@ export class SelfHostedCaptchaProvider implements CaptchaProvider {
|
||||
|
||||
verify(input: VerifyCaptchaInput) {
|
||||
return input.responseToken || input.providerToken
|
||||
? this.engine.verifyResponseToken({ ...input, responseToken: input.responseToken ?? input.providerToken })
|
||||
? this.engine.verifyResponseToken({
|
||||
...input,
|
||||
responseToken: input.responseToken ?? input.providerToken,
|
||||
})
|
||||
: this.engine.verify(input);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,8 +61,10 @@ export class SiteverifyCaptchaProvider implements CaptchaProvider {
|
||||
async verify(input: VerifyCaptchaInput): Promise<CaptchaVerificationResult> {
|
||||
const token = input.providerToken ?? input.responseToken;
|
||||
const action = input.action;
|
||||
if (!token) return providerFailure(this.name, action, "missing-input", "Missing provider response token");
|
||||
if (token.length > 4096) return providerFailure(this.name, action, "invalid-input", "Provider token is too long");
|
||||
if (!token)
|
||||
return providerFailure(this.name, action, "missing-input", "Missing provider response token");
|
||||
if (token.length > 4096)
|
||||
return providerFailure(this.name, action, "invalid-input", "Provider token is too long");
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), this.options.timeoutMs ?? 10_000);
|
||||
const body = new URLSearchParams({ secret: this.options.secretKey, response: token });
|
||||
@@ -79,7 +81,12 @@ export class SiteverifyCaptchaProvider implements CaptchaProvider {
|
||||
signal: controller.signal,
|
||||
});
|
||||
if (!response.ok) {
|
||||
return providerFailure(this.name, action, "provider-error", `${this.name} verification returned HTTP ${response.status}`);
|
||||
return providerFailure(
|
||||
this.name,
|
||||
action,
|
||||
"provider-error",
|
||||
`${this.name} verification returned HTTP ${response.status}`,
|
||||
);
|
||||
}
|
||||
const data = (await response.json()) as SiteverifyPayload;
|
||||
if (!data.success) {
|
||||
@@ -98,26 +105,45 @@ export class SiteverifyCaptchaProvider implements CaptchaProvider {
|
||||
}
|
||||
const expectedAction = this.options.expectedAction ?? action;
|
||||
if (data.action && expectedAction && data.action !== expectedAction) {
|
||||
return providerFailure(this.name, action, "action-mismatch", "Provider action does not match", {
|
||||
receivedAction: data.action,
|
||||
});
|
||||
return providerFailure(
|
||||
this.name,
|
||||
action,
|
||||
"action-mismatch",
|
||||
"Provider action does not match",
|
||||
{
|
||||
receivedAction: data.action,
|
||||
},
|
||||
);
|
||||
}
|
||||
if (
|
||||
this.options.expectedHostnames?.length &&
|
||||
(!data.hostname || !this.options.expectedHostnames.includes(data.hostname))
|
||||
) {
|
||||
return providerFailure(this.name, action, "hostname-mismatch", "Provider hostname does not match", {
|
||||
hostname: data.hostname,
|
||||
});
|
||||
return providerFailure(
|
||||
this.name,
|
||||
action,
|
||||
"hostname-mismatch",
|
||||
"Provider hostname does not match",
|
||||
{
|
||||
hostname: data.hostname,
|
||||
},
|
||||
);
|
||||
}
|
||||
if (this.options.minScore !== undefined && typeof data.score === "number") {
|
||||
const rejected = this.preset.scoreDirection === "higher-is-risk"
|
||||
? data.score >= this.options.minScore
|
||||
: data.score < this.options.minScore;
|
||||
const rejected =
|
||||
this.preset.scoreDirection === "higher-is-risk"
|
||||
? data.score >= this.options.minScore
|
||||
: data.score < this.options.minScore;
|
||||
if (rejected) {
|
||||
return providerFailure(this.name, action, "risk-rejected", "Provider risk score did not pass", {
|
||||
score: data.score,
|
||||
});
|
||||
return providerFailure(
|
||||
this.name,
|
||||
action,
|
||||
"risk-rejected",
|
||||
"Provider risk score did not pass",
|
||||
{
|
||||
score: data.score,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
return {
|
||||
@@ -135,7 +161,9 @@ export class SiteverifyCaptchaProvider implements CaptchaProvider {
|
||||
return providerFailure(
|
||||
this.name,
|
||||
action,
|
||||
error instanceof DOMException && error.name === "AbortError" ? "network-error" : "provider-error",
|
||||
error instanceof DOMException && error.name === "AbortError"
|
||||
? "network-error"
|
||||
: "provider-error",
|
||||
error instanceof DOMException && error.name === "AbortError"
|
||||
? `${this.name} verification timed out`
|
||||
: `${this.name} verification failed`,
|
||||
|
||||
Reference in New Issue
Block a user