Created
February 17, 2019 17:35
-
-
Save nathanbarrett/1454fc185fcc18c730ea1596b32e2227 to your computer and use it in GitHub Desktop.
Laravel rule for implementing Google's recaptcha V3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace App\Rules; | |
use Illuminate\Contracts\Validation\Rule; | |
use GuzzleHttp\Client; | |
use Illuminate\Support\Facades\Log; | |
use Bugsnag\BugsnagLaravel\Facades\Bugsnag; | |
class GoogleRecaptchaToken implements Rule | |
{ | |
/** | |
* The minimum score that will allow a token to pass | |
* @var float | |
*/ | |
protected $minScore = 0.7; | |
/** | |
* The client IP address | |
* @var string | |
*/ | |
protected $clientIp; | |
/** | |
* Create a new rule instance. | |
* @param string $clientIp | |
* @param float $minScore | |
* @return void | |
*/ | |
public function __construct($clientIp, $minScore = null) | |
{ | |
$this->clientIp = $clientIp; | |
if(is_float($minScore)) { | |
$this->minScore = $minScore; | |
} | |
} | |
/** | |
* Determine if the validation rule passes. | |
* | |
* @param string $attribute | |
* @param mixed $value | |
* @return bool | |
*/ | |
public function passes($attribute, $value) | |
{ | |
if(app()->environment() === 'local') | |
{ | |
return true; | |
} | |
try { | |
$score = $this->getRecaptchaScore($value); | |
} catch (\Exception $exception) { | |
Bugsnag::notifyException($exception); | |
return false; | |
} | |
return $score >= $this->minScore; | |
} | |
/** | |
* Get the validation error message. | |
* | |
* @return string | |
*/ | |
public function message() | |
{ | |
return 'Not a valid request.'; | |
} | |
/** | |
* Creates the multipart form data for the request | |
* @param string $token | |
* @return array | |
*/ | |
protected function createFormData($token) | |
{ | |
$formData = [ | |
[ | |
'name' => 'secret', | |
'contents' => config('services.recaptcha.secret_key'), | |
], | |
[ | |
'name' => 'response', | |
'contents' => $token | |
] | |
]; | |
if($this->clientIp) | |
{ | |
$formData[] = [ | |
'name' => 'remoteip', | |
'contents' => $this->clientIp, | |
]; | |
} | |
return $formData; | |
} | |
/** | |
* Creates and executes the request to google's recaptcha service | |
* and returns the recaptcha score | |
* | |
* @throws \Exception | |
* @param string $token | |
* @return float | |
*/ | |
protected function getRecaptchaScore($token) | |
{ | |
$formData = $this->createFormData($token); | |
Bugsnag::leaveBreadcrumb('attempting recaptcha', 'recaptcha', [ | |
'formData' => $formData, | |
'request' => request()->all() | |
]); | |
$client = new Client(); | |
$response = $client->post('https://www.google.com/recaptcha/api/siteverify', [ | |
'multipart' => $formData, | |
'timeout' => 4 | |
]); | |
$reply = json_decode($response->getBody()->getContents(), true); | |
Log::info('Reply from recaptcha', [ | |
'response' => $reply, | |
'formData' => $formData, | |
'request' => request()->all(), | |
]); | |
if( ! array_get($reply, 'success')) | |
{ | |
throw new \Exception('Non success response from recaptcha', 400); | |
} | |
return array_get($reply, 'score', 0); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment