stages/captcha: add Cap and JSON verification support (#22373)

* stages/captcha: add Cap and JSON verification support

Add a configurable verification request content type so CAPTCHA providers can use either form-encoded or JSON token verification.

Add Cap as a preset and flow controller, including module-script loading, interactive widget handling, generated API/client types, tests, and docs.

* web/admin: clarify Cap captcha configuration

Treat the Cap endpoint as a form-only alias for the existing public key field and document Cap alongside the other CAPTCHA providers.

Agent-thread: https://sdko.org/internal/threads/019e737a-314e-72d0-98ae-201cb855df3a

A7k-product: product

A7k-product-repo: 2

Co-authored-by: Agent <agent@svc.sdko.net>

* stages/captcha: prefer self-hosted Cap widget URL

Default the Cap provider guidance to the self-hosted widget asset and keep CDN usage pinned to reviewed releases.

Agent-thread: https://sdko.org/internal/thr/ak/019ead31-2435-7e12-b933-e873155d6894

A7k-product: product

A7k-product-repo: 2

Co-authored-by: Agent <agent@svc.sdko.net>

* floating

---------

Co-authored-by: Agent <agent@svc.sdko.net>
Co-authored-by: Simonyi Gergő <28359278+gergosimonyi@users.noreply.github.com>
This commit is contained in:
Dominic R
2026-06-11 12:15:21 -04:00
committed by GitHub
parent 80e7dd0c62
commit fc8424ac50
21 changed files with 563 additions and 44 deletions
+16
View File
@@ -14,6 +14,11 @@
import type { FlowSet } from "./FlowSet";
import { FlowSetFromJSON } from "./FlowSet";
import type { RequestContentTypeEnum } from "./RequestContentTypeEnum";
import {
RequestContentTypeEnumFromJSON,
RequestContentTypeEnumToJSON,
} from "./RequestContentTypeEnum";
/**
* CaptchaStage Serializer
@@ -81,6 +86,12 @@ export interface CaptchaStage {
* @memberof CaptchaStage
*/
apiUrl?: string;
/**
*
* @type {RequestContentTypeEnum}
* @memberof CaptchaStage
*/
requestContentType?: RequestContentTypeEnum;
/**
*
* @type {boolean}
@@ -141,6 +152,10 @@ export function CaptchaStageFromJSONTyped(json: any, ignoreDiscriminator: boolea
publicKey: json["public_key"],
jsUrl: json["js_url"] == null ? undefined : json["js_url"],
apiUrl: json["api_url"] == null ? undefined : json["api_url"],
requestContentType:
json["request_content_type"] == null
? undefined
: RequestContentTypeEnumFromJSON(json["request_content_type"]),
interactive: json["interactive"] == null ? undefined : json["interactive"],
scoreMinThreshold:
json["score_min_threshold"] == null ? undefined : json["score_min_threshold"],
@@ -171,6 +186,7 @@ export function CaptchaStageToJSONTyped(
public_key: value["publicKey"],
js_url: value["jsUrl"],
api_url: value["apiUrl"],
request_content_type: RequestContentTypeEnumToJSON(value["requestContentType"]),
interactive: value["interactive"],
score_min_threshold: value["scoreMinThreshold"],
score_max_threshold: value["scoreMaxThreshold"],
+17
View File
@@ -12,6 +12,12 @@
* Do not edit the class manually.
*/
import type { RequestContentTypeEnum } from "./RequestContentTypeEnum";
import {
RequestContentTypeEnumFromJSON,
RequestContentTypeEnumToJSON,
} from "./RequestContentTypeEnum";
/**
* CaptchaStage Serializer
* @export
@@ -48,6 +54,12 @@ export interface CaptchaStageRequest {
* @memberof CaptchaStageRequest
*/
apiUrl?: string;
/**
*
* @type {RequestContentTypeEnum}
* @memberof CaptchaStageRequest
*/
requestContentType?: RequestContentTypeEnum;
/**
*
* @type {boolean}
@@ -101,6 +113,10 @@ export function CaptchaStageRequestFromJSONTyped(
privateKey: json["private_key"],
jsUrl: json["js_url"] == null ? undefined : json["js_url"],
apiUrl: json["api_url"] == null ? undefined : json["api_url"],
requestContentType:
json["request_content_type"] == null
? undefined
: RequestContentTypeEnumFromJSON(json["request_content_type"]),
interactive: json["interactive"] == null ? undefined : json["interactive"],
scoreMinThreshold:
json["score_min_threshold"] == null ? undefined : json["score_min_threshold"],
@@ -129,6 +145,7 @@ export function CaptchaStageRequestToJSONTyped(
private_key: value["privateKey"],
js_url: value["jsUrl"],
api_url: value["apiUrl"],
request_content_type: RequestContentTypeEnumToJSON(value["requestContentType"]),
interactive: value["interactive"],
score_min_threshold: value["scoreMinThreshold"],
score_max_threshold: value["scoreMaxThreshold"],
@@ -12,6 +12,12 @@
* Do not edit the class manually.
*/
import type { RequestContentTypeEnum } from "./RequestContentTypeEnum";
import {
RequestContentTypeEnumFromJSON,
RequestContentTypeEnumToJSON,
} from "./RequestContentTypeEnum";
/**
* CaptchaStage Serializer
* @export
@@ -48,6 +54,12 @@ export interface PatchedCaptchaStageRequest {
* @memberof PatchedCaptchaStageRequest
*/
apiUrl?: string;
/**
*
* @type {RequestContentTypeEnum}
* @memberof PatchedCaptchaStageRequest
*/
requestContentType?: RequestContentTypeEnum;
/**
*
* @type {boolean}
@@ -100,6 +112,10 @@ export function PatchedCaptchaStageRequestFromJSONTyped(
privateKey: json["private_key"] == null ? undefined : json["private_key"],
jsUrl: json["js_url"] == null ? undefined : json["js_url"],
apiUrl: json["api_url"] == null ? undefined : json["api_url"],
requestContentType:
json["request_content_type"] == null
? undefined
: RequestContentTypeEnumFromJSON(json["request_content_type"]),
interactive: json["interactive"] == null ? undefined : json["interactive"],
scoreMinThreshold:
json["score_min_threshold"] == null ? undefined : json["score_min_threshold"],
@@ -128,6 +144,7 @@ export function PatchedCaptchaStageRequestToJSONTyped(
private_key: value["privateKey"],
js_url: value["jsUrl"],
api_url: value["apiUrl"],
request_content_type: RequestContentTypeEnumToJSON(value["requestContentType"]),
interactive: value["interactive"],
score_min_threshold: value["scoreMinThreshold"],
score_max_threshold: value["scoreMaxThreshold"],
+58
View File
@@ -0,0 +1,58 @@
/* tslint:disable */
/* eslint-disable */
/**
* authentik
* Making authentication simple.
*
* The version of the OpenAPI document: 2026.8.0-rc1
* Contact: hello@goauthentik.io
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/
/**
*
* @export
*/
export const RequestContentTypeEnum = {
ApplicationXWwwFormUrlencoded: "application/x-www-form-urlencoded",
ApplicationJson: "application/json",
UnknownDefaultOpenApi: "11184809",
} as const;
export type RequestContentTypeEnum =
(typeof RequestContentTypeEnum)[keyof typeof RequestContentTypeEnum];
export function instanceOfRequestContentTypeEnum(value: any): boolean {
for (const key in RequestContentTypeEnum) {
if (Object.prototype.hasOwnProperty.call(RequestContentTypeEnum, key)) {
if (RequestContentTypeEnum[key as keyof typeof RequestContentTypeEnum] === value) {
return true;
}
}
}
return false;
}
export function RequestContentTypeEnumFromJSON(json: any): RequestContentTypeEnum {
return RequestContentTypeEnumFromJSONTyped(json, false);
}
export function RequestContentTypeEnumFromJSONTyped(
json: any,
ignoreDiscriminator: boolean,
): RequestContentTypeEnum {
return json as RequestContentTypeEnum;
}
export function RequestContentTypeEnumToJSON(value?: RequestContentTypeEnum | null): any {
return value as any;
}
export function RequestContentTypeEnumToJSONTyped(
value: any,
ignoreDiscriminator: boolean,
): RequestContentTypeEnum {
return value as RequestContentTypeEnum;
}
+1
View File
@@ -713,6 +713,7 @@ export * from "./RelatedRule";
export * from "./Reputation";
export * from "./ReputationPolicy";
export * from "./ReputationPolicyRequest";
export * from "./RequestContentTypeEnum";
export * from "./Review";
export * from "./ReviewRequest";
export * from "./ReviewerGroup";