diff --git a/src/Auth/GatewayGuard.php b/src/Auth/GatewayGuard.php index 0c45dae..78f953a 100644 --- a/src/Auth/GatewayGuard.php +++ b/src/Auth/GatewayGuard.php @@ -2,22 +2,21 @@ namespace Kroderdev\LaravelMicroserviceCore\Auth; -use Firebase\JWT\JWT; -use Firebase\JWT\Key; use Illuminate\Auth\SessionGuard; use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract; use Illuminate\Contracts\Auth\UserProvider; use Illuminate\Contracts\Session\Session; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Cache; -use Illuminate\Support\Facades\Log; use Kroderdev\LaravelMicroserviceCore\Contracts\AccessUserInterface; use Kroderdev\LaravelMicroserviceCore\Services\AuthServiceClient; +use Kroderdev\LaravelMicroserviceCore\Services\JwtValidator; class GatewayGuard extends SessionGuard { protected AuthServiceClient $client; + protected JwtValidator $jwtValidator; + protected ?string $token = null; protected string $userModel; @@ -32,10 +31,12 @@ public function __construct( UserProvider $provider, Session $session, Request $request, - AuthServiceClient $client + AuthServiceClient $client, + JwtValidator $jwtValidator ) { parent::__construct($name, $provider, $session, $request); $this->client = $client; + $this->jwtValidator = $jwtValidator; $this->userModel = config('microservice.gateway_guard.user_model', ExternalUser::class); $this->loadAccess = (bool) config('microservice.gateway_guard.load_access', true); } @@ -81,28 +82,8 @@ public function user() protected function retrieveUserData(): ?array { $token = $this->token; - try { - // Attempt to get the JWT public key from cache or load it from file - $publicKey = Cache::remember('jwt_public_key', config('microservice.auth.jwt_cache_ttl', 3600), function () { - $path = config('microservice.auth.jwt_public_key'); - Log::info('Loaded JWT public key for token validation', ['path' => $path]); - - return file_get_contents($path); - }); - - // Try to decode the token to check if it's valid - JWT::decode($token, new Key($publicKey, config('microservice.auth.jwt_algorithm'))); - $valid = true; - } catch (\Throwable $e) { - // Log decode failure and mark token as invalid - Log::warning('JWT decode failed in GatewayGuard', [ - 'error' => $e->getMessage(), - 'token' => $token, - ]); - $valid = false; - } - if (! $valid) { + if (! $this->jwtValidator->isValid($token)) { try { // Attempt to refresh the token using the AuthServiceClient $response = $this->client->refresh($token); diff --git a/src/Http/Middleware/ValidateJwt.php b/src/Http/Middleware/ValidateJwt.php index e9801cf..19cefa2 100644 --- a/src/Http/Middleware/ValidateJwt.php +++ b/src/Http/Middleware/ValidateJwt.php @@ -3,16 +3,18 @@ namespace Kroderdev\LaravelMicroserviceCore\Http\Middleware; use Closure; -use Firebase\JWT\JWT; -use Firebase\JWT\Key; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; -use Illuminate\Support\Facades\Cache; use Kroderdev\LaravelMicroserviceCore\Auth\ExternalUser; +use Kroderdev\LaravelMicroserviceCore\Services\JwtValidator; use Symfony\Component\HttpFoundation\Response; class ValidateJwt { + public function __construct(protected JwtValidator $validator) + { + } + /** * Handle an incoming request. * @@ -31,11 +33,7 @@ public function handle(Request $request, Closure $next): Response $token = substr($authHeader, strlen((string) $prefix) + 1); try { - $publicKey = Cache::remember('jwt_public_key', config('microservice.auth.jwt_cache_ttl', 3600), function () { - return file_get_contents(config('microservice.auth.jwt_public_key')); - }); - - $decoded = JWT::decode($token, new Key($publicKey, config('microservice.auth.jwt_algorithm'))); + $decoded = $this->validator->decode($token); // Auth from JWT $user = new ExternalUser(['sub' => $decoded->sub]); diff --git a/src/Providers/MicroserviceServiceProvider.php b/src/Providers/MicroserviceServiceProvider.php index 3b28666..d93732c 100644 --- a/src/Providers/MicroserviceServiceProvider.php +++ b/src/Providers/MicroserviceServiceProvider.php @@ -24,6 +24,7 @@ use Kroderdev\LaravelMicroserviceCore\Services\ApiGatewayClientFactory; use Kroderdev\LaravelMicroserviceCore\Services\AuthServiceClient; use Kroderdev\LaravelMicroserviceCore\Services\PermissionsClient; +use Kroderdev\LaravelMicroserviceCore\Services\JwtValidator; class MicroserviceServiceProvider extends ServiceProvider { @@ -43,6 +44,7 @@ public function register(): void $this->app->bind(ApiGatewayClientInterface::class, fn ($app) => $app->make(ApiGatewayClientFactory::class)->default()); $this->app->scoped(PermissionsClient::class, fn ($app) => new PermissionsClient($app->make(ApiGatewayClientInterface::class))); $this->app->singleton(AuthServiceClient::class, fn () => new AuthServiceClient()); + $this->app->singleton(JwtValidator::class, fn () => new JwtValidator()); $this->app->singleton(\Illuminate\Foundation\Console\ModelMakeCommand::class, function ($app) { return new \Kroderdev\LaravelMicroserviceCore\Console\ModelMakeCommand($app['files']); @@ -75,7 +77,8 @@ public function boot(Router $router): void $provider, $app['session.store'], $app->make('request'), - $app->make(AuthServiceClient::class) + $app->make(AuthServiceClient::class), + $app->make(JwtValidator::class) ); $guard->setCookieJar($app['cookie']); diff --git a/src/Services/JwtValidator.php b/src/Services/JwtValidator.php new file mode 100644 index 0000000..fd9c877 --- /dev/null +++ b/src/Services/JwtValidator.php @@ -0,0 +1,44 @@ +getPublicKey(); + + return JWT::decode($token, new Key($publicKey, config('microservice.auth.jwt_algorithm'))); + } + + public function isValid(string $token): bool + { + try { + $this->decode($token); + + return true; + } catch (\Throwable $e) { + Log::warning('JWT decode failed', [ + 'error' => $e->getMessage(), + 'token' => $token, + ]); + + return false; + } + } + + protected function getPublicKey(): string + { + return Cache::remember('jwt_public_key', config('microservice.auth.jwt_cache_ttl', 3600), function () { + $path = config('microservice.auth.jwt_public_key'); + Log::info('Loaded JWT public key for token validation', ['path' => $path]); + + return file_get_contents($path); + }); + } +} diff --git a/tests/Auth/GatewayGuardTest.php b/tests/Auth/GatewayGuardTest.php index 3da1ecb..33479f6 100644 --- a/tests/Auth/GatewayGuardTest.php +++ b/tests/Auth/GatewayGuardTest.php @@ -11,6 +11,7 @@ use Kroderdev\LaravelMicroserviceCore\Auth\GatewayGuard; use Kroderdev\LaravelMicroserviceCore\Providers\MicroserviceServiceProvider; use Kroderdev\LaravelMicroserviceCore\Services\AuthServiceClient; +use Kroderdev\LaravelMicroserviceCore\Services\JwtValidator; use Orchestra\Testbench\TestCase; class FakeAuthServiceClient extends AuthServiceClient @@ -100,7 +101,8 @@ protected function setUp(): void $provider, $app['session.store'], $app->make('request'), - $app->make(AuthServiceClient::class) + $app->make(AuthServiceClient::class), + $app->make(JwtValidator::class) ); }); diff --git a/tests/Http/GatewayAuthControllersTest.php b/tests/Http/GatewayAuthControllersTest.php index f18a1ef..4ccdc40 100644 --- a/tests/Http/GatewayAuthControllersTest.php +++ b/tests/Http/GatewayAuthControllersTest.php @@ -14,6 +14,7 @@ use Kroderdev\LaravelMicroserviceCore\Http\Auth\SocialiteController; use Kroderdev\LaravelMicroserviceCore\Providers\MicroserviceServiceProvider; use Kroderdev\LaravelMicroserviceCore\Services\AuthServiceClient; +use Kroderdev\LaravelMicroserviceCore\Services\JwtValidator; use Orchestra\Testbench\TestCase; class FakeAuthServiceClient extends AuthServiceClient @@ -60,7 +61,8 @@ protected function setUp(): void $provider, $app['session.store'], $app->make('request'), - $app->make(AuthServiceClient::class) + $app->make(AuthServiceClient::class), + $app->make(JwtValidator::class) ); });