From 21ad516dced6697603cbb4efc2775ea86680a743 Mon Sep 17 00:00:00 2001 From: Irwan Cahyono Date: Tue, 10 Feb 2026 16:11:19 +0700 Subject: [PATCH] api auth --- app/Http/Controllers/Api/AuthController.php | 49 +++++++++++ app/Models/User.php | 3 +- bootstrap/app.php | 1 + composer.json | 3 +- config/sanctum.php | 84 +++++++++++++++++++ ...02_create_personal_access_tokens_table.php | 33 ++++++++ database/seeders/AdminUserSeeder.php | 25 ++++++ routes/api.php | 18 ++++ 8 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 app/Http/Controllers/Api/AuthController.php create mode 100644 config/sanctum.php create mode 100644 database/migrations/2026_02_06_013702_create_personal_access_tokens_table.php create mode 100644 database/seeders/AdminUserSeeder.php create mode 100644 routes/api.php diff --git a/app/Http/Controllers/Api/AuthController.php b/app/Http/Controllers/Api/AuthController.php new file mode 100644 index 0000000..13c708b --- /dev/null +++ b/app/Http/Controllers/Api/AuthController.php @@ -0,0 +1,49 @@ +validate([ + 'email' => ['required', 'email'], + 'password' => ['required', 'string'], + ]); + + if (!Auth::attempt($credentials)) { + return response()->json([ + 'message' => 'Email dan/atau password tidak sesuai' + ], 401); + } + + $user = Auth::user(); + $user->tokens()->delete(); + + $token = $user->createToken('mobile-token')->plainTextToken; + + return response()->json([ + 'access_token' => $token, + 'token_type' => 'Bearer', + ]); + } + + public function me(Request $request): JSONResponse + { + return response()->json($request->user()); + } + + public function logout(Request $request): JSONResponse + { + $request->user()->currentAccessToken()->delete(); + + return response()->json([ + 'message' => 'Berhasil logout', + ]); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 749c7b7..a6ab88e 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -6,11 +6,12 @@ use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Laravel\Sanctum\HasApiTokens; class User extends Authenticatable { /** @use HasFactory<\Database\Factories\UserFactory> */ - use HasFactory, Notifiable; + use HasFactory, Notifiable, HasApiTokens; /** * The attributes that are mass assignable. diff --git a/bootstrap/app.php b/bootstrap/app.php index c183276..c3928c5 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -7,6 +7,7 @@ return Application::configure(basePath: dirname(__DIR__)) ->withRouting( web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) diff --git a/composer.json b/composer.json index ab16d7d..3018e7e 100644 --- a/composer.json +++ b/composer.json @@ -11,6 +11,7 @@ "require": { "php": "^8.2", "laravel/framework": "^12.0", + "laravel/sanctum": "^4.0", "laravel/tinker": "^2.10.1" }, "require-dev": { @@ -87,4 +88,4 @@ }, "minimum-stability": "stable", "prefer-stable": true -} \ No newline at end of file +} diff --git a/config/sanctum.php b/config/sanctum.php new file mode 100644 index 0000000..44527d6 --- /dev/null +++ b/config/sanctum.php @@ -0,0 +1,84 @@ + explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( + '%s%s', + 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', + Sanctum::currentApplicationUrlWithPort(), + // Sanctum::currentRequestHost(), + ))), + + /* + |-------------------------------------------------------------------------- + | 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. This will override any values set in the token's + | "expires_at" attribute, but first-party sessions are not affected. + | + */ + + 'expiration' => null, + + /* + |-------------------------------------------------------------------------- + | Token Prefix + |-------------------------------------------------------------------------- + | + | Sanctum can prefix new tokens in order to take advantage of numerous + | security scanning initiatives maintained by open source platforms + | that notify developers if they commit tokens into repositories. + | + | See: https://docs.github.com/en/code-security/secret-scanning/about-secret-scanning + | + */ + + 'token_prefix' => env('SANCTUM_TOKEN_PREFIX', ''), + + /* + |-------------------------------------------------------------------------- + | 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' => [ + 'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class, + 'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class, + 'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class, + ], + +]; diff --git a/database/migrations/2026_02_06_013702_create_personal_access_tokens_table.php b/database/migrations/2026_02_06_013702_create_personal_access_tokens_table.php new file mode 100644 index 0000000..40ff706 --- /dev/null +++ b/database/migrations/2026_02_06_013702_create_personal_access_tokens_table.php @@ -0,0 +1,33 @@ +id(); + $table->morphs('tokenable'); + $table->text('name'); + $table->string('token', 64)->unique(); + $table->text('abilities')->nullable(); + $table->timestamp('last_used_at')->nullable(); + $table->timestamp('expires_at')->nullable()->index(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('personal_access_tokens'); + } +}; diff --git a/database/seeders/AdminUserSeeder.php b/database/seeders/AdminUserSeeder.php new file mode 100644 index 0000000..739797a --- /dev/null +++ b/database/seeders/AdminUserSeeder.php @@ -0,0 +1,25 @@ +insert([ + 'name' => 'Admin', + 'email' => 'admin@dbpetani.com', + 'password' => Hash::make('Asalada123'), + 'created_at' => now(), + 'updated_at' => now(), + ]); + } +} diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 0000000..d163db2 --- /dev/null +++ b/routes/api.php @@ -0,0 +1,18 @@ +user(); +})->middleware('auth:sanctum'); + +Route::middleware('auth:sanctum') + ->name('auth.') + ->prefix('auth') + ->group(function () { + Route::get('/me', [\App\Http\Controllers\Api\AuthController::class, 'me']); + Route::post('/logout', [\App\Http\Controllers\Api\AuthController::class, 'logout']); +});