From 86c42a9e0622de920a000e73767fada150f406fd Mon Sep 17 00:00:00 2001 From: Arthur Monney Date: Wed, 2 Nov 2022 14:31:46 +0100 Subject: [PATCH] :alien: Mis en place de l'api de connexion avec Sanctum --- .../Controllers/Api/Auth/LoginController.php | 59 ++++++++++++++++ app/Http/Kernel.php | 1 + app/Http/Requests/Api/LoginRequest.php | 31 +++++++++ .../Resources/AuthenticateUserResource.php | 39 +++++++++++ app/Models/User.php | 2 + composer.json | 1 + composer.lock | 67 ++++++++++++++++++- config/sanctum.php | 67 +++++++++++++++++++ ...01_create_personal_access_tokens_table.php | 37 ++++++++++ routes/api.php | 10 +++ 10 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 app/Http/Controllers/Api/Auth/LoginController.php create mode 100644 app/Http/Requests/Api/LoginRequest.php create mode 100644 app/Http/Resources/AuthenticateUserResource.php create mode 100644 config/sanctum.php create mode 100644 database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php diff --git a/app/Http/Controllers/Api/Auth/LoginController.php b/app/Http/Controllers/Api/Auth/LoginController.php new file mode 100644 index 00000000..f4099e65 --- /dev/null +++ b/app/Http/Controllers/Api/Auth/LoginController.php @@ -0,0 +1,59 @@ +with(['roles', 'permissions']) + ->where('email', strtolower($request->input('email'))) + ->first(); + + $sanitized = [ + 'email' => strtolower($request->input('email')), + 'password' => $request->input('password'), + ]; + + if (empty($user) || !Auth::attempt($sanitized)) { + throw ValidationException::withMessages([ + 'email' => 'Les informations d\'identification fournies sont incorrectes.', + ]); + } + + if (! empty($user->tokens())) { + $user->tokens()->delete(); + } + + $user->last_login_at = Carbon::now(); + $user->last_login_ip = $request->ip(); + $user->save(); + + return response()->json([ + 'user' => new AuthenticateUserResource($user), + 'token' => $user->createToken($request->input('email'))->plainTextToken, + 'roles' => $user->roles()->pluck('name'), + 'permissions' => $user->permissions()->pluck('name'), + ]); + } + + public function logout(Request $request): JsonResponse + { + if ($request->user()->currentAccessToken()) { + $request->user()->currentAccessToken()->delete(); + } + + return response()->json(['message' => 'Déconnecté avec succès']); + } +} diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index aaca3676..e87c3846 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -41,6 +41,7 @@ class Kernel extends HttpKernel ], 'api' => [ + \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], diff --git a/app/Http/Requests/Api/LoginRequest.php b/app/Http/Requests/Api/LoginRequest.php new file mode 100644 index 00000000..aac4ed52 --- /dev/null +++ b/app/Http/Requests/Api/LoginRequest.php @@ -0,0 +1,31 @@ + + */ + public function rules(): array + { + return [ + 'email' => ['required', 'email', 'exists:users,email'], + 'password' => ['required'], + ]; + } +} diff --git a/app/Http/Resources/AuthenticateUserResource.php b/app/Http/Resources/AuthenticateUserResource.php new file mode 100644 index 00000000..e854fc78 --- /dev/null +++ b/app/Http/Resources/AuthenticateUserResource.php @@ -0,0 +1,39 @@ + $this->id, + 'name' => $this->name, + 'email' => $this->email, + 'username' => $this->username, + 'bio' => $this->bio, + 'profilePhotoUrl' => $this->profile_photo_url, + 'phoneNumber' => $this->phone_number, + 'optIn' => $this->opt_in, + 'settings' => $this->settings, + 'reputation' => $this->reputation, + + 'lastLoginAt' => $this->last_login_at, + 'emailVerifiedAt' => $this->email_verified_at, + 'createdAt' => $this->created_at, + 'updatedAt' => $this->updated_at, + ]; + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 30117f1f..45621b8f 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -13,6 +13,7 @@ use Illuminate\Notifications\Notifiable; use Illuminate\Notifications\Notification; use Illuminate\Support\Facades\Auth; +use Laravel\Sanctum\HasApiTokens; use QCod\Gamify\Gamify; use Rinvex\Subscriptions\Traits\HasPlanSubscriptions; use Spatie\MediaLibrary\HasMedia; @@ -28,6 +29,7 @@ class User extends Authenticatable implements MustVerifyEmail, HasMedia use HasFactory; use HasPlanSubscriptions; use HasProfilePhoto; + use HasApiTokens; use HasRoles; use InteractsWithMedia; use Notifiable; diff --git a/composer.json b/composer.json index 60954aa7..aa143b04 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ "laravel-notification-channels/twitter": "^6.0", "laravel/fortify": "^1.13", "laravel/framework": "^9.24", + "laravel/sanctum": "^3.0", "laravel/slack-notification-channel": "^2.4", "laravel/socialite": "^5.2", "laravel/tinker": "^2.5", diff --git a/composer.lock b/composer.lock index ac8e42aa..166a7714 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1cd7658576ca9c6b971d277592f8486c", + "content-hash": "9da85bc5e37ee65ac37bfbefd993f3c4", "packages": [ { "name": "abraham/twitteroauth", @@ -3416,6 +3416,71 @@ }, "time": "2022-10-25T15:43:46+00:00" }, + { + "name": "laravel/sanctum", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/laravel/sanctum.git", + "reference": "b71e80a3a8e8029e2ec8c1aa814b999609ce16dc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/b71e80a3a8e8029e2ec8c1aa814b999609ce16dc", + "reference": "b71e80a3a8e8029e2ec8c1aa814b999609ce16dc", + "shasum": "" + }, + "require": { + "ext-json": "*", + "illuminate/console": "^9.21", + "illuminate/contracts": "^9.21", + "illuminate/database": "^9.21", + "illuminate/support": "^9.21", + "php": "^8.0.2" + }, + "require-dev": { + "mockery/mockery": "^1.0", + "orchestra/testbench": "^7.0", + "phpunit/phpunit": "^9.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + }, + "laravel": { + "providers": [ + "Laravel\\Sanctum\\SanctumServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "Laravel\\Sanctum\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.", + "keywords": [ + "auth", + "laravel", + "sanctum" + ], + "support": { + "issues": "https://github.com/laravel/sanctum/issues", + "source": "https://github.com/laravel/sanctum" + }, + "time": "2022-07-29T21:33:30+00:00" + }, { "name": "laravel/serializable-closure", "version": "v1.2.2", diff --git a/config/sanctum.php b/config/sanctum.php new file mode 100644 index 00000000..529cfdc9 --- /dev/null +++ b/config/sanctum.php @@ -0,0 +1,67 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Sanctum::currentApplicationUrlWithPort() + ))), + + /* + |-------------------------------------------------------------------------- + | Sanctum Guards + |-------------------------------------------------------------------------- + | + | This array contains the authentication guards that will be checked when + | Sanctum is trying to authenticate a request. If none of these guards + | are able to authenticate the request, Sanctum will use the bearer + | token that's present on an incoming request for authentication. + | + */ + + 'guard' => ['web'], + + /* + |-------------------------------------------------------------------------- + | Expiration Minutes + |-------------------------------------------------------------------------- + | + | This value controls the number of minutes until an issued token will be + | considered expired. If this value is null, personal access tokens do + | not expire. This won't tweak the lifetime of first-party sessions. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Sanctum Middleware + |-------------------------------------------------------------------------- + | + | When authenticating your first-party SPA with Sanctum you may need to + | customize some of the middleware Sanctum uses while processing the + | request. You may change the middleware listed below as required. + | + */ + + 'middleware' => [ + 'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class, + 'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class, + ], + +]; diff --git a/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php b/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php new file mode 100644 index 00000000..6c81fd22 --- /dev/null +++ b/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php @@ -0,0 +1,37 @@ +id(); + $table->morphs('tokenable'); + $table->string('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/routes/api.php b/routes/api.php index 2ddaecbf..867b2599 100644 --- a/routes/api.php +++ b/routes/api.php @@ -1,5 +1,6 @@ group(function () { + Route::post('logout', [LoginController::class, 'logout']); +});