diff --git a/src/app/Packages/Domains/User/Interface/TokenServiceInterface.php b/src/app/Packages/Domains/User/Interface/TokenServiceInterface.php index c224937..d9719a0 100644 --- a/src/app/Packages/Domains/User/Interface/TokenServiceInterface.php +++ b/src/app/Packages/Domains/User/Interface/TokenServiceInterface.php @@ -9,4 +9,6 @@ interface TokenServiceInterface public function createToken(UserEntity $entity): string; public function revokeAllTokens(UserEntity $entity): void; + + public function revokeCurrentToken(string $plainTextToken): void; } diff --git a/src/app/Packages/Domains/User/Service/SanctumTokenService.php b/src/app/Packages/Domains/User/Service/SanctumTokenService.php index 1c9c84d..2af30c5 100644 --- a/src/app/Packages/Domains/User/Service/SanctumTokenService.php +++ b/src/app/Packages/Domains/User/Service/SanctumTokenService.php @@ -5,6 +5,7 @@ use App\Models\User; use App\Packages\Domains\User\Interface\TokenServiceInterface; use App\Packages\Domains\User\UserEntity; +use Laravel\Sanctum\PersonalAccessToken; use RuntimeException; class SanctumTokenService implements TokenServiceInterface @@ -30,4 +31,15 @@ public function revokeAllTokens(UserEntity $entity): void $user->tokens()->delete(); } + + // Logout lives here rather than in UserRepository because it operates on personal_access_tokens, + // not on user data. No user lookup is needed — the plain-text token alone identifies the session to end. + public function revokeCurrentToken(string $plainTextToken): void + { + $token = PersonalAccessToken::findToken($plainTextToken); + + if ($token !== null) { + $token->delete(); + } + } } diff --git a/src/app/Packages/Domains/User/Tests/Service/SanctumTokenServiceTest.php b/src/app/Packages/Domains/User/Tests/Service/SanctumTokenServiceTest.php index 98db8ee..bd71646 100644 --- a/src/app/Packages/Domains/User/Tests/Service/SanctumTokenServiceTest.php +++ b/src/app/Packages/Domains/User/Tests/Service/SanctumTokenServiceTest.php @@ -133,4 +133,26 @@ public function test_revokeAllTokens_throws_when_user_not_found(): void $this->service()->revokeAllTokens($entity); } + + public function test_revokeCurrentToken_deletes_the_given_token(): void + { + $user = $this->seedUser(); + $plainText = $user->createToken('auth-token')->plainTextToken; + + $this->assertDatabaseHas('personal_access_tokens', [ + 'tokenable_id' => $user->id, + ]); + + $this->service()->revokeCurrentToken($plainText); + + $this->assertDatabaseMissing('personal_access_tokens', [ + 'tokenable_id' => $user->id, + ]); + } + + #[\PHPUnit\Framework\Attributes\DoesNotPerformAssertions] + public function test_revokeCurrentToken_does_nothing_when_token_not_found(): void + { + $this->service()->revokeCurrentToken('invalid-token-that-does-not-exist'); + } }