From c3f54c79cb49dac1c33940c181ae6a4bfa0b3a75 Mon Sep 17 00:00:00 2001 From: ncoronel Date: Tue, 21 Jul 2026 10:36:40 -0300 Subject: [PATCH] feat(auth): implement token expiration for personal access tokens and update tests --- .env.example | 2 ++ .../Auth/Controllers/LoginController.php | 7 ++++++- config/sanctum.php | 2 +- tests/Feature/Auth/LoginControllerTest.php | 21 +++++++++++++++++++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/.env.example b/.env.example index a3c1c20..ac6557a 100644 --- a/.env.example +++ b/.env.example @@ -35,6 +35,8 @@ SESSION_ENCRYPT=false SESSION_PATH=/ SESSION_DOMAIN=null +SANCTUM_EXPIRATION=720 + BROADCAST_CONNECTION=log FILESYSTEM_DISK=local QUEUE_CONNECTION=database diff --git a/app/Domains/Auth/Controllers/LoginController.php b/app/Domains/Auth/Controllers/LoginController.php index 626c345..7e4110a 100644 --- a/app/Domains/Auth/Controllers/LoginController.php +++ b/app/Domains/Auth/Controllers/LoginController.php @@ -26,7 +26,12 @@ class LoginController extends Controller ]); } - $token = $user->createToken('api-token')->plainTextToken; + $expirationMinutes = (int) config('sanctum.expiration'); + $token = $user->createToken( + 'api-token', + ['*'], + now()->addMinutes($expirationMinutes), + )->plainTextToken; return response()->json([ 'message' => 'Sesion iniciada correctamente.', diff --git a/config/sanctum.php b/config/sanctum.php index d315693..574712e 100644 --- a/config/sanctum.php +++ b/config/sanctum.php @@ -50,7 +50,7 @@ return [ | */ - 'expiration' => null, + 'expiration' => (int) env('SANCTUM_EXPIRATION', 720), /* |-------------------------------------------------------------------------- diff --git a/tests/Feature/Auth/LoginControllerTest.php b/tests/Feature/Auth/LoginControllerTest.php index dc4e3a4..c266479 100644 --- a/tests/Feature/Auth/LoginControllerTest.php +++ b/tests/Feature/Auth/LoginControllerTest.php @@ -34,6 +34,12 @@ class LoginControllerTest extends TestCase $this->assertIsString($response->json('token')); $this->assertNotEmpty($response->json('token')); + $accessToken = $user->tokens()->sole(); + $this->assertTrue( + $accessToken->expires_at->equalTo( + $accessToken->created_at->copy()->addMinutes(config('sanctum.expiration')) + ) + ); $this->withHeader('Authorization', 'Bearer '.$response->json('token')) ->getJson('/api/me') @@ -42,6 +48,21 @@ class LoginControllerTest extends TestCase ->assertJsonPath('email', 'grace@example.com'); } + public function test_personal_access_tokens_expire_after_twelve_hours(): void + { + $this->travelTo(now()->startOfSecond()); + + $user = User::factory()->create(); + $token = $user->createToken('api-token')->plainTextToken; + + $this->travel(12)->hours(); + $this->travel(1)->minutes(); + + $this->withHeader('Authorization', 'Bearer '.$token) + ->getJson('/api/me') + ->assertUnauthorized(); + } + public function test_it_rejects_access_to_the_current_user_endpoint_without_a_token(): void { $this->getJson('/api/me')->assertUnauthorized();