Skip to content

Commit 8710a83

Browse files
author
Your Name
committed
add support otp lifetime token functionality with expiration handling
1 parent c2c1744 commit 8710a83

15 files changed

+193
-112
lines changed

.github/workflows/php-cs-fixer.yml

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,19 @@
11
name: Check & fix styling
22

3-
on: [push, pull_request]
3+
on: [push,pull_request]
44

55
jobs:
66
php-cs-fixer:
7-
runs-on: ubuntu-latest
7+
name: PHP-CS-Fixer
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v2
11+
- name: PHP-CS-Fixer
12+
uses: docker://oskarstark/php-cs-fixer-ga
13+
with:
14+
args: --config=.php-cs-fixer.dist --allow-risky=yes
815

9-
steps:
10-
- name: Checkout code
11-
uses: actions/checkout@v4
12-
with:
13-
ref: ${{ github.head_ref }}
14-
15-
- name: Run PHP CS Fixer
16-
uses: docker://oskarstark/php-cs-fixer-ga
17-
with:
18-
args: --config=.php-cs-fixer.dist --allow-risky=yes
19-
20-
- name: Commit changes
21-
uses: stefanzweifel/git-auto-commit-action@v5
22-
with:
23-
commit_message: Fix styling
16+
- name: Commit changes
17+
uses: stefanzweifel/git-auto-commit-action@v4
18+
with:
19+
commit_message: Fix styling

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 4.3.0 - 2024-06-21
2+
- Add support only confirm token
3+
14
## 4.2.0 - 2024-04-12
25
- Add support Laravel 11
36
- Detracted Laravel 9

README.md

Lines changed: 74 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,46 +19,60 @@ Laravel | Laravel-OTP
1919
6.0.x to 8.0.x | 1.0.x
2020

2121
## Basic Usage:
22-
2322
```php
2423
<?php
2524

26-
/**
27-
* Send OTP via SMS.
28-
*/
29-
OTP()->send('+989389599530');
30-
// or
31-
OTP('+989389599530');
32-
33-
/**
34-
* Send OTP via channels.
35-
*/
25+
/*
26+
|--------------------------------------------------------------------------
27+
| Send OTP via SMS.
28+
|--------------------------------------------------------------------------
29+
*/
30+
OTP()->send('+98900000000');
31+
// Or
32+
OTP('+98900000000');
33+
34+
/*
35+
|--------------------------------------------------------------------------
36+
| Send OTP via channels.
37+
|--------------------------------------------------------------------------
38+
*/
3639
OTP()->channel(['otp_sms', 'mail', \App\Channels\CustomSMSChannel::class])
37-
->send('+989389599530');
38-
// or
39-
OTP('+989389599530', ['otp_sms', 'mail', \App\Channels\CustomSMSChannel::class]);
40-
41-
/**
42-
* Send OTP for specific user provider
43-
*/
40+
->send('+98900000000');
41+
// Or
42+
OTP('+98900000000', ['otp_sms', 'mail', \App\Channels\CustomSMSChannel::class]);
43+
44+
/*
45+
|--------------------------------------------------------------------------
46+
| Send OTP for specific user provider
47+
|--------------------------------------------------------------------------
48+
*/
4449
OTP()->useProvider('admins')
45-
->send('+989389599530');
46-
47-
/**
48-
* Validate OTP
49-
*/
50-
OTP()->validate('+989389599530', 'token_123');
51-
// or
52-
OTP('+989389599530', 'token_123');
53-
// or
50+
->send('+98900000000');
51+
52+
/*
53+
|--------------------------------------------------------------------------
54+
| Validate OTP
55+
|--------------------------------------------------------------------------
56+
*/
57+
OTP()->validate('+98900000000', 'token_123');
58+
// Or
59+
OTP('+98900000000', 'token_123');
60+
61+
/*
62+
|--------------------------------------------------------------------------
63+
| Validate OTP for specific user provider
64+
|--------------------------------------------------------------------------
65+
*/
5466
OTP()->useProvider('users')
55-
->validate('+989389599530', 'token_123');
56-
// or
57-
OTP()->useProvider('users')
58-
->onlyConfirmToken()
59-
->validate('+989389599530', 'token_123');
67+
->validate('+98900000000', 'token_123');
68+
/*
69+
|--------------------------------------------------------------------------
70+
| You may wish to only confirm the token
71+
|--------------------------------------------------------------------------
72+
*/
73+
OTP()->onlyConfirmToken()
74+
->validate('+98900000000', 'token_123');
6075
```
61-
6276
## Installation
6377

6478
You can install the package via composer:
@@ -132,7 +146,24 @@ php artisan migrate
132146

133147
> **Note:** When you are using OTP to login user, consider all columns must be nullable except for the `mobile` column. Because, after verifying OTP, a user record will be created if the user does not exist.
134148
135-
## User providers
149+
### Token Life Time
150+
You can specify an OTP `token_lifetime`, ensuring that once an OTP token is sent to the user, no new OTP token will be generated or sent until the current token has expired.
151+
152+
```php
153+
// config/otp.php
154+
155+
<?php
156+
157+
return [
158+
//...
159+
160+
'token_lifetime' => env('OTP_TOKEN_LIFE_TIME', 5),
161+
],
162+
163+
//...
164+
];
165+
```
166+
### User providers
136167

137168
You may wish to use the OTP for variant users. Laravel OTP allows you to define and manage many user providers that you
138169
need. In order to set up, you should open `config/otp.php` file and define your providers:
@@ -242,15 +273,15 @@ return [
242273

243274
## Practical Example
244275

245-
Here we have prepared a practical example. Suppose you are going to login/register a customer by sending an OTP:
276+
Here we have prepared a practical example. Suppose you are going to login/register a user by sending an OTP:
246277

247278
```php
248279
<?php
249280

250281
namespace App\Http\Controllers;
251282

252283
use App\Models\User;
253-
use Fouladgar\OTP\Exceptions\InvalidOTPTokenException;
284+
use Fouladgar\OTP\Exceptions\OTPException;
254285
use Fouladgar\OTP\OTPBroker as OTPService;
255286
use Illuminate\Http\JsonResponse;
256287
use Illuminate\Http\Request;
@@ -269,10 +300,10 @@ class AuthController
269300
$user = $this->OTPService->send($request->get('mobile'));
270301
} catch (Throwable $ex) {
271302
// or prepare and return a view.
272-
return response()->json(['message'=>'An unexpected error occurred.'], 500);
303+
return response()->json(['message' => 'An unexpected error occurred.'], 500);
273304
}
274305

275-
return response()->json(['message'=>'A token has been sent to:'. $user->mobile]);
306+
return response()->json(['message' => 'A token has been sent to:'. $user->mobile]);
276307
}
277308

278309
public function verifyOTPAndLogin(Request $request): JsonResponse
@@ -283,13 +314,13 @@ class AuthController
283314

284315
// and do login actions...
285316

286-
} catch (InvalidOTPTokenException $exception){
287-
return response()->json(['error'=>$exception->getMessage()],$exception->getCode());
317+
} catch (OTPException $exception){
318+
return response()->json(['error' => $exception->getMessage()],$exception->getCode());
288319
} catch (Throwable $ex) {
289-
return response()->json(['message'=>'An unexpected error occurred.'], 500);
320+
return response()->json(['message' => 'An unexpected error occurred.'], 500);
290321
}
291322

292-
return response()->json(['message'=>'Logged in successfully.']);
323+
return response()->json(['message' => 'Logged in successfully.']);
293324
}
294325
}
295326

@@ -305,6 +336,7 @@ channel. In order to replace, you should specify channel class here:
305336
```php
306337
//config/otp.php
307338
<?php
339+
308340
return [
309341
// ...
310342

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
4.2.0
1+
4.3.0

src/Contracts/TokenRepositoryInterface.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ public function create(OTPNotifiable $user): string;
1212
/**
1313
* Determine if a token record exists and is valid.
1414
*/
15-
public function exists(OTPNotifiable $user, string $token): bool;
15+
public function exists(string $mobile): bool;
16+
17+
/**
18+
* Determine if the given token matches the provided one.
19+
*/
20+
public function isTokenMatching(OTPNotifiable $user, string $token): bool;
1621

1722
/**
1823
* Delete all existing tokens from the storage.

src/Exceptions/InvalidOTPTokenException.php

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/Exceptions/OTPException.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
namespace Fouladgar\OTP\Exceptions;
4+
5+
use Exception;
6+
7+
class OTPException extends Exception
8+
{
9+
public static function whenOtpTokenIsInvalid(): static
10+
{
11+
return new static('The token has been expired or invalid.');
12+
}
13+
14+
public static function whenUserNotFoundByMobile(): static
15+
{
16+
return new static('User not found by mobile.');
17+
}
18+
19+
public static function whenOtpAlreadySent(): static
20+
{
21+
return new static('OTP has already been sent for this mobile.');
22+
}
23+
}

src/Exceptions/UserNotFoundByMobileException.php

Lines changed: 0 additions & 13 deletions
This file was deleted.

src/OTPBroker.php

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
use Fouladgar\OTP\Contracts\NotifiableRepositoryInterface;
99
use Fouladgar\OTP\Contracts\OTPNotifiable;
1010
use Fouladgar\OTP\Contracts\TokenRepositoryInterface;
11-
use Fouladgar\OTP\Exceptions\InvalidOTPTokenException;
12-
use Fouladgar\OTP\Exceptions\UserNotFoundByMobileException;
11+
use Fouladgar\OTP\Exceptions\OTPException;
1312
use Illuminate\Support\Arr;
1413
use Throwable;
1514

@@ -38,7 +37,8 @@ public function send(string $mobile, bool $userExists = false): OTPNotifiable
3837
{
3938
$user = $userExists ? $this->findUserByMobile($mobile) : null;
4039

41-
throw_if(! $user && $userExists, UserNotFoundByMobileException::class);
40+
throw_if(! $user && $userExists, OTPException::whenUserNotFoundByMobile());
41+
throw_if($this->tokenExists($mobile), OTPException::whenOtpAlreadySent());
4242

4343
$notifiable = $user ?? $this->makeNotifiable($mobile);
4444

@@ -53,13 +53,13 @@ public function send(string $mobile, bool $userExists = false): OTPNotifiable
5353
}
5454

5555
/**
56-
* @throws InvalidOTPTokenException|Throwable
56+
* @throws OTPException|Throwable
5757
*/
5858
public function validate(string $mobile, string $token, bool $create = true): OTPNotifiable
5959
{
6060
$notifiable = $this->makeNotifiable($mobile);
6161

62-
throw_unless($this->tokenExists($notifiable, $token), InvalidOTPTokenException::class);
62+
throw_unless($this->verifyToken($notifiable, $token), OTPException::whenOtpTokenIsInvalid());
6363

6464
if(!$this->onlyConfirm){
6565
$notifiable = $this->find($mobile, $create);
@@ -136,9 +136,14 @@ private function getDefaultChannel(): array
136136
return is_array($channel) ? $channel : Arr::wrap($channel);
137137
}
138138

139-
private function tokenExists(OTPNotifiable $user, string $token): bool
139+
public function verifyToken(OTPNotifiable $user, string $token): bool
140140
{
141-
return $this->tokenRepository->exists($user, $token);
141+
return $this->tokenRepository->isTokenMatching($user, $token);
142+
}
143+
144+
private function tokenExists(string $mobile): bool
145+
{
146+
return $this->tokenRepository->exists($mobile);
142147
}
143148

144149
private function makeNotifiable(string $mobile): OTPNotifiable

src/Token/CacheTokenRepository.php

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,17 @@ public function deleteExisting(OTPNotifiable $user): bool
2424
return $this->cache->forget($this->getSignatureKey($user->getMobileForOTPNotification()));
2525
}
2626

27-
public function exists(OTPNotifiable $user, string $token): bool
27+
public function exists(string $mobile): bool
2828
{
29+
return $this->cache->has($this->getSignatureKey($mobile));
30+
}
31+
32+
public function isTokenMatching(OTPNotifiable $user, string $token): bool
33+
{
34+
$exist = $this->exists($user->getMobileForOTPNotification());
2935
$signature = $this->getSignatureKey($user->getMobileForOTPNotification());
3036

31-
return $this->cache->has($signature) &&
32-
$this->cache->get($signature)['token'] === $token;
37+
return $exist && $this->cache->get($signature)['token'] === $token;
3338
}
3439

3540
protected function save(string $mobile, string $token): bool

0 commit comments

Comments
 (0)