Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.
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
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ use App\Helpers\SmsVerifyMethod;

$result = Verify::request($request->input('mobile'), SmsVerifyMethod::class);

if ($result['success'] == false) { // If user exceed limitation
return redirect()->back()->with('error', $result['message']); // Show error message
if ($result->success == false) { // If user exceed limitation
return redirect()->back()->with('error', $result->message); // Show error message
}
```

Expand Down Expand Up @@ -92,8 +92,8 @@ You can also verify it manually.
use Verify;

$result = Verify::verify($request->input('mobile'), $request->input('code'));
if ($result['success'] == false) {
// Show error $result['message']
if ($result->success == false) {
// Show error $result->message
}
```
> Note: You can verify a code just once. so if you need to check code in two different requests then you should use something like the session to handle that.
Expand Down
17 changes: 17 additions & 0 deletions src/VerificationResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace SanjabVerify;

class VerificationResponse
{
public $success;
public $message;
public $seconds;

public function __construct(bool $success, string $message, int $seconds = 0)
{
$this->success = $success;
$this->message = $message;
$this->seconds = $seconds;
}
}
126 changes: 77 additions & 49 deletions src/Verify.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Exception;
use Illuminate\Support\Facades\Session;
use InvalidArgumentException;
use SanjabVerify\Contracts\VerifyMethod;
use SanjabVerify\Models\VerifyLog;

Expand All @@ -14,37 +15,19 @@ class Verify
*
* @property string $receiver receiver of code
* @property string $method method of sending code
* @return array
* @return VerificationResponse
* @example ['success' => true, 'message' => '...']
*/
public function request(string $receiver, string $method = null)
{
VerifyLog::where('created_at', '<', now()->subDay())->delete();
$lastestLog = VerifyLog::where('ip', request()->ip())->orWhere('receiver', $receiver)->latest()->first();
if ($lastestLog) {
if ($lastestLog->created_at->gt(now()->subSeconds(config('verify.resend_delay')))) {
return [
'success' => false,
'message' => trans('verify::verify.resend_wait', [
'seconds' => config('verify.resend_delay') - $lastestLog->created_at->diffInSeconds()
]),
'seconds' => config('verify.resend_delay') - $lastestLog->created_at->diffInSeconds()
];
}
if (VerifyLog::where('created_at', '>', now()->subHour())
->where(function ($query) use ($receiver) {
$query->where('ip', request()->ip())->orWhere('receiver', $receiver);
})->count() > config('verify.max_resends.per_ip') ||
(is_array(session('sanjab_verify')) && count(array_filter(session('sanjab_verify'), function ($time) {
return $time > time() - 3600;
})) > config('verify.max_resends.per_session'))
) {
return ['success' => false, 'message' => trans('verify::verify.too_many_requests')];
}

if ($verificationValidation = $this->validateVerificationRequests($receiver)) {
return $verificationValidation;
}
$verifyMethod = new $method;
if (!($verifyMethod instanceof VerifyMethod)) {
throw new Exception('Verify method is not instance of SanjabVerify\Contracts\VerifyMethod.');
$verifyMethod = new $method();
if ( ! ($verifyMethod instanceof VerifyMethod)) {
throw new InvalidArgumentException('Verify method is not instance of SanjabVerify\Contracts\VerifyMethod.');
}
$code = $this->generate();
if ($verifyMethod->send($receiver, $code)) {
Expand All @@ -56,54 +39,58 @@ public function request(string $receiver, string $method = null)
'method' => $method,
]);
Session::push('sanjab_verify', time());
return ['success' => true, 'message' => trans('verify::verify.sent_successfully')];
return new VerificationResponse(true, trans('verify::verify.sent_successfully'));

}
return ['success' => false, 'message' => trans('verify::verify.send_failed')];
return new VerificationResponse(false, trans('verify::verify.send_failed'));
}

/**
* Request a new code.
*
* @property string $receiver receiver of code
* @property string $code code input value
* @return array
* @return VerificationResponse
* @example ['success' => true, 'message' => '...']
*/
public function verify(string $receiver, string $code)
{
$log = VerifyLog::where('receiver', $receiver)->latest()->first();
if ($log == null || $log->created_at->diffInMinutes() > config('verify.expire_in')) {
return [
'success' => false,
'message' => trans('verify::verify.code_expired'),
];
if (null === $log || $log->created_at->diffInMinutes() > config('verify.expire_in')) {
return new VerificationResponse(
false,
trans('verify::verify.code_expired'),
);
}
if ($log->count > config('verify.max_attemps')) {
return [
'success' => false,
'message' => trans('verify::verify.code_attempt_limited', ['count' => config('verify.max_attemps')]),
];
return new VerificationResponse(
false,
trans('verify::verify.code_attempt_limited', ['count' => config('verify.max_attemps')]),
);

}
if ($log->ip != request()->ip() || $log->agent != request()->userAgent()) {
return [
'success' => false,
'message' => trans('verify::verify.code_is_not_yours'),
];
if ($log->ip !== request()->ip() || $log->agent !== request()->userAgent()) {
return new VerificationResponse(
false,
trans('verify::verify.code_is_not_yours'),
);
}

$log->increment('count');

if ($log->code != $code && (config('verify.code.case_sensitive') == false && strtolower($log->code) != strtolower($code))) {
return [
'success' => false,
'message' => trans('verify::verify.code_is_wrong'),
];
if ($log->code !== $code && ( ! config('verify.code.case_sensitive') && mb_strtolower($log->code) !== mb_strtolower($code))) {
return new VerificationResponse(
false,
trans('verify::verify.code_is_wrong'),
);
}

$log->update(['count' => 2147483647]);
$log->save();

return ['success' => true, 'message' => trans('verify::verify.verified_successfully')];
return new VerificationResponse(
true,
trans('verify::verify.verified_successfully'),
);
}

/**
Expand Down Expand Up @@ -133,4 +120,45 @@ public function generate()
}
return str_shuffle($string);
}

private function validateVerificationRequests($receiver)
{

$latestLog = VerifyLog::where('ip', request()->ip())
->orWhere('receiver', $receiver)
->latest()
->first();

if ( ! $latestLog) {
return null;
}

if ($latestLog->created_at->gt(now()->subSeconds(config('verify.resend_delay')))) {
$waitTime = config('verify.resend_delay') - $latestLog->created_at->diffInSeconds();
return new VerificationResponse(
false,
trans('verify::verify.resend_wait', ['seconds' => $waitTime]),
$waitTime
);
}

$numberOfRequests = VerifyLog::where('created_at', '>', now()->subHour())
->where(function ($query) use ($receiver) {
$query->where('ip', request()->ip())->orWhere('receiver', $receiver);
})
->count();
if ($numberOfRequests > config('verify.max_resends.per_ip')) {
return new VerificationResponse(false, trans('verify::verify.too_many_requests'));
}

$sanjabVerifySession = session('sanjab_verify') ?: [];
$recentRequests = array_filter($sanjabVerifySession, function ($time) {
return $time > time() - 3600;
});
if (count($recentRequests) > config('verify.max_resends.per_session')) {
return new VerificationResponse(false, trans('verify::verify.too_many_requests'));
}

return null;
}
}
18 changes: 9 additions & 9 deletions src/VerifyServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,24 +13,24 @@ class VerifyServiceProvider extends ServiceProvider
*/
public function boot()
{
$this->loadTranslationsFrom(__DIR__.'/../resources/lang', 'verify');
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
$this->loadTranslationsFrom(__DIR__ . '/../resources/lang', 'verify');
$this->loadMigrationsFrom(__DIR__ . '/../database/migrations');

$this->publishes([
__DIR__.'/../config/config.php' => config_path('verify.php'),
__DIR__ . '/../config/config.php' => config_path('verify.php'),
], 'config');

$this->publishes([
__DIR__.'/../resources/lang' => resource_path('lang/vendor/verify'),
__DIR__ . '/../resources/lang' => resource_path('lang/vendor/verify'),
], 'lang');

Validator::extend('sanjab_verify', function ($attribute, $value, $parameters = [], $validator = null) {
$success = false;
$message = '';
if (isset($validator->getData()[$parameters[0] ?? 'receiver']) && !empty($validator->getData()[$parameters[0]])) {
$result = app(Verify::class)->verify($validator->getData()[$parameters[0] ?? 'receiver'], $value);
$message = $result['message'];
$success = $result['success'];
if (isset($validator->getData()[$parameters[0] ?? 'receiver']) && ! empty($validator->getData()[$parameters[0]])) {
$result = app(Verify::class)->verify($validator->getData()[$parameters[0] ?? 'receiver'], $value);
$message = $result->message;
$success = $result->success;
}
App::singleton('sanjab_verify_validation_message', function () use ($message) {
return $message;
Expand All @@ -47,7 +47,7 @@ public function boot()
*/
public function register()
{
$this->mergeConfigFrom(__DIR__.'/../config/config.php', 'verify');
$this->mergeConfigFrom(__DIR__ . '/../config/config.php', 'verify');

$this->app->singleton('verify', function () {
return new Verify;
Expand Down
Loading