Intégrations
Des paquets prêts à l’emploi pour Laravel, Symfony, WordPress et WoltLab Suite — et le guide pour créer le vôtre.
Intégration Laravel
1. Déclarer le dépôt
Ajoutez le dépôt CaptchaCore à votre composer.json :
{
"repositories": [
{
"type": "composer",
"url": "https://captchacore.eu/packages"
}
]
}
2. Installer le paquet
composer require captchacore/laravel # optional php artisan vendor:publish --tag=captchacore-config php artisan vendor:publish --tag=captchacore-lang
Laravel enregistre le ServiceProvider, l’alias du middleware, le composant Blade et la façade via l’auto-discovery. Vous ne devez publier la configuration que si vous souhaitez modifier des valeurs en dehors du .env.
3. Configurer le .env
CAPTCHACORE_URL=https://api.captchacore.eu CAPTCHACORE_SITE_KEY=cc_pub_xxxxxxxxxxxxxxxxxxxxxxxxxxxx CAPTCHACORE_SECRET_KEY=cc_sec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx # optional CAPTCHACORE_TIMEOUT=3 # Sekunden CAPTCHACORE_FAIL_OPEN=true # bei Störung des Dienstes durchlassen CAPTCHACORE_WIDGET_MODE= # interactive | visible | invisible CAPTCHACORE_WIDGET_THEME= # auto | light | dark CAPTCHACORE_WIDGET_COLOR= # z. B. #34d399 CAPTCHACORE_WIDGET_LABEL= CAPTCHACORE_WIDGET_BRAND= CAPTCHACORE_WIDGET_SIZE= # normal | compact CAPTCHACORE_WIDGET_LANG= # de | en | fr | es … CAPTCHACORE_WIDGET_ENDPOINT=eu # eu | global CAPTCHACORE_WIDGET_CDN_URL= # eigener Widget-Host (optional)
Le paquet n’émet pas les options du widget laissées vides : le widget reprend alors les réglages de l’espace client et la langue de la page.
Composant Blade
<form method="post" action="/contact"> @csrf <!-- … --> <x-captchacore::widget form-type="contact" /> @error('captchacore') <p class="error">{{ $message }}</p> @enderror <button type="submit">Absenden</button> </form>
Le type de formulaire du widget et de la vérification doit être identique : form-type="contact" correspond à captchacore:contact ou new CaptchaCoreToken('contact'). Chaque type a sa propre difficulté ; un type différent est rejeté.
Middleware
use App\Http\Controllers\ContactController; use Illuminate\Support\Facades\Route; Route::post('/contact', [ContactController::class, 'send']) ->middleware('captchacore:contact');
Les méthodes POST, PUT, PATCH et DELETE sont vérifiées. En cas de refus, le middleware redirige en arrière avec les saisies et le message sous la clé captchacore. Les requêtes via fetch() ou XHR reçoivent du JSON avec le statut 422 et errors.captchacore — envoyez pour cela Accept: application/json (exemple sous « Envoyer des formulaires via fetch() »).
Règle de validation
use CaptchaCore\Laravel\Rules\CaptchaCoreToken; 'captchacore_token' => [new CaptchaCoreToken('contact')],
La règle s’applique aussi sans token — required n’est pas nécessaire et produirait un second message. Le message se trouve sous le nom du champ : @error('captchacore_token').
Façade
use CaptchaCore\Laravel\Facades\CaptchaCore; $result = CaptchaCore::verify($request->input('captchacore_token'), 'comment'); if (! $result->passed()) { return back()->withInput()->withErrors(['captchacore' => __('captchacore::messages.failed')]); }
Messages, logs et proxys
- Les messages sont fournis en allemand, anglais, espagnol et français et suivent la langue de l’application. Adaptez-les après
vendor:publish --tag=captchacore-langdanslang/vendor/captchacore. - Les refus sont journalisés en info avec les motifs et le score de risque, les pannes avec fail-open en warning — le token lui-même n’est jamais journalisé.
- Derrière un répartiteur de charge, un CDN ou un proxy inverse, configurez TrustProxies (
$middleware->trustProxies(at: …)), sinon CaptchaCore voit l’IP du proxy pour tous les visiteurs. - CSP stricte : le composant reprend le nonce de Vite (
Vite::useCspNonce()) ounonce="…"et l’ajoute à la balise script.
Tests
use CaptchaCore\Laravel\Facades\CaptchaCore; CaptchaCore::fake(); // jede Prüfung besteht; wird bei jedem App-Start zurückgesetzt $this->post('/contact', ['captchacore_token' => 'fake'])->assertRedirect();
Symfony Bundle
Pour Symfony 6.4 LTS, 7.x et 8.x. Quatre voies d'intégration : FormType, contrainte de validation, attributs de contrôleur et un badge Security pour la connexion.
1. Installer
Ajoutez le dépôt CaptchaCore à votre composer.json :
{
"repositories": [
{ "type": "composer", "url": "https://captchacore.eu/packages" }
]
}
composer require captchacore/captchacore-bundle
Inscrivez le bundle dans config/bundles.php et créez la configuration dans config/packages/captchacore.yaml : Les deux étapes sont manuelles — il n’existe pas de recette Flex pour le bundle.
// config/bundles.php CaptchaCore\SymfonyBundle\CaptchaCoreBundle::class => ['all' => true], # config/packages/captchacore.yaml captchacore: service_url: '%env(CAPTCHACORE_URL)%' site_key: '%env(CAPTCHACORE_SITE_KEY)%' secret_key: '%env(CAPTCHACORE_SECRET_KEY)%'
2. Configurer le .env
CAPTCHACORE_URL=https://api.captchacore.eu CAPTCHACORE_SITE_KEY=cc_pub_xxxxxxxxxxxxxxxxxxxxxxxxxxxx CAPTCHACORE_SECRET_KEY=cc_sec_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
FormType (recommandé)
use CaptchaCore\SymfonyBundle\Form\Type\CaptchaCoreType; $builder ->add('email', EmailType::class) ->add('message', TextareaType::class) ->add('captcha', CaptchaCoreType::class, [ 'form_type' => 'contact', ]);
La voie recommandée pour les formulaires HTML : un refus apparaît comme une erreur de formulaire normale. Le widget envoie toujours son token sous captchacore_token ; le FormType le reprend avant la validation.
Attributs de contrôleur
use CaptchaCore\SymfonyBundle\Security\Attribute\RequiresCaptcha; #[Route('/contact', methods: ['POST'])] #[RequiresCaptcha(formType: 'contact')] public function submit(Request $request): Response { /* ... */ }
Un refus se termine par le statut 403 (journalisé en info, pas en error) — adapté aux points de terminaison appelés via JavaScript. Avec throwOnFailure: false, vous obtenez à la place du JSON avec le statut 422. Pour les formulaires HTML, utilisez le FormType. L’attribut ne protège pas la connexion, car le pare-feu authentifie avant le contrôleur.
Contrainte Validator (DTOs)
Pour #[MapRequestPayload], Symfony a besoin en plus de symfony/serializer et symfony/property-access.
use CaptchaCore\SymfonyBundle\Validator\CaptchaCoreToken; final class ContactDto { public function __construct( #[Assert\NotBlank] public string $email, #[CaptchaCoreToken(formType: 'contact')] public string $captchaToken, ) {} }
Programmatique
use CaptchaCore\SymfonyBundle\Client\CaptchaCoreClient; $result = $this->captcha->verify($token, 'login'); if ($result->blocked()) { throw new AccessDeniedHttpException(); } // Properties: valid, riskScore, action, confidence, reasons, stepUp
Twig (standalone)
{{ captchacore_widget(form_type: 'contact') }}
@{# Strikte CSP: Nonce Ihrer Seite mitgeben #}
{{ captchacore_widget(form_type: 'contact', nonce: page_nonce) }}
{{ form_row(form.captcha, {nonce: page_nonce}) }}
Sans form_type, default_form_type s’applique, comme pour le FormType. Le script n’est inclus qu’une fois par page, même avec plusieurs widgets ou combiné au FormType. Le widget reprend le nonce de sa balise script pour son élément style ; autorisez-le dans script-src et style-src-elem. Définissez votre propre hôte de widget avec widget.cdn_url.
Protéger la connexion
Le form_login standard de Symfony n’est pas protégé : seules les connexions portant un CaptchaCoreBadge sont vérifiées. Remplacez form_login par votre propre authenticator qui ajoute le badge — le token est alors vérifié avant le mot de passe.
// src/Security/LoginFormAuthenticator.php namespace App\Security; use CaptchaCore\SymfonyBundle\Security\CaptchaCoreAuthenticator; use CaptchaCore\SymfonyBundle\Security\CaptchaCoreBadge; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\RememberMeBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials; use Symfony\Component\Security\Http\Authenticator\Passport\Passport; use Symfony\Component\Security\Http\SecurityRequestAttributes; use Symfony\Component\Security\Http\Util\TargetPathTrait; final class LoginFormAuthenticator extends AbstractLoginFormAuthenticator { use TargetPathTrait; public function __construct(private readonly UrlGeneratorInterface $urlGenerator) {} public function authenticate(Request $request): Passport { $payload = $request->getPayload(); $username = $payload->getString('_username'); $request->getSession()->set(SecurityRequestAttributes::LAST_USERNAME, $username); return new Passport( new UserBadge($username), new PasswordCredentials($payload->getString('_password')), [ new CaptchaCoreBadge(CaptchaCoreAuthenticator::extractToken($request) ?? '', 'login'), new CsrfTokenBadge('authenticate', $payload->getString('_csrf_token')), new RememberMeBadge(), ], ); } public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response { $target = $this->getTargetPath($request->getSession(), $firewallName); return new RedirectResponse($target ?? $this->urlGenerator->generate('app_home')); } protected function getLoginUrl(Request $request): string { return $this->urlGenerator->generate('app_login'); } }
# config/packages/security.yaml — statt form_login security: firewalls: main: custom_authenticator: App\Security\LoginFormAuthenticator logout: path: app_logout @{# templates/security/login.html.twig, im <form method="post"> #} <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}"> {{ captchacore_widget(form_type: 'login') }}
app_login est la route de votre page de connexion (GET affiche le formulaire, POST est traité par l’authenticator), app_home la page après la connexion. L’exemple complet avec contrôleur et template se trouve dans le README du bundle.
Tests
CaptchaCoreClient::fake(VerificationResult::allow()); $this->client->request('POST', '/contact', [...]); self::assertResponseIsSuccessful();
Plugin WordPress
- Installez l’extension — dans WordPress, ouvrez
Extensions → Ajouteret cherchez « CaptchaCore » (fiche dans le répertoire WordPress). Les mises à jour viennent ensuite de WordPress. Sinon, téléversez le ZIP depuis votre espace client. - Activer dans Extensions
- Ouvrir Réglages > CaptchaCore
- Renseigner l'URL du service, la clé de site et la clé secrète
- Choisir le mode du widget, le thème et la couleur
- Activer les formulaires souhaités (connexion, inscription, commentaires, etc.)
Aucun code requis. Le plugin intègre automatiquement le widget dans tous les formulaires WordPress activés et vérifie les tokens côté serveur.
WoltLab Suite
Le paquet est distribué via la boutique officielle de plugins WoltLab. L’installation et toutes les mises à jour passent par elle — il n’y a pas de téléchargement séparé dans l’espace client.
- Achetez le paquet dans la boutique de plugins et installez-le dans l’ACP sous
Paquets → Installer un paquet - Saisissez vos identifiants sous
Options → Sécurité → Anti-spam → CaptchaCore: URL du service, site key et secret key - Choisissez l’apparence : mode, thème, couleur d’accent et langue — « auto » suit la langue du forum.
- Sélectionnez CaptchaCore comme captcha sous
Options → Sécurité → Anti-spam → Captcha— il s’applique alors à l’inscription, au formulaire de contact et à tous les autres formulaires du système
| Réglage | Signification |
|---|---|
| Point d'accès | Endpoint UE par défaut ou mondial. L’UE conserve toutes les données de vérification dans l’UE ; l’endpoint global accélère la réponse pour les visiteurs éloignés. |
| Mode | interactive, visible ou invisible — les trois mêmes modes que partout ailleurs, voir les modes du widget. |
| Comportement en cas de panne | Si CaptchaCore est injoignable, le plugin laisse passer le formulaire par défaut et journalise l’erreur. Désactivable si vous préférez bloquer en cas de doute. |
Un seul paquet pour 6.1 et 6.2. Le handler vérifie chaque jeton côté serveur via l’API V2 ; la vérification supplémentaire (« step_up ») est prise en charge par le widget lui-même et le visiteur reçoit un message dédié plutôt qu’une erreur.
Créer votre propre intégration
Vous voulez intégrer CaptchaCore dans un système sans paquet existant, comme Shopware, Joomla ou votre propre framework ? Comptez quelques heures. Cet ordre a fait ses preuves.
Diffuser le script
Incluez captchacore-v2.min.js depuis le point d'accès UE et transmettez la clé de site et l'URL du service comme attributs data. Dans un CMS, cela relève du hook qui enregistre les scripts du frontend. Ne chargez le script que sur les pages contenant réellement un formulaire protégé.
Marquer les formulaires
Placez data-captchacore sur le formulaire et insérez un conteneur vide avec data-captchacore-widget. data-form-type décide de la politique appliquée. Plusieurs formulaires sur une page ne posent pas de problème, chacun reçoit son propre widget.
Vérifier le jeton côté serveur
Lisez le champ captchacore_token de la requête et envoyez-le avec votre clé secrète à /api/v2/verify. Vérifiez decision et ne traitez pas step_up comme block.
Gérer les pannes
Fixez un délai d'attente d'environ trois secondes et décidez délibérément si une panne laisse passer ou bloque. Rendez-le configurable dans l'extension, la réponse diffère selon le formulaire.
Prendre en compte le chargement différé
Si votre système remplace des formulaires via AJAX, appelez ensuite CaptchaCoreV2.attach().
Proposer des réglages
Clé de site, clé secrète, choix des formulaires protégés, apparence et comportement en cas de panne ont leur place dans l'écran de configuration de votre extension.
Modèles dont s'inspirer
Toutes les intégrations fournies suivent le même schéma. Pour un système basé sur Symfony comme Shopware 6, le bundle Symfony est le modèle le plus proche ; pour un CMS classique, l'extension WordPress.
À vérifier avant publication
- Formulaire envoyé sans JavaScript — votre règle de repli s'applique-t-elle ?
- Jeton utilisé deux fois — la seconde vérification doit échouer, le nonce est à usage unique.
- Jeton envoyé après plus de cinq minutes — le widget le renouvelle de lui-même, vérifiez-le lors d'un test de longue durée.
- Deux formulaires protégés sur une page — les deux doivent fonctionner indépendamment.
- Formulaire chargé via AJAX — le widget apparaît-il ?
- La clé secrète n'apparaît nulle part dans le HTML ni en JavaScript.
- Une réponse avec decision=step_up — l'utilisateur ne doit pas se retrouver dans une impasse.
Vous développez une intégration et souhaitez que nous la référencions ici ou que nous participions à sa maintenance ? Contactez-nous — nous fournissons des accès de test et un interlocuteur.