├── .gitignore
├── LICENSE
├── README.md
├── composer.json
└── src
├── Casts
└── PhoneCast.php
├── Exceptions
├── PhoneAuthLimitException.php
└── PhoneAuthSmsServiceNotFoundException.php
├── Livewire
└── PhoneVerification.php
├── Models
├── ConfirmedPhone.php
├── PhoneVerificationCode.php
└── Traits
│ └── PhoneVerification.php
├── Providers
└── PhoneAuthServiceProvider.php
├── Rules
└── PhoneNumber.php
├── SmsBox.php
├── SmsServiceExample.php
├── SmsServiceInterface.php
├── config
└── phone_auth.php
├── database
└── migrations
│ └── 2021_02_18_090500_users_phone_auth.php
├── lang
├── en
│ └── phone_auth.php
└── ru
│ └── phone_auth.php
└── views
└── livewire
└── phone-verification.blade.php
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | /vendor/
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Danil Shutsky
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # laravel-phone-auth
2 |
3 | https://youtu.be/UaYx2VgXMEY
4 |
5 | ## Important
6 | - Need laravel livewire package
7 | - The default template uses tailwind classes (customize it if you want)
8 |
9 | ## Install
10 |
11 | - install livewire
12 |
13 | - install doctrine/dbal
14 |
15 | - composer require lee-to/laravel-phone-auth
16 |
17 | - php artisan vendor:publish --provider="Leeto\PhoneAuth\Providers\PhoneAuthServiceProvider"
18 |
19 | - configure config/phone_auth.php
20 |
21 | ### Usage
22 |
23 | ### User Model
24 |
25 | - Add PhoneVerification Trait to User Model
26 |
27 | ``` php
28 | use PhoneVerification;
29 | ```
30 |
31 | - Add phone cast to User Model
32 |
33 | ``` php
34 | protected $casts = [
35 | 'phone' => PhoneCast::class
36 | ];
37 | ```
38 |
39 | ### Blade component
40 | #### Auth/Phone verification form
41 |
42 | - Simple
43 | ``` html
44 | @livewire('phone-verification')
45 | ```
46 |
47 | - Without form wrap
48 |
49 | ``` html
50 | @livewire('phone-verification', ['formWrap' => false])
51 | ```
52 |
53 | - Register new or login if phone verified and exist
54 |
55 | ``` html
56 | @livewire('phone-verification', ['loginAndRegister' => true])
57 | ```
58 |
59 | ### Check phone confirmed
60 |
61 | \Leeto\PhoneAuth\Models\ConfirmedPhone::confirmed($phone, $user_id = null);
62 |
63 |
64 | #### Components properties (override config)
65 | - stopEvents (bool) = turn off emitBefore, emitAfter
66 | - customRedirectTo (bool|array) = redirect after success
67 | - emptyCustomFields (bool) = disable custom fields
68 | - customParams (array) = send custom properties to view
69 |
70 | ``` html
71 |
79 | ```
80 |
81 |
82 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lee-to/laravel-phone-auth",
3 | "description": "Laravel+livewire phone auth module",
4 | "keywords": ["laravel", "livewire", "phone auth", "phone register", "phone login"],
5 | "type": "library",
6 | "homepage": "https://github.com/lee-to/laravel-phone-auth",
7 | "require": {
8 | "php": "^7.3|^8.0",
9 | "doctrine/dbal": "^3.0"
10 | },
11 | "require-dev": {
12 | "phpunit/phpunit": "8.5.x-dev",
13 | "mockery/mockery": "^1.0"
14 | },
15 | "license": "MIT",
16 | "authors": [
17 | {
18 | "name": "Danil Shutsky",
19 | "email": "leetodev@ya.ru",
20 | "homepage": "https://github.com/lee-to"
21 | }
22 | ],
23 | "minimum-stability": "dev",
24 | "autoload": {
25 | "psr-4": {
26 | "Leeto\\PhoneAuth\\": "src/"
27 | }
28 | },
29 | "autoload-dev": {
30 | "psr-4": {
31 | "Leeto\\PhoneAuth\\Tests\\": "tests/"
32 | }
33 | },
34 | "extra": {
35 | "laravel": {
36 | "providers": [
37 | "Leeto\\PhoneAuth\\Providers\\PhoneAuthServiceProvider"
38 | ]
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/Casts/PhoneCast.php:
--------------------------------------------------------------------------------
1 | 'verification',
115 | 'phoneVerificationDefaultParams' => 'defaultParams'
116 | ];
117 |
118 | /**
119 | * @return array
120 | */
121 | protected function rules()
122 | {
123 | $rules = [
124 | 'phone' => ['required', new PhoneNumber()],
125 | 'code' => ['nullable', 'sometimes']
126 | ];
127 |
128 | $customFieldsRules = $this->customFieldsRules ? collect($this->customFieldsRules) : collect(config("phone_auth.custom_fields_rules"));
129 |
130 | if($this->emptyCustomFields) {
131 | $customFieldsRules = collect([]);
132 | }
133 |
134 | $newRules = $customFieldsRules->mapWithKeys(function ($value, $key) {
135 | return ["fieldsValues.{$key}" => $value];
136 | })->merge($rules);
137 |
138 |
139 |
140 | return $newRules->toArray();
141 | }
142 |
143 | /**
144 | *
145 | */
146 | public function mount() {
147 | $customFields = $this->customFields ? $this->customFields : config("phone_auth.custom_fields", []);
148 |
149 | if($this->emptyCustomFields) {
150 | $customFields = [];
151 | }
152 |
153 | $this->fields = $this->fields ? $this->fields : $customFields;
154 |
155 | $this->sendCountRemain();
156 | }
157 |
158 | public function defaultParams($params) {
159 | $this->fieldsValues = collect($this->fieldsValues)->merge($params)->toArray();
160 | }
161 |
162 | /**
163 | *
164 | */
165 | public function nextSend() {
166 | $this->nextSend = PhoneVerificationCode::nextSend();
167 | }
168 |
169 | /**
170 | *
171 | */
172 | public function sendCountRemain() {
173 | $this->sendCountRemain = PhoneVerificationCode::sendCountRemain();
174 | }
175 |
176 | /**
177 | * @param false $resend
178 | */
179 | public function send($resend = false) {
180 | try {
181 | $this->phoneVerificationCode = PhoneVerificationCode::sendCode($this->phone);
182 |
183 | $this->nextSend();
184 | $this->sendCountRemain();
185 |
186 | $this->resetErrorBag();
187 | $this->resetValidation();
188 |
189 | $this->eventsSendCode();
190 | } catch (PhoneAuthLimitException $e) {
191 | $this->addError($resend ? "code" : "phone", $e->getMessage());
192 | }
193 | }
194 |
195 | /**
196 | *
197 | */
198 | public function change() {
199 | $this->reset(["phone", "code", "successfullyConfirmed", "phoneVerificationCode"]);
200 | }
201 |
202 | /**
203 | * @return \Illuminate\Contracts\Auth\Factory|\Illuminate\Contracts\Auth\Guard|\Illuminate\Contracts\Auth\StatefulGuard
204 | */
205 | protected function getAuth() {
206 | return auth(config("phone_auth.auth.guard"));
207 | }
208 |
209 | /**
210 | * @param $value
211 | */
212 | public function updatingCode($value) {
213 | $value = Str::of($value)->replace(" ", "");
214 |
215 | if(config("phone_auth.verify_code_dynamic") && Str::length($value) == config("phone_auth.code_length")) {
216 | $this->code = $value;
217 |
218 | $this->dispatchBrowserEvent('code-loading');
219 |
220 | $this->verification();
221 | } else {
222 | $this->resetErrorBag(["code"]);
223 | $this->resetValidation(["code"]);
224 | }
225 | }
226 |
227 | /**
228 | * @param $value
229 | */
230 | public function updatingPhone($value) {
231 | if($this->successfullyConfirmed && $this->confirmedPhone != $value) {
232 | $this->reset(["code", "confirmedPhone", "successfullyConfirmed", "phoneVerificationCode"]);
233 | }
234 | }
235 |
236 | /**
237 | *
238 | */
239 | public function verification() {
240 | if($this->successfullyConfirmed) {
241 | $this->eventsAfter();
242 | } else {
243 | $this->dispatchBrowserEvent('phone-verification');
244 |
245 | $this->eventsBefore();
246 |
247 | $this->validate();
248 |
249 | if($this->loginAndRegister == false && !User::isUniquePhone($this->phone)) {
250 | $this->addError("phone", __("phone_auth::phone_auth.validation.phone_already_use"));
251 |
252 | if($this->phoneVerificationCode) {
253 | $this->change();
254 | }
255 | } else {
256 | if($this->phoneVerificationCode) {
257 | if(PhoneVerificationCode::validateCode($this->code)) {
258 | try {
259 | if(config("phone_auth.auth.createUser")) {
260 | $userModel = app(config("phone_auth.auth.class_user"));
261 |
262 | $fields = collect($this->fields)->mapWithKeys(function ($item) {
263 | return [$item => $this->fieldsValues[$item] ?? ''];
264 | });
265 |
266 | $formatPhoneNumber = Str::phoneNumber($this->phone);
267 |
268 | $createData = Arr::add($fields->toArray(), "phone", $formatPhoneNumber);
269 |
270 | $user = $this->getAuth()->check() ? $this->getAuth()->user() : $userModel->firstOrCreate(["phone" => $formatPhoneNumber], $createData);
271 | $user->setVerifiedPhone($this->phone);
272 |
273 | if(config("phone_auth.auth.loginAfter") && !$this->getAuth()->check()) {
274 | $this->getAuth()->login($user);
275 | }
276 |
277 | ConfirmedPhone::firstOrCreate(["phone" => $this->phone]);
278 | }
279 |
280 | $this->phone = $this->phoneVerificationCode->phone;
281 | $this->successfullyConfirmed = true;
282 | $this->confirmedPhone = $this->phone;
283 |
284 | $this->eventsAfter();
285 |
286 | $this->reset(["code", "sendCountRemain", "phoneVerificationCode", "nextSend"]);
287 |
288 | if(config("phone_auth.flushCode")) {
289 | PhoneVerificationCode::flush();
290 | }
291 |
292 | $this->dispatchBrowserEvent('phone-verification-done');
293 |
294 | $redirectTo = $this->customRedirectTo ? $this->customRedirectTo : config("phone_auth.auth.redirectTo");
295 |
296 | if($redirectTo) {
297 | $this->redirect($redirectTo);
298 | }
299 | } catch (\Exception $e) {
300 | $this->addError("code", __("phone_auth::phone_auth.validation.error"));
301 | }
302 | } else {
303 | $this->addError("code", __("phone_auth::phone_auth.validation.code_invalid"));
304 | }
305 | } else {
306 | $this->send();
307 | }
308 | }
309 | }
310 | }
311 |
312 | /**
313 | *
314 | */
315 | protected function eventsSendCode() {
316 | if(!$this->stopEvents && config("phone_auth.emitSendCode")) {
317 | $this->emit(config("phone_auth.emitSendCode"), [
318 | "phone" => Str::phoneNumber($this->phone),
319 | "values" => $this->fieldsValues,
320 | ]);
321 | }
322 | }
323 |
324 | /**
325 | *
326 | */
327 | protected function eventsBefore() {
328 | if(!$this->stopEvents && config("phone_auth.emitBefore")) {
329 | $this->emit(config("phone_auth.emitBefore"), [
330 | "success" => $this->successfullyConfirmed,
331 | "phone" => Str::phoneNumber($this->phone),
332 | "values" => $this->fieldsValues,
333 | ]);
334 | }
335 | }
336 |
337 | /**
338 | *
339 | */
340 | protected function eventsAfter() {
341 | if(!$this->stopEvents && config("phone_auth.emitAfter")) {
342 | $this->emit(config("phone_auth.emitAfter"), [
343 | "success" => $this->successfullyConfirmed,
344 | "phone" => Str::phoneNumber($this->phone),
345 | "values" => $this->fieldsValues,
346 | ]);
347 | }
348 | }
349 |
350 | /**
351 | * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View
352 | */
353 | public function render()
354 | {
355 | return view('phone_auth::livewire.phone-verification');
356 | }
357 | }
358 |
--------------------------------------------------------------------------------
/src/Models/ConfirmedPhone.php:
--------------------------------------------------------------------------------
1 | PhoneCast::class
27 | ];
28 |
29 | /**
30 | *
31 | */
32 | public static function boot()
33 | {
34 | parent::boot();
35 |
36 | self::creating(function (Model $model) {
37 | $model->user_id = auth(config("phone_auth.auth.guard"))->check() ? auth(config("phone_auth.auth.guard"))->id() : null;
38 | $model->ip = request()->ip();
39 | });
40 | }
41 |
42 |
43 | /**
44 | * @param $phone
45 | * @param null $user_id
46 | * @return int
47 | */
48 | public static function confirmed($phone, $user_id = null) {
49 | return self::query()->where(["phone" => Str::phoneNumber($phone)])->when($user_id, function (Builder $query) use($user_id) {
50 | return $query->where(["user_id" => $user_id]);
51 | })->when(is_null($user_id), function (Builder $query) {
52 | return $query->where(["ip" => request()->ip()]);
53 | })->count();
54 | }
55 |
56 | /**
57 | * @return mixed
58 | */
59 | public static function flush() {
60 | return self::query()->where("ip", request()->ip())->delete();
61 | }
62 |
63 | /**
64 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
65 | */
66 | public function user() {
67 | return $this->belongsTo(config("phone_auth.auth.class_user"));
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Models/PhoneVerificationCode.php:
--------------------------------------------------------------------------------
1 | PhoneCast::class
28 | ];
29 |
30 | /**
31 | *
32 | */
33 | public static function boot()
34 | {
35 | parent::boot();
36 |
37 | self::creating(function (Model $model) {
38 | if(config("phone_auth.code_digits_only")) {
39 | $code = rand(pow(10, config("phone_auth.code_length")-1), pow(10, config("phone_auth.code_length"))-1);
40 | } else {
41 | $code = Str::upper(Str::random(config("phone_auth.code_length")));
42 | }
43 |
44 | $model->user_id = self::getUserId();
45 | $model->ip = self::getIp();
46 | $model->code = $code;
47 | $model->expires_at = now()->addSeconds(config("phone_auth.expire_seconds"));
48 |
49 | app("sms")->send($model->phone, __("phone_auth::phone_auth.sms.text", ["code" => $code]));
50 | });
51 | }
52 |
53 | /**
54 | * @param Builder $query
55 | * @return Builder
56 | */
57 | public function scopeUnexpired(Builder $query): Builder
58 | {
59 | return $query->where('ip', self::getIp())->where('expires_at', '>=', now());
60 | }
61 |
62 | /**
63 | * @param Builder $query
64 | * @return Builder
65 | */
66 | public function scopeLimiter(Builder $query): Builder
67 | {
68 | return $query->where('ip', self::getIp())->whereDate('expires_at', '=', now());
69 | }
70 |
71 | /**
72 | * @return float|int
73 | */
74 | public static function nextSend() {
75 | $nexSendTimestamp = self::query()->unexpired()->max('created_at');
76 |
77 | $nexSendTimestamp = $nexSendTimestamp ? now()->setDateTimeFrom($nexSendTimestamp)->addSeconds(config("phone_auth.next_send_after")) : now();
78 |
79 | return $nexSendTimestamp->isPast() ? 0 : now()->diffInSeconds($nexSendTimestamp);
80 | }
81 |
82 | /**
83 | * @return mixed
84 | */
85 | public static function sendCountRemain() {
86 | $sendCount = config("phone_auth.limit_send_count") - self::query()->limiter()->count();
87 |
88 | return $sendCount < 0 ? 0 : $sendCount;
89 | }
90 |
91 | /**
92 | * @return mixed
93 | */
94 | public static function sendCount() {
95 | return self::query()->limiter()->count();
96 | }
97 |
98 | /**
99 | * @param $phone
100 | * @return mixed
101 | * @throws PhoneAuthLimitException
102 | */
103 | public static function sendCode($phone) {
104 | $nextSend = self::nextSend();
105 | $sendCount = self::sendCount();
106 |
107 | if($nextSend > 0) {
108 | throw new PhoneAuthLimitException(__("phone_auth::phone_auth.ui.sms_count_limit", ["seconds" => $nextSend]));
109 | }
110 |
111 | if(config("phone_auth.limit_send_count") > $sendCount) {
112 | return self::create(["phone" => $phone]);
113 | } else {
114 | throw new PhoneAuthLimitException(__("phone_auth::phone_auth.ui.send_count", ["count" => config("phone_auth.limit_send_count")]));
115 | }
116 | }
117 |
118 | /**
119 | * @param $code
120 | * @return mixed
121 | */
122 | public static function validateCode($code) {
123 | return self::query()->unexpired()->where("code", $code)->count();
124 | }
125 |
126 | /**
127 | * @return null|string
128 | */
129 | protected static function getIp() {
130 | return request()->ip();
131 | }
132 |
133 | /**
134 | * @return int|null|string
135 | */
136 | protected static function getUserId() {
137 | return auth(config("phone_auth.auth.guard"))->check() ? auth(config("phone_auth.auth.guard"))->id() : null;
138 | }
139 |
140 | /**
141 | * @return mixed
142 | */
143 | public static function flush() {
144 | return self::query()->where("ip", self::getIp())->delete();
145 | }
146 |
147 | /**
148 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
149 | */
150 | public function user() {
151 | return $this->belongsTo(config("phone_auth.class_user"));
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/src/Models/Traits/PhoneVerification.php:
--------------------------------------------------------------------------------
1 | where("phone", Str::phoneNumber($phone))->where("phone_verified", true)->count();
20 | }
21 |
22 | /**
23 | * @return mixed
24 | */
25 | public function hasVerifiedPhone()
26 | {
27 | return $this->phone_verified;
28 | }
29 |
30 | /**
31 | * @param $phone
32 | * @return bool
33 | */
34 | public function setVerifiedPhone($phone)
35 | {
36 | $this->forceFill([
37 | 'phone' => $phone,
38 | 'phone_verified' => true,
39 | 'phone_verified_at' => $this->freshTimestamp(),
40 | ]);
41 |
42 | return $this->save();
43 | }
44 | }
--------------------------------------------------------------------------------
/src/Providers/PhoneAuthServiceProvider.php:
--------------------------------------------------------------------------------
1 | app->singleton('sms', function ($app) {
26 | return (new SmsBox())->smsService();
27 | });
28 | }
29 |
30 | /**
31 | * Bootstrap services.
32 | *
33 | * @return void
34 | */
35 | public function boot()
36 | {
37 | $path = __DIR__ . "/..";
38 |
39 | /* Config */
40 | $this->publishes([
41 | $path . '/config/' . $this->namespace . '.php' => config_path($this->namespace . '.php'),
42 | ]);
43 |
44 | /* Views */
45 | $this->loadViewsFrom($path . '/views', $this->namespace);
46 |
47 | $this->publishes([
48 | $path . '/views' => resource_path('views/vendor/' . $this->namespace),
49 | ]);
50 |
51 | /* Translates */
52 |
53 | $this->loadTranslationsFrom($path . '/lang', $this->namespace);
54 |
55 | $this->publishes([
56 | $path . '/lang' => resource_path('lang/vendor/' . $this->namespace),
57 | ]);
58 |
59 | Livewire::component('phone-verification', PhoneVerification::class);
60 |
61 | /* Migrations */
62 | $this->loadMigrationsFrom($path . '/database/migrations');
63 |
64 | Str::macro('phoneNumber', function ($string) {
65 | return preg_replace('/^8{1}/', '7', preg_replace('/[^0-9]/', '', $string));
66 | });
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/Rules/PhoneNumber.php:
--------------------------------------------------------------------------------
1 | smsService = new $smsServiceClass();
34 | $this->smsService->settings(config("phone_auth.sms_service.settings"));
35 | }
36 | }
37 |
38 | /**
39 | * @return SmsServiceInterface
40 | */
41 | public function smsService() : SmsServiceInterface {
42 | return $this->smsService;
43 | }
44 | }
--------------------------------------------------------------------------------
/src/SmsServiceExample.php:
--------------------------------------------------------------------------------
1 | [
5 | "class_user" => "App\\Models\\User",
6 | "guard" => "web",
7 | "createUser" => true,
8 | "loginAfter" => true,
9 | "redirectTo" => false,
10 | ],
11 |
12 | "emitSendCode" => false,
13 | "emitBefore" => false,
14 | "emitAfter" => false,
15 |
16 | "code_length" => 4,
17 | "code_digits_only" => false,
18 | "verify_code_dynamic" => false,
19 | "limit_send_count" => 3,
20 | "next_send_after" => 30,
21 | "expire_seconds" => 240,
22 | "flushCode" => true,
23 |
24 | "custom_phone_field_name" => "phone", //If you need to use without a form
25 |
26 | "custom_fields" => ["name", "email"],
27 | "custom_fields_rules" => [
28 | "name" => "required",
29 | "email" => "required|email",
30 | ],
31 |
32 | "sms_service" => [
33 | "class" => \Leeto\PhoneAuth\SmsServiceExample::class,
34 | "settings" => [],
35 | ]
36 | ];
37 |
--------------------------------------------------------------------------------
/src/database/migrations/2021_02_18_090500_users_phone_auth.php:
--------------------------------------------------------------------------------
1 | string('phone')->unique()->nullable()->after("id");
21 | }
22 |
23 | if (!Schema::hasColumn('users', 'phone_verified')) {
24 | $table->boolean('phone_verified')->default(false)->after("phone");
25 | }
26 |
27 | if (!Schema::hasColumn('users', 'phone_verified_at')) {
28 | $table->timestamp('phone_verified_at')->nullable()->after("phone_verified");
29 | }
30 |
31 | if (Schema::hasColumn('users', 'email')) {
32 | $table->string('email')->nullable()->change();
33 | }
34 |
35 | if (Schema::hasColumn('users', 'name')) {
36 | $table->string('name')->nullable()->change();
37 | }
38 |
39 | if (Schema::hasColumn('users', 'password')) {
40 | $table->string('password')->nullable()->change();
41 | }
42 | });
43 | }
44 |
45 | if (!Schema::hasTable('phone_verification_codes')) {
46 | Schema::create('phone_verification_codes', function (Blueprint $table) {
47 | $table->id();
48 |
49 | $table->foreignId('user_id')
50 | ->nullable()
51 | ->constrained()
52 | ->onUpdate('cascade')
53 | ->onDelete('cascade')
54 | ;
55 |
56 | $table->ipAddress('ip');
57 | $table->string('phone');
58 | $table->string('code');
59 | $table->timestamp('expires_at');
60 |
61 | $table->timestamps();
62 | });
63 | }
64 |
65 | if (!Schema::hasTable('confirmed_phones')) {
66 | Schema::create('confirmed_phones', function (Blueprint $table) {
67 | $table->id();
68 |
69 | $table->ipAddress('ip');
70 | $table->foreignId('user_id')
71 | ->nullable()
72 | ->constrained()
73 | ->onUpdate('cascade')
74 | ->onDelete('cascade')
75 | ;
76 | $table->string('phone');
77 |
78 | $table->timestamps();
79 | });
80 | }
81 | }
82 |
83 |
84 | }
85 |
86 | /**
87 | * Reverse the migrations.
88 | *
89 | * @return void
90 | */
91 | public function down()
92 | {
93 | Schema::dropColumns('users', ['phone', 'phone_verified', 'phone_verified_at']);
94 | Schema::dropIfExists('phone_verification_codes');
95 | Schema::dropIfExists('confirmed_phones');
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/src/lang/en/phone_auth.php:
--------------------------------------------------------------------------------
1 | [
5 | "current_phone" => "Current phone",
6 | "change" => "Change",
7 | "next_send" => "Next send after :seconds sec.",
8 | "send_count" => "Send count limit - :count",
9 | "send_count_remain" => "Send count remain - :count",
10 | "sms_count_limit" => "Sms sending limit wait. Try after :seconds sec.",
11 | "send_again" => "Send again",
12 | "check_code" => "Check Code",
13 | "send_code" => "Send Code",
14 | "phone_field_label" => "Phone",
15 | "code_field_label" => "Code",
16 | "phone_verified_message" => "Phone verified",
17 | ],
18 | "validation" => [
19 | "phone_format" => "Incorrect phone format",
20 | "phone_already_use" => "The phone is already in use",
21 | "code_invalid" => "Code is invalid",
22 | "error" => "Something went wrong"
23 | ],
24 | "sms" => [
25 | "text" => ":code - Your code",
26 | ]
27 | ];
--------------------------------------------------------------------------------
/src/lang/ru/phone_auth.php:
--------------------------------------------------------------------------------
1 | [
5 | "current_phone" => "Ваш телефон",
6 | "change" => "Изменить",
7 | "next_send" => "Повторить отправку смс можно через :seconds сек.",
8 | "send_count" => "Ограничение в количестве отправок - :count",
9 | "send_count_remain" => "Осталось повторных попыток - :count",
10 | "sms_count_limit" => "Ограничение в отправке смс. Попробуйте через :seconds секунд.",
11 | "send_again" => "Отправить снова",
12 | "check_code" => "Проверить",
13 | "send_code" => "Отправить код",
14 | "phone_field_label" => "Телефон",
15 | "code_field_label" => "Код",
16 | "phone_verified_message" => "Телефон подтвержден",
17 | ],
18 | "validation" => [
19 | "phone_format" => "Формат номера телефона введен не правильно",
20 | "phone_already_use" => "Номер телефона уже используется",
21 | "code_invalid" => "Код введен не правильно",
22 | "error" => "Произошла ошибка",
23 | ],
24 | "sms" => [
25 | "text" => ":code - Ваш проверочный код",
26 | ]
27 | ];
--------------------------------------------------------------------------------
/src/views/livewire/phone-verification.blade.php:
--------------------------------------------------------------------------------
1 |
2 | @if($formWrap)
@endif
60 |
61 |
--------------------------------------------------------------------------------