213 lines
6.8 KiB
YAML
213 lines
6.8 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: WRNexus Managed CAPTCHA API
|
|
version: 0.5.0
|
|
servers:
|
|
- url: https://captcha.example.com
|
|
paths:
|
|
/v1/challenges:
|
|
post:
|
|
summary: Create a public challenge
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [siteKey, action]
|
|
properties:
|
|
siteKey: { type: string }
|
|
action: { type: string }
|
|
type:
|
|
{
|
|
type: string,
|
|
enum:
|
|
[
|
|
number,
|
|
alpha,
|
|
alphanumeric,
|
|
calculation,
|
|
image,
|
|
honeypot,
|
|
timing,
|
|
not-robot,
|
|
],
|
|
}
|
|
presentation: { type: string, enum: [visual, audio, invisible] }
|
|
difficulty: { type: string, enum: [easy, normal, hard] }
|
|
disturbance: { type: integer, minimum: 25, maximum: 75, default: 50 }
|
|
imageStyle:
|
|
type: string
|
|
default: random
|
|
enum:
|
|
[
|
|
random,
|
|
classic,
|
|
collision,
|
|
snow,
|
|
corrosion,
|
|
spiderweb,
|
|
cross-shadow,
|
|
split,
|
|
split2,
|
|
cut,
|
|
darts,
|
|
distortion,
|
|
stitch,
|
|
striped,
|
|
wave,
|
|
grid-noise,
|
|
scribble,
|
|
pixel,
|
|
broken-lines,
|
|
]
|
|
allowedStyles:
|
|
oneOf:
|
|
- { type: string, description: Comma-separated renderer names }
|
|
- type: array
|
|
items: { type: string }
|
|
excludedStyles:
|
|
oneOf:
|
|
- { type: string, description: Comma-separated renderer names }
|
|
- type: array
|
|
items: { type: string }
|
|
randomizeStyle: { type: boolean, default: false }
|
|
locale: { type: string }
|
|
responses:
|
|
"201":
|
|
description: Challenge created
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/Challenge" }
|
|
"403": { description: Invalid site key or hostname }
|
|
"429": { description: Project quota exceeded }
|
|
/v1/solve:
|
|
post:
|
|
summary: Solve a public challenge and receive a temporary response token
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [challengeId, action]
|
|
properties:
|
|
challengeId: { type: string }
|
|
action: { type: string }
|
|
answer: { oneOf: [{ type: string }, { type: number }] }
|
|
selections: { type: array, items: { type: string } }
|
|
honeypot: { type: string }
|
|
timingToken: { type: string }
|
|
responses:
|
|
"200":
|
|
description: Challenge solved
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/Verification" }
|
|
"400": { description: Challenge rejected }
|
|
/v1/verify:
|
|
post:
|
|
summary: Verify and consume a response token on the application server
|
|
security: [{ projectSecret: [] }]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [responseToken, action]
|
|
properties:
|
|
responseToken: { type: string }
|
|
action: { type: string }
|
|
hostname: { type: string }
|
|
sessionId: { type: string }
|
|
responses:
|
|
"200":
|
|
description: Token accepted and consumed
|
|
content:
|
|
application/json:
|
|
schema: { $ref: "#/components/schemas/Verification" }
|
|
"400": { description: Token invalid, expired, mismatched, or already used }
|
|
"401": { description: Invalid project secret }
|
|
/v1/projects:
|
|
get:
|
|
summary: List projects
|
|
security: [{ adminToken: [] }]
|
|
responses:
|
|
"200": { description: Project list without secrets }
|
|
post:
|
|
summary: Create a project and return keys once
|
|
security: [{ adminToken: [] }]
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
required: [name]
|
|
properties:
|
|
name: { type: string }
|
|
allowedHostnames: { type: array, items: { type: string } }
|
|
monthlyQuota: { type: integer, minimum: 1 }
|
|
responses:
|
|
"201": { description: Project and initial keys }
|
|
/v1/projects/{id}/rotate-secret:
|
|
post:
|
|
summary: Rotate a project verification secret
|
|
security: [{ adminToken: [] }]
|
|
parameters:
|
|
- in: path
|
|
name: id
|
|
required: true
|
|
schema: { type: string }
|
|
responses:
|
|
"200": { description: New secret returned once }
|
|
"404": { description: Project not found }
|
|
components:
|
|
securitySchemes:
|
|
projectSecret:
|
|
type: http
|
|
scheme: bearer
|
|
adminToken:
|
|
type: http
|
|
scheme: bearer
|
|
schemas:
|
|
Challenge:
|
|
type: object
|
|
required:
|
|
[id, provider, type, presentation, action, prompt, createdAt, expiresAt, responseField]
|
|
properties:
|
|
id: { type: string }
|
|
provider: { type: string }
|
|
type: { type: string }
|
|
presentation: { type: string }
|
|
action: { type: string }
|
|
prompt: { type: string }
|
|
image: { type: string }
|
|
audioUrl: { type: string }
|
|
items: { type: array, items: { type: object } }
|
|
createdAt: { type: integer }
|
|
expiresAt: { type: integer }
|
|
responseField: { type: string }
|
|
metadata:
|
|
type: object
|
|
properties:
|
|
difficulty: { type: string }
|
|
disturbance: { type: integer }
|
|
imageStyle: { type: string }
|
|
requestedImageStyle: { type: string }
|
|
imageStylePool:
|
|
type: array
|
|
items: { type: string }
|
|
Verification:
|
|
type: object
|
|
required: [success, provider, action]
|
|
properties:
|
|
success: { type: boolean }
|
|
provider: { type: string }
|
|
action: { type: string }
|
|
code: { type: string }
|
|
message: { type: string }
|
|
responseToken: { type: string }
|
|
expiresAt: { type: integer }
|