Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@

All notable changes to `ossycodes/friendlycaptcha` will be documented in this file

## 4.0.0 - 2026-04-08
- Replace the friendly-challenge scripts
```html
<script type="module" src="https://cdn.jsdelivr.net/npm/friendly-challenge@0.9.12/widget.module.min.js" async defer></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/friendly-challenge@0.9.12/widget.min.js" async defer></script>
```
with the new @friendlycaptcha/sdk scripts
```html
<script type="module" src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.2.0/site.min.js"
async defer></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.2.0/site.compat.min.js"
async defer></script>
```

For more information visit: https://developer.friendlycaptcha.com/docs/v2/guides/upgrading-from-v1/script

## 1.0.0 - 2021-10-21

- initial release
15 changes: 8 additions & 7 deletions lang/de/validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
*/

return [
'secret_missing' => 'Sie haben vergessen, den Parameter secret (=API-Schlüssel) hinzuzufügen.',
'secret_invalid' => 'Der von Ihnen angegebene API-Schlüssel war ungültig.',
'solution_missing' => 'Sie haben vergessen, den Parameter secret (=API-Schlüssel) hinzuzufügen.',
'secret_missing' => 'Sie haben vergessen, den Lösungsparameter hinzuzufügen.',
'bad_request' => 'Mit Ihrer Anfrage ist etwas anderes nicht in Ordnung, z. B. ist Ihr Anfragekörper leer.',
'solution_invalid' => 'Die von Ihnen angegebene Lösung war ungültig (vielleicht wurde versucht, das Rätsel zu manipulieren).',
'solution_timeout_or_duplicate' => 'Das Rätsel, für das Sie die Lösung angegeben haben, ist abgelaufen oder wurde bereits verwendet.',
'auth_required' => 'Authentifizierung erforderlich.',
'auth_invalid' => 'Authentifizierung fehlgeschlagen.',
'sitekey_invalid' => 'Der Sitekey ist ungültig.',
'response_missing' => 'Die Captcha-Lösung fehlt.',
'response_invalid' => 'Die Captcha-Lösung ist falsch.',
'response_timeout' => 'Anfrage abgelaufen.',
'response_duplicate' => 'Diese Lösung wurde bereits verwendet.',
'unexpected' => 'Ein unerwarteter Fehler ist aufgetreten.'
];
];
15 changes: 8 additions & 7 deletions lang/en/validation.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
*/

return [
'secret_missing' => 'You forgot to add the secret (=API key) parameter.',
'secret_invalid' => 'The API key you provided was invalid.',
'solution_missing' => 'You forgot to add the secret (=API key) parameter.',
'secret_missing' => 'You forgot to add the solution parameter.',
'bad_request' => 'Something else is wrong with your request, e.g. your request body is empty.',
'solution_invalid' => 'The solution you provided was invalid (perhaps the user tried to tamper with the puzzle).',
'solution_timeout_or_duplicate' => 'The puzzle that the solution was for has expired or has already been used.',
'auth_required' => 'Authentication required.',
'auth_invalid' => 'Authentication failed.',
'sitekey_invalid' => 'The sitekey is invalid.',
'response_missing' => 'The captcha solution is missing.',
'response_invalid' => 'The captcha solution is invalid.',
'response_timeout' => 'Connection timed out.',
'response_duplicate' => 'This solution has already been used.',
'unexpected' => 'An unexpected error occurred.'
];
];
58 changes: 26 additions & 32 deletions src/FriendlyCaptcha.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Ossycodes\FriendlyCaptcha;

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;

class FriendlyCaptcha
{
Expand All @@ -20,11 +21,6 @@ class FriendlyCaptcha
*/
protected $sitekey;

/**
* FriendlyCaptcha Puzzle endpoint
*/
protected $puzzle;

/**
* FriendlyCaptcha verify endpoint
*/
Expand All @@ -35,36 +31,28 @@ class FriendlyCaptcha
*
* @var array
*/
protected $error = [];
protected $errors = [];

public $isSuccess = false;

/**
* @var \GuzzleHttp\Client
* @var Client
*/
protected $http;

public function __construct($secret, $sitekey, $puzzle, $verify, $options = [])
public function __construct($secret, $sitekey, $verify, $options = [])
{
$this->secret = $secret;
$this->sitekey = $sitekey;
$this->puzzle = $puzzle;
$this->verify = $verify;
$this->http = new Client($options);
}

public function renderWidgetScripts($option = 'unpkg')
public function renderWidgetScripts(): string
{
if ($option == 'unpkg') {
return <<<EOF
<script type="module" src="https://unpkg.com/friendly-challenge@0.9.9/widget.module.min.js" async defer></script>
<script nomodule src="https://unpkg.com/friendly-challenge@0.9.9/widget.min.js" async defer></script>
EOF;
}

return <<<EOF
<script type="module" src="https://cdn.jsdelivr.net/npm/friendly-challenge@0.9.9/widget.module.min.js" async defer></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/friendly-challenge@0.9.9/widget.min.js" async defer></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.2.0/site.min.js" async defer></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.2.0/site.compat.min.js" async defer></script>
EOF;
}

Expand All @@ -83,8 +71,6 @@ public function renderWidget($attributes = [])
*/
protected function prepareAttributes(array $attributes)
{
$attributes['data-puzzle-endpoint'] = $this->puzzle;

$attributes['data-sitekey'] = $this->sitekey;

if (isset($attributes['dark-theme'])) {
Expand Down Expand Up @@ -129,8 +115,9 @@ protected function buildAttributes(array $attributes)
* @param string $solution
*
* @return bool
* @throws GuzzleException
*/
public function verifyRequest($solution)
public function verifyRequest(string $solution)
{
return $this->verifyResponse(
$solution,
Expand All @@ -143,18 +130,23 @@ public function verifyRequest($solution)
* @param string $solution
*
* @return self
* @throws GuzzleException
*/
public function verifyResponse($solution)
public function verifyResponse(string $solution): self
{
if (empty($solution)) {
return false;
$this->isSuccess = false;
$this->errors = [];
return $this;
}

$verifyResponse = $this->sendRequestVerify([
'solution' => $solution,
'secret' => $this->secret,
'sitekey' => $this->sitekey,
]);
$verifyResponse = $this->sendRequestVerify(
['X-API-Key' => $this->secret],
[
'response' => $solution,
'sitekey' => $this->sitekey,
]
);

if (isset($verifyResponse['success']) && $verifyResponse['success'] === true) {
$this->isSuccess = true;
Expand All @@ -178,14 +170,16 @@ public function verifyResponse($solution)
/**
* Send verify request.
*
* @param array $headers
* @param array $data
*
* @return array
* @throws GuzzleException
*/
protected function sendRequestVerify(array $data = [])
protected function sendRequestVerify(array $headers = [], array $data = []): array
{
$response = $this->http->request('POST', $this->verify, [
'form_params' => $data,
'headers' => $headers,
'json' => $data,
]);

return json_decode($response->getBody(), true);
Expand Down
30 changes: 5 additions & 25 deletions src/FriendlyCaptchaServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ public function boot()
$this->bootConfig();
}

$this->loadTranslationsFrom(__DIR__.'/../lang', 'friendlycaptcha');

$this->bootBladeDirectives();

$this->bootMacro();

$this->bootLang();
}

/**
Expand All @@ -49,19 +49,10 @@ protected function bootConfig()
*/
public function bootBladeDirectives()
{
Blade::directive('friendlyCaptchaRenderWidgetScripts', function ($option) {
$option = trim($option, "'");

if (empty($option) || $option == 'unpkg') {
return <<<EOF
<script type="module" src="https://unpkg.com/friendly-challenge@0.9.8/widget.module.min.js" async defer></script>
<script nomodule src="https://unpkg.com/friendly-challenge@0.9.8/widget.min.js" async defer></script>
EOF;
}

Blade::directive('friendlyCaptchaRenderWidgetScripts', function () {
return <<<EOF
<script type="module" src="https://cdn.jsdelivr.net/npm/friendly-challenge@0.9.8/widget.module.min.js" async defer></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/friendly-challenge@0.9.8/widget.min.js" async defer></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.2.0/site.min.js" async defer></script>
<script nomodule src="https://cdn.jsdelivr.net/npm/@friendlycaptcha/sdk@0.2.0/site.compat.min.js" async defer></script>
EOF;
});
}
Expand All @@ -76,16 +67,6 @@ public function bootMacro()
});
}

/**
* boot lang
*/
public function bootLang()
{
Rule::macro('friendlycaptcha', function () {
$this->loadTranslationsFrom(__DIR__.'/../lang', 'friendlycaptcha');
});
}

/**
* Register the application services.
*/
Expand All @@ -99,7 +80,6 @@ public function register()
return new FriendlyCaptcha(
$app['config']['friendlycaptcha.secret'],
$app['config']['friendlycaptcha.sitekey'],
$app['config']['friendlycaptcha.puzzle_endpoint'],
$app['config']['friendlycaptcha.verify_endpoint'],
$app['config']['friendlycaptcha.options']
);
Expand Down
59 changes: 22 additions & 37 deletions src/Rules/FriendlyCaptcha.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,69 +2,54 @@

namespace Ossycodes\FriendlyCaptcha\Rules;

use Illuminate\Contracts\Validation\Rule;
use Closure;
use Illuminate\Contracts\Validation\ValidationRule;
use Ossycodes\FriendlyCaptcha\FriendlyCaptcha as FriendlyCaptchaClient;

class FriendlyCaptcha implements Rule
class FriendlyCaptcha implements ValidationRule
{
protected $friendlyCaptchaClient;

protected array $messages = [];

public function __construct(
FriendlyCaptchaClient $friendlyCaptcha
) {
$this->friendlyCaptchaClient = $friendlyCaptcha;
}

public function passes($attribute, $value)
public function validate(string $attribute, mixed $value, Closure $fail): void
{
$response = $this->friendlyCaptchaClient->verifyResponse($value);

if ($response->isSuccess()) {
return true;
return;
}

foreach ($response->getErrors() as $errorCode) {
$this->messages[] = $this->mapErrorCodeToMessage($errorCode);
$fail($this->mapErrorCodeToMessage($errorCode));
}

return false;
}

public function message()
{
return $this->messages;
}

/**
* map FriendlyCaptcha error code to human readable validation message
*
* @var string $code
*/
protected function mapErrorCodeToMessage(string $code): string
{
switch ($code) {
case "secret_missing":
return __('validation.secret_missing');
break;
case "secret_invalid":
return __('validation.secret_invalid');
break;
case "solution_missing":
return __('validation.solution_missing');
break;
case "auth_required":
return __('friendlycaptcha::validation.auth_required');
case "auth_invalid":
return __('friendlycaptcha::validation.auth_invalid');
case "sitekey_invalid":
return __('friendlycaptcha::validation.sitekey_invalid');
case "response_missing":
return __('friendlycaptcha::validation.response_missing');
case "response_invalid":
return __('friendlycaptcha::validation.response_invalid');
case "response_timeout":
return __('friendlycaptcha::validation.response_timeout');
case "response_duplicate":
return __('friendlycaptcha::validation.response_duplicate');
case "bad_request":
return __('validation.bad_request');
break;
case "solution_invalid":
return __('validation.solution_invalid');
break;
case "solution_timeout_or_duplicate":
return __('validation.solution_timeout_or_duplicate');
break;
return __('friendlycaptcha::validation.bad_request');
default:
return __('validation.unexpected');
return __('friendlycaptcha::validation.unexpected');
}
}
}
3 changes: 1 addition & 2 deletions src/config/friendlycaptcha.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
return [
'secret' => env('FRIENDLY_CAPTCHA_SECRET'),
'sitekey' => env('FRIENDLY_CAPTCHA_SITEKEY'),
'puzzle_endpoint' => env('FRIENDLY_CAPTCHA_PUZZLE_ENDPOINT', 'https://api.friendlycaptcha.com/api/v1/puzzle'),
'verify_endpoint' => env('FRIENDLY_CAPTCHA_VERIFY_ENDPOINT', 'https://api.friendlycaptcha.com/api/v1/siteverify'),
'verify_endpoint' => env('FRIENDLY_CAPTCHA_VERIFY_ENDPOINT', 'https://global.frcapi.com/api/v2/captcha/siteverify'), //see https://developer.friendlycaptcha.com/docs/v2/guides/upgrading-from-v1/backend-integration
'options' => [
'timeout' => 30,
'http_errors' => false,
Expand Down
Loading