Skip to content

Commit 04a561b

Browse files
committed
login refactoring
1 parent fedee5a commit 04a561b

File tree

6 files changed

+70
-118
lines changed

6 files changed

+70
-118
lines changed

app/Http/Controllers/AuthController.php

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22

33
namespace App\Http\Controllers;
44

5-
use App\Http\Requests\Auth\LoginRequest;
65
use App\Models\User;
76
use App\Models\UserProvider;
8-
use Browser;
97
use Illuminate\Auth\Events\PasswordReset;
108
use Illuminate\Auth\Events\Registered;
119
use Illuminate\Auth\Events\Verified;
@@ -114,23 +112,17 @@ public function callback(Request $request, string $provider): View
114112
$user = $userProvider->user;
115113
}
116114

117-
$browser = Browser::parse($request->userAgent());
118-
$device = $browser->platformName() . ' / ' . $browser->browserName();
119-
120-
$sanctumToken = $user->createToken(
121-
$device,
122-
['*'],
123-
now()->addMonth()
115+
$token = $user->createDeviceToken(
116+
device: $request->deviceName(),
117+
ip: $request->ip(),
118+
remember: true
124119
);
125120

126-
$sanctumToken->accessToken->ip = $request->ip();
127-
$sanctumToken->accessToken->save();
128-
129121
return view('oauth', [
130122
'message' => [
131123
'ok' => true,
132124
'provider' => $provider,
133-
'token' => $sanctumToken->plainTextToken,
125+
'token' => $token,
134126
],
135127
]);
136128
}
@@ -139,29 +131,30 @@ public function callback(Request $request, string $provider): View
139131
* Generate sanctum token on successful login
140132
* @throws ValidationException
141133
*/
142-
public function login(LoginRequest $request): JsonResponse
134+
public function login(Request $request): JsonResponse
143135
{
144-
$user = User::where('email', $request->email)->first();
136+
$request->validate([
137+
'email' => ['required', 'string', 'email'],
138+
'password' => ['required', 'string'],
139+
]);
145140

146-
$request->authenticate($user);
141+
$user = User::select(['id', 'password'])->where('email', $request->email)->first();
147142

148-
$browser = Browser::parse($request->userAgent());
149-
$device = $browser->platformName() . ' / ' . $browser->browserName();
143+
if (!$user || !Hash::check($request->password, $user->password)) {
144+
throw ValidationException::withMessages([
145+
'email' => __('auth.failed'),
146+
]);
147+
}
150148

151-
$sanctumToken = $user->createToken(
152-
$device,
153-
['*'],
154-
$request->remember ?
155-
now()->addMonth() :
156-
now()->addDay()
149+
$token = $user->createDeviceToken(
150+
device: $request->deviceName(),
151+
ip: $request->ip(),
152+
remember: $request->remember
157153
);
158154

159-
$sanctumToken->accessToken->ip = $request->ip();
160-
$sanctumToken->accessToken->save();
161-
162155
return response()->json([
163156
'ok' => true,
164-
'token' => $sanctumToken->plainTextToken,
157+
'token' => $token,
165158
]);
166159
}
167160

app/Http/Requests/Auth/LoginRequest.php

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

app/Models/User.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,20 @@ public function mustVerifyEmail(): bool
6060
{
6161
return $this instanceof MustVerifyEmail && !$this->hasVerifiedEmail();
6262
}
63+
64+
public function createDeviceToken(string $device, string $ip, bool $remember = false): string
65+
{
66+
$sanctumToken = $this->createToken(
67+
$device,
68+
['*'],
69+
$remember ?
70+
now()->addMonth() :
71+
now()->addDay()
72+
);
73+
74+
$sanctumToken->accessToken->ip = $ip;
75+
$sanctumToken->accessToken->save();
76+
77+
return $sanctumToken->plainTextToken;
78+
}
6379
}

app/Providers/AppServiceProvider.php

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace App\Providers;
44

55
use App\Helpers\Image;
6+
use Illuminate\Auth\Events\Lockout;
67
use Illuminate\Auth\Notifications\ResetPassword;
78
use Illuminate\Auth\Notifications\VerifyEmail;
89
use Illuminate\Cache\RateLimiting\Limit;
@@ -11,6 +12,7 @@
1112
use Illuminate\Support\Facades\RateLimiter;
1213
use Illuminate\Support\ServiceProvider;
1314
use Illuminate\Support\Str;
15+
use Illuminate\Validation\ValidationException;
1416

1517
class AppServiceProvider extends ServiceProvider
1618
{
@@ -45,8 +47,26 @@ public function boot(): void
4547
: Limit::perMinute(10)->by($request->ip());
4648
});
4749

50+
RateLimiter::for('login', static function (Request $request) {
51+
return Limit::perMinute(5)
52+
->by(Str::transliterate(implode('|', [
53+
strtolower($request->input('email')),
54+
$request->ip()
55+
])))
56+
->response(static function (Request $request, array $headers): void {
57+
event(new Lockout($request));
58+
59+
throw ValidationException::withMessages([
60+
'email' => trans('auth.throttle', [
61+
'seconds' => $headers['Retry-After'],
62+
'minutes' => ceil($headers['Retry-After'] / 60),
63+
]),
64+
]);
65+
});
66+
});
67+
4868
ResetPassword::createUrlUsing(static function (object $notifiable, string $token) {
49-
return config('app.frontend_url') . "/auth/reset/{$token}?email={$notifiable->getEmailForPasswordReset()}";
69+
return config('app.frontend_url') . '/auth/reset/' . $token . '?email=' . $notifiable->getEmailForPasswordReset();
5070
});
5171

5272
VerifyEmail::createUrlUsing(static function (object $notifiable) {
@@ -65,7 +85,7 @@ public function boot(): void
6585
/**
6686
* Convert uploaded image to webp, jpeg or png format and resize it
6787
*/
68-
UploadedFile::macro('convert', function (?int $width = null, ?int $height = null, string $extension = 'webp', int $quality = 90): UploadedFile {
88+
UploadedFile::macro('convert', function (?int $width = null, ?int $height = null, string $extension = 'webp', int $quality = 90) {
6989
return tap($this, static function (UploadedFile $file) use ($width, $height, $extension, $quality) {
7090
Image::convert($file->path(), $file->path(), $width, $height, $extension, $quality);
7191
});
@@ -79,5 +99,14 @@ public function boot(): void
7999
// \d matches a digit in any script
80100
return Str::replaceMatches('/[^\p{L}\d ]/u', '', $text);
81101
});
102+
103+
Request::macro('deviceName', function (): string {
104+
$device = $this->device();
105+
106+
return implode(' / ', array_filter([
107+
trim(implode(' ', [$device->getOs('name'), $device->getOs('version')])),
108+
trim(implode(' ', [$device->getClient('name'), $device->getClient('version')])),
109+
])) ?? 'Unknown';
110+
});
82111
}
83112
}

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
"license": "MIT",
1010
"require": {
1111
"php": "^8.2",
12-
"hisorange/browser-detect": "^5.0",
1312
"intervention/image": "^3.4",
1413
"laravel/framework": "^11.0",
1514
"laravel/octane": "^2.3",
1615
"laravel/sanctum": "^4.0",
1716
"laravel/socialite": "^5.12",
1817
"laravel/tinker": "^2.9",
1918
"league/flysystem-aws-s3-v3": "^3.24",
19+
"reefki/laravel-device-detector": "^1.0",
2020
"spatie/laravel-permission": "^6.4"
2121
},
2222
"require-dev": {

routes/api.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
Route::prefix('api/v1')->group(function () {
1313
Route::get('login/{provider}/redirect', [AuthController::class, 'redirect'])->name('login.provider.redirect');
1414
Route::get('login/{provider}/callback', [AuthController::class, 'callback'])->name('login.provider.callback');
15-
Route::post('login', [AuthController::class, 'login'])->name('login');
15+
Route::post('login', [AuthController::class, 'login'])->middleware('throttle:login')->name('login');
1616
Route::post('register', [AuthController::class, 'register'])->name('register');
1717
Route::post('forgot-password', [AuthController::class, 'sendResetPasswordLink'])->middleware('throttle:5,1')->name('password.email');
1818
Route::post('reset-password', [AuthController::class, 'resetPassword'])->name('password.store');

0 commit comments

Comments
 (0)