diff --git a/src/Auth/DynamicJwtAuthDriver.php b/src/Auth/DynamicJwtAuthDriver.php new file mode 100644 index 0000000..438a66a --- /dev/null +++ b/src/Auth/DynamicJwtAuthDriver.php @@ -0,0 +1,231 @@ +provider = $provider; + $this->request = request(); + } + + /** + * Get the currently authenticated user. + * + * @return \Illuminate\Contracts\Auth\Authenticatable|null + */ + public function user() + { + if (!$this->validate()) { + return null; + } + + if (!is_null($this->user)) { + return $this->user; + } + + // retrieve user from db or create a new user by id + if (is_null($user = $this->provider->retrieveById($this->jwtPayload->sub))) { + $model = $this->provider->createModel(); + $model->forceCreate([ + 'id' => $this->jwtPayload->sub + ]); + $user = $model->query()->where('id', $this->jwtPayload->sub)->first(); + } + + $this->setUser($user); + $this->afterRetrieveUserValidation(); + return $user; + } + + + /** + * @return false|string + */ + public function fetchPublicKey() + { + $url = rtrim(config('sguard.auth_server_url'), '/') . '/public/public_key'; + return file_get_contents($url); + } + + /** + * @return false|string + */ + public function fetchAlgo() + { + $url = rtrim(config('sguard.auth_server_url'), '/') . '/public/algo'; + return file_get_contents($url); + } + + /** + * @return array + */ + public function fetchRevokedTokens(): array + { + try { + $url = rtrim(config('sguard.auth_server_url'), '/') . '/public/revoked_ids'; + return json_decode(file_get_contents($url)); + } catch (Throwable $e) { + return []; + } + } + + /** + * Validate a token signature + * + * @param array $credentials + * @return bool + * @throws AuthenticationException + */ + public function validate(array $credentials = []): bool + { + if (is_null($bearerToken = $this->request->bearerToken())) { + return false; + } + + try { + $publicKey = Cache::remember('supaapps_jwt/public_key', 600, function () { + return $this->fetchPublicKey(); + }); + + $algorithm = Cache::remember('supaapps_jwt/algo', 600, function () { + return $this->fetchAlgo(); + }); + $this->jwtPayload = JWT::decode($bearerToken, new Key(trim($publicKey), trim($algorithm))); + $revokedIds = Cache::remember('supaapps_jwt/revoked_ids', 15, function () { + return $this->fetchRevokedTokens(); + }); + if (in_array($this->jwtPayload->id, $revokedIds)) { + throw new AuthenticationException('access token has been revoked'); + } + $this->firstName = $this->jwtPayload->first_name; + $this->lastName = $this->jwtPayload->last_name; + $this->email = $this->jwtPayload->email; + $this->scopesArray = explode(' ', $this->jwtPayload->scopes); + $this->scopes = $this->jwtPayload->scopes; + } catch (Throwable $ex) { + throw new AuthenticationException('Auth error - ' . $ex->getMessage()); + } + + return true; + } + + public function afterRetrieveUserValidation() + { + if ($this->user->realm->name !== $this->jwtPayload->aud) { + throw new AuthenticationException('Auth error - realm mismatch'); + } + + if (strpos($this->scopes, '/' . $this->jwtPayload->aud . '/*') !== false) { + $this->admin = true; + } else { + $this->admin = false; + } + } + + public function setRequest(Request $request) + { + $this->request = $request; + + return $this; + } + + /** + * @return string + */ + public function firstName(): string + { + return $this->firstName; + } + + /** + * @return string + */ + public function lastName(): string + { + return $this->lastName; + } + + /** + * @return string + */ + public function email(): string + { + return $this->email; + } + + /** + * @return string + */ + public function scopes(): string + { + return $this->scopes; + } + + /** + * @return array + */ + public function scopesArray(): array + { + return $this->scopesArray; + } +} diff --git a/src/GuardServiceProvider.php b/src/GuardServiceProvider.php index 2da1a37..60fa694 100644 --- a/src/GuardServiceProvider.php +++ b/src/GuardServiceProvider.php @@ -5,6 +5,7 @@ use Illuminate\Contracts\Foundation\Application; use Illuminate\Support\Facades\Auth; use Illuminate\Support\ServiceProvider; +use Supaapps\Guard\Auth\DynamicJwtAuthDriver; use Supaapps\Guard\Auth\JwtAuthDriver; class GuardServiceProvider extends ServiceProvider @@ -29,6 +30,12 @@ public function boot(): void Auth::createUserProvider($config['provider']) ); }); + + Auth::extend('supaapps-dynamic-guard', function (Application $app, string $name, array $config) { + return new DynamicJwtAuthDriver( + Auth::createUserProvider($config['provider']) + ); + }); } /**