JSON endpoints and DTOs
The constraint
Section titled “The constraint”#[Cap] validates any property holding a token:
use Silarhi\CapBundle\Validator\Cap;
final class ContactRequest{ public string $email;
#[Cap] public ?string $capToken = null;}#[Route('/api/contact', methods: ['POST'])]public function contact(#[MapRequestPayload] ContactRequest $request): JsonResponseOn the client, solve the challenge with Cap’s programmatic mode and send the token with the payload:
const cap = new Cap({ apiEndpoint: 'https://cap.example.com/<site-key>/' })const { token } = await cap.solve()
await fetch('/api/contact', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, capToken: token }),})The verifier
Section titled “The verifier”CapVerifierInterface redeems a token directly:
use Silarhi\CapBundle\Verifier\CapVerifierInterface;
public function __construct(private readonly CapVerifierInterface $capVerifier) {}
public function __invoke(Request $request): Response{ $result = $this->capVerifier->verify($request->getPayload()->getString('cap-token'));
if (!$result->isSuccess()) { throw new AccessDeniedHttpException(sprintf('Cap: %s', $result->error)); } // ...}verify() never throws: a Cap server out of reach is a failed verification with the connection-failed error.
Unlike the constraint, it ignores enabled: false.
VerificationResult::$error |
Meaning |
|---|---|
null |
Success |
Token not found |
Unknown or already redeemed token (Cap’s message) |
Token expired |
Solved too long ago (Cap’s message) |
Invalid site key or secret |
Check secret_key and the site key of endpoint |
missing-input-response |
Empty token: no request sent |
connection-failed |
DNS, connection or timeout error |
bad-response |
Cap answered a 5xx |
invalid-json |
Cap answered something else than JSON |
unknown-error |
Cap said no without a reason |
