Skip to content

Commit 92688b3

Browse files
Merge pull request #17 from arefebsh/support-custom-indicator
add support custom indicator
2 parents 148055f + 63dffa2 commit 92688b3

12 files changed

+245
-66
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ OTP()->useProvider('users')
7272
*/
7373
OTP()->onlyConfirmToken()
7474
->validate('+98900000000', 'token_123');
75+
76+
/*
77+
|--------------------------------------------------------------------------
78+
| You may wish to use a custom indicator
79+
|--------------------------------------------------------------------------
80+
*/
81+
OTP()->indicator('custom_indicator')
82+
->send('+98900000000');
83+
84+
OTP()->indicator('custom_indicator')
85+
->onlyConfirmToken()
86+
->validate('+98900000000', 'token_123');
7587
```
7688
## Installation
7789

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
class AddIndicatorToOTPTokensTable extends Migration
8+
{
9+
private string $tokenTable;
10+
11+
private string $defaultIndicator;
12+
13+
public function __construct()
14+
{
15+
$this->tokenTable = config('otp.token_table', 'otp_tokens');
16+
$this->defaultIndicator = config('otp.prefix', 'otp_tokens');
17+
}
18+
19+
public function up(): void
20+
{
21+
if (Schema::hasTable($this->tokenTable)) {
22+
Schema::table($this->tokenTable, function (Blueprint $table): void {
23+
$table->string('indicator')->default($this->defaultIndicator);
24+
});
25+
}
26+
}
27+
28+
public function down(): void
29+
{
30+
if (Schema::hasTable($this->tokenTable)) {
31+
Schema::table($this->tokenTable, static function (Blueprint $table): void {
32+
$table->dropColumn('indicator');
33+
});
34+
}
35+
}
36+
}

src/Contracts/AbstractTokenRepository.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ public function __construct(protected int $expires, protected int $tokenLength)
1212
{
1313
}
1414

15-
public function create(OTPNotifiable $user): string
15+
public function create(OTPNotifiable $user, string $indicator): string
1616
{
1717
$mobile = $user->getMobileForOTPNotification();
1818

19-
$this->deleteExisting($user);
19+
$this->deleteExisting($user, $indicator);
2020

2121
$token = $this->createNewToken();
2222

23-
$this->save($mobile, $token);
23+
$this->save($mobile, $indicator, $token);
2424

2525
return $token;
2626
}
@@ -35,13 +35,13 @@ protected function tokenExpired(string $expiresAt): bool
3535
return Carbon::parse($expiresAt)->isPast();
3636
}
3737

38-
protected function getPayload(string $mobile, string $token): array
38+
protected function getPayload(string $mobile, string $indicator, string $token): array
3939
{
40-
return ['mobile' => $mobile, 'token' => $token, 'sent_at' => now()->toDateTimeString()];
40+
return ['mobile' => $mobile, 'indicator' => $indicator, 'token' => $token, 'sent_at' => now()->toDateTimeString()];
4141
}
4242

4343
/**
4444
* Insert into token storage.
4545
*/
46-
abstract protected function save(string $mobile, string $token): bool;
46+
abstract protected function save(string $mobile, string $indicator, string $token): bool;
4747
}

src/Contracts/TokenRepositoryInterface.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@ interface TokenRepositoryInterface
77
/**
88
* Create a new token record.
99
*/
10-
public function create(OTPNotifiable $user): string;
10+
public function create(OTPNotifiable $user, string $indicator): string;
1111

1212
/**
1313
* Determine if a token record exists and is valid.
1414
*/
15-
public function exists(string $mobile): bool;
15+
public function exists(string $mobile, string $indicator): bool;
1616

1717
/**
1818
* Determine if the given token matches the provided one.
1919
*/
20-
public function isTokenMatching(OTPNotifiable $user, string $token): bool;
20+
public function isTokenMatching(OTPNotifiable $user, string $indicator, string $token): bool;
2121

2222
/**
2323
* Delete all existing tokens from the storage.
2424
*/
25-
public function deleteExisting(OTPNotifiable $user): bool;
25+
public function deleteExisting(OTPNotifiable $user, string $indicator): bool;
2626
}

src/OTPBroker.php

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class OTPBroker
1616
{
1717
private array $channel;
1818

19+
private string $indicator;
20+
1921
private ?string $token = null;
2022

2123
private bool $onlyConfirm = false;
@@ -27,6 +29,7 @@ public function __construct(
2729
private UserProviderResolver $providerResolver
2830
) {
2931
$this->channel = $this->getDefaultChannel();
32+
$this->indicator = $this->getDefaultIndicator();
3033
$this->userRepository = $this->resolveUserRepository();
3134
}
3235

@@ -42,7 +45,7 @@ public function send(string $mobile, bool $userExists = false): OTPNotifiable
4245

4346
$notifiable = $user ?? $this->makeNotifiable($mobile);
4447

45-
$this->token = $this->tokenRepository->create($notifiable);
48+
$this->token = $this->tokenRepository->create($notifiable, $this->indicator);
4649

4750
$notifiable->sendOTPNotification(
4851
$this->token,
@@ -77,6 +80,13 @@ public function onlyConfirmToken(bool $confirm = true): static
7780
return $this;
7881
}
7982

83+
public function indicator(string $indicator): static
84+
{
85+
$this->indicator = $indicator;
86+
87+
return $this;
88+
}
89+
8090
public function getToken(): ?string
8191
{
8292
return $this->token;
@@ -101,7 +111,7 @@ public function channel($channel = ['']): static
101111

102112
public function revoke(OTPNotifiable $user): bool
103113
{
104-
return $this->tokenRepository->deleteExisting($user);
114+
return $this->tokenRepository->deleteExisting($user, $this->indicator);
105115
}
106116

107117
/**
@@ -126,7 +136,7 @@ private function findOrCreateUser(string $mobile): OTPNotifiable
126136

127137
private function findUserByMobile(string $mobile): ?OTPNotifiable
128138
{
129-
return $this->userRepository->findByMobile($mobile);
139+
return $this->userRepository->findByMobile($mobile, $this->indicator);
130140
}
131141

132142
private function getDefaultChannel(): array
@@ -138,12 +148,12 @@ private function getDefaultChannel(): array
138148

139149
public function verifyToken(OTPNotifiable $user, string $token): bool
140150
{
141-
return $this->tokenRepository->isTokenMatching($user, $token);
151+
return $this->tokenRepository->isTokenMatching($user, $this->indicator, $token);
142152
}
143153

144154
private function tokenExists(string $mobile): bool
145155
{
146-
return $this->tokenRepository->exists($mobile);
156+
return $this->tokenRepository->exists($mobile, $this->indicator);
147157
}
148158

149159
private function makeNotifiable(string $mobile): OTPNotifiable
@@ -152,4 +162,9 @@ private function makeNotifiable(string $mobile): OTPNotifiable
152162

153163
return $this->userRepository->getModel()->make([$mobileColumn => $mobile]);
154164
}
165+
166+
private function getDefaultIndicator()
167+
{
168+
return config('otp.prefix');
169+
}
155170
}

src/Token/CacheTokenRepository.php

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,47 +7,53 @@
77
use Fouladgar\OTP\Contracts\AbstractTokenRepository;
88
use Fouladgar\OTP\Contracts\OTPNotifiable;
99
use Illuminate\Contracts\Cache\Repository as Cache;
10+
use Psr\SimpleCache\InvalidArgumentException;
1011

1112
class CacheTokenRepository extends AbstractTokenRepository
1213
{
1314
public function __construct(
1415
protected Cache $cache,
1516
protected int $expires,
1617
protected int $tokenLength,
17-
protected string $prefix
1818
) {
1919
parent::__construct($expires, $tokenLength);
2020
}
2121

22-
public function deleteExisting(OTPNotifiable $user): bool
22+
public function deleteExisting(OTPNotifiable $user, string $indicator): bool
2323
{
24-
return $this->cache->forget($this->getSignatureKey($user->getMobileForOTPNotification()));
24+
return $this->cache->forget($this->getSignatureKey($user->getMobileForOTPNotification(), $indicator));
2525
}
2626

27-
public function exists(string $mobile): bool
27+
/**
28+
* @throws InvalidArgumentException
29+
*/
30+
public function exists(string $mobile, string $indicator): bool
2831
{
29-
return $this->cache->has($this->getSignatureKey($mobile));
32+
return $this->cache->has($this->getSignatureKey($mobile, $indicator));
3033
}
3134

32-
public function isTokenMatching(OTPNotifiable $user, string $token): bool
35+
/**
36+
* @throws InvalidArgumentException
37+
*/
38+
public function isTokenMatching(OTPNotifiable $user, string $indicator, string $token): bool
3339
{
34-
$exist = $this->exists($user->getMobileForOTPNotification());
35-
$signature = $this->getSignatureKey($user->getMobileForOTPNotification());
40+
$exist = $this->exists($user->getMobileForOTPNotification(), $indicator);
41+
$signature = $this->getSignatureKey($user->getMobileForOTPNotification(), $indicator);
3642

3743
return $exist && $this->cache->get($signature)['token'] === $token;
3844
}
3945

40-
protected function save(string $mobile, string $token): bool
46+
protected function save(string $mobile, string $indicator, string $token): bool
4147
{
4248
return $this->cache->add(
43-
$this->getSignatureKey($mobile),
44-
$this->getPayload($mobile, $token),
49+
$this->getSignatureKey($mobile, $indicator),
50+
$this->getPayload($mobile, $indicator, $token),
4551
now()->addMinutes($this->expires)
4652
);
4753
}
4854

49-
protected function getSignatureKey($mobile): string
55+
protected function getSignatureKey($mobile, string $indicator): string
5056
{
51-
return $this->prefix.$mobile;
57+
return sprintf('%s%s', $indicator, $mobile);
5258
}
5359
}

src/Token/DatabaseTokenRepository.php

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,19 @@ class DatabaseTokenRepository extends AbstractTokenRepository
1313
{
1414
public function __construct(
1515
protected ConnectionInterface $connection,
16-
protected int $expires,
17-
protected int $tokenLength,
18-
protected string $table
16+
protected int $expires,
17+
protected int $tokenLength,
18+
protected string $table
1919
) {
2020
parent::__construct($expires, $tokenLength);
2121
}
2222

23-
public function deleteExisting(OTPNotifiable $user): bool
23+
public function deleteExisting(OTPNotifiable $user, string $indicator): bool
2424
{
25-
return (bool) optional($this->getTable()->where('mobile', $user->getMobileForOTPNotification()))->delete();
25+
return (bool)optional($this->getTable()->where([
26+
'mobile' => $user->getMobileForOTPNotification(),
27+
'indicator' => $indicator,
28+
]))->delete();
2629
}
2730

2831
protected function getLatestRecord(array $filters): ?array
@@ -32,21 +35,22 @@ protected function getLatestRecord(array $filters): ?array
3235
->latest()
3336
->first();
3437

35-
return $record ? (array) $record : null;
38+
return $record ? (array)$record : null;
3639
}
3740

38-
public function exists(string $mobile): bool
41+
public function exists(string $mobile, string $indicator): bool
3942
{
40-
$record = $this->getLatestRecord(['mobile' => $mobile]);
43+
$record = $this->getLatestRecord(['mobile' => $mobile, 'indicator' => $indicator]);
4144

4245
return $record && ! $this->tokenExpired($record['expires_at']);
4346
}
4447

45-
public function isTokenMatching(OTPNotifiable $user, string $token): bool
48+
public function isTokenMatching(OTPNotifiable $user, string $indicator, string $token): bool
4649
{
4750
$record = $this->getLatestRecord([
4851
'mobile' => $user->getMobileForOTPNotification(),
4952
'token' => $token,
53+
'indicator' => $indicator,
5054
]);
5155

5256
return $record && ! $this->tokenExpired($record['expires_at']);
@@ -57,13 +61,14 @@ protected function getTable(): Builder
5761
return $this->connection->table($this->table);
5862
}
5963

60-
protected function save(string $mobile, string $token): bool
64+
protected function save(string $mobile, string $indicator, string $token): bool
6165
{
62-
return $this->getTable()->insert($this->getPayload($mobile, $token));
66+
return $this->getTable()->insert($this->getPayload($mobile, $indicator, $token));
6367
}
6468

65-
protected function getPayload(string $mobile, string $token): array
69+
protected function getPayload(string $mobile, string $indicator, string $token): array
6670
{
67-
return parent::getPayload($mobile, $token) + ['expires_at' => now()->addMinutes($this->expires)];
71+
return parent::getPayload($mobile, $indicator, $token) +
72+
['expires_at' => now()->addMinutes($this->expires), 'indicator' => $indicator];
6873
}
6974
}

src/Token/TokenRepositoryManager.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ protected function createCacheDriver(): TokenRepositoryInterface
2020
$this->container->make(CacheRepository::class),
2121
$this->config->get('otp.token_lifetime', 5),
2222
$this->config->get('otp.token_length', 5),
23-
$this->config->get('otp.prefix'),
2423
);
2524
}
2625

0 commit comments

Comments
 (0)