├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── .php-cs-fixer.php ├── LICENSE ├── README.md ├── composer.json ├── migrations └── .gitkeep ├── phpunit.xml.dist ├── src ├── CacheClientRepository.php └── CacheClientServiceProvider.php └── tests ├── FeatureTest.php ├── TestCase.php └── User.php /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [overtrue] 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: 7 | branches: [master] 8 | 9 | jobs: 10 | phpcs: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Setup PHP environment 15 | uses: shivammathur/setup-php@v2 16 | - name: Install dependencies 17 | run: composer install 18 | - name: PHPCSFixer check 19 | run: composer check-style 20 | phpunit: 21 | strategy: 22 | matrix: 23 | php_version: [8.0, 8.1] 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v2 27 | - name: Setup PHP environment 28 | uses: shivammathur/setup-php@v2 29 | with: 30 | php-version: ${{ matrix.php_version }} 31 | coverage: xdebug 32 | - name: Install dependencies 33 | run: composer install 34 | - name: PHPUnit check 35 | run: ./vendor/bin/phpunit --coverage-text 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /vendor/ 3 | composer.lock 4 | .php_cs.cache 5 | /coverage/ 6 | .phpunit.result.cache 7 | cghooks.lock 8 | 9 | .php-cs-fixer.cache 10 | -------------------------------------------------------------------------------- /.php-cs-fixer.php: -------------------------------------------------------------------------------- 1 | setRules([ 5 | '@PSR12' => true, 6 | 'binary_operator_spaces' => true, 7 | 'blank_line_after_opening_tag' => true, 8 | 'compact_nullable_typehint' => true, 9 | 'declare_equal_normalize' => true, 10 | 'lowercase_cast' => true, 11 | 'lowercase_static_reference' => true, 12 | 'new_with_braces' => true, 13 | 'no_blank_lines_after_class_opening' => true, 14 | 'no_leading_import_slash' => true, 15 | 'no_whitespace_in_blank_line' => true, 16 | 'no_unused_imports' => true, 17 | 'ordered_class_elements' => [ 18 | 'order' => [ 19 | 'use_trait', 20 | ], 21 | ], 22 | 'ordered_imports' => [ 23 | 'imports_order' => [ 24 | 'class', 25 | 'function', 26 | 'const', 27 | ], 28 | 'sort_algorithm' => 'none', 29 | ], 30 | 'return_type_declaration' => true, 31 | 'short_scalar_cast' => true, 32 | 'single_blank_line_before_namespace' => true, 33 | 'single_trait_insert_per_statement' => true, 34 | 'ternary_operator_spaces' => true, 35 | 'unary_operator_spaces' => true, 36 | 'visibility_required' => [ 37 | 'elements' => [ 38 | 'const', 39 | 'method', 40 | 'property', 41 | ], 42 | ], 43 | ]) 44 | ->setFinder( 45 | PhpCsFixer\Finder::create() 46 | ->exclude('vendor') 47 | ->in([__DIR__.'/src/', __DIR__.'/tests/']) 48 | ) 49 | ; 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 overtrue 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | > 🚨 This package was created because of this issue: laravel/passport#382 . But the new version of passport has officially solved this issue at: laravel/passport#1447, so I think we can stop needing this package. 3 | 4 | 5 | # Laravel Passport Cache Client 6 | 7 | Make [laravel/passport](https://github.com/laravel/passport) client cacheable. 8 | 9 | [![Sponsor me](https://github.com/overtrue/overtrue/blob/master/sponsor-me-button-s.svg?raw=true)](https://github.com/sponsors/overtrue) 10 | 11 | 12 | ## Installing 13 | 14 | ```shell 15 | $ composer require overtrue/laravel-passport-cache-client -vvv 16 | ``` 17 | 18 | ## Usage 19 | 20 | Thanks to Laravel's automatic package discovery mechanism, you don't need to do any additional operations. 21 | 22 | Of course, you can also control the cache strategy freely, just need to configure the following in the configuration file: 23 | 24 | **config/passport.php** 25 | ```php 26 | return [ 27 | //... 28 | 'cache' => [ 29 | // Cache key prefix 30 | 'prefix' => 'passport_', 31 | 32 | // The lifetime of passport cache(seconds). 33 | 'expires_in' => 300, 34 | 35 | // Cache tags 36 | 'tags' => [], 37 | ], 38 | ]; 39 | ``` 40 | 41 | 42 | ## :heart: Sponsor me 43 | 44 | [![Sponsor me](https://github.com/overtrue/overtrue/blob/master/sponsor-me.svg?raw=true)](https://github.com/sponsors/overtrue) 45 | 46 | 如果你喜欢我的项目并想支持它,[点击这里 :heart:](https://github.com/sponsors/overtrue) 47 | 48 | 49 | ## Project supported by JetBrains 50 | 51 | Many thanks to Jetbrains for kindly providing a license for me to work on this and other open-source projects. 52 | 53 | [![](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/overtrue) 54 | 55 | ## Contributing 56 | 57 | You can contribute in one of three ways: 58 | 59 | 1. File bug reports using the [issue tracker](https://github.com/overtrue/laravel-passport-cache-client/issues). 60 | 2. Answer questions or fix bugs on the [issue tracker](https://github.com/overtrue/laravel-passport-cache-client/issues). 61 | 3. Contribute new features or update the wiki. 62 | 63 | _The code contribution process is not very formal. You just need to make sure that you follow the PSR-0, PSR-1, and PSR-2 coding guidelines. Any new code contributions must be accompanied by unit tests where applicable._ 64 | 65 | ## License 66 | 67 | MIT 68 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "overtrue/laravel-passport-cache-client", 3 | "description": "Make laravel/passport client cacheable", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "overtrue", 8 | "email": "anzhengchao@gmail.com" 9 | } 10 | ], 11 | "require": { 12 | "laravel/framework": "^8.0|^9.0", 13 | "laravel/passport": "^10.0" 14 | }, 15 | "autoload": { 16 | "psr-4": { 17 | "Overtrue\\LaravelPassportCacheClient\\": "src" 18 | } 19 | }, 20 | "autoload-dev": { 21 | "psr-4": { 22 | "Tests\\": "tests" 23 | } 24 | }, 25 | "require-dev": { 26 | "mockery/mockery": "^1.4", 27 | "phpunit/phpunit": "^9.5", 28 | "orchestra/testbench": "^7.0", 29 | "friendsofphp/php-cs-fixer": "^3.0", 30 | "brainmaestro/composer-git-hooks": "dev-master" 31 | }, 32 | "extra": { 33 | "laravel": { 34 | "providers": [ 35 | "\\Overtrue\\LaravelPassportCacheClient\\CacheClientServiceProvider" 36 | ] 37 | }, 38 | "hooks": { 39 | "pre-commit": [ 40 | "composer check-style", 41 | "composer test" 42 | ], 43 | "pre-push": [ 44 | "composer test" 45 | ] 46 | } 47 | }, 48 | "scripts": { 49 | "post-update-cmd": [ 50 | "cghooks remove", 51 | "cghooks add --ignore-lock", 52 | "cghooks update" 53 | ], 54 | "post-merge": "composer install", 55 | "post-install-cmd": [ 56 | "cghooks remove", 57 | "cghooks add --ignore-lock", 58 | "cghooks update" 59 | ], 60 | "cghooks": "vendor/bin/cghooks", 61 | "check-style": "php-cs-fixer fix --using-cache=no --diff --dry-run --ansi", 62 | "fix-style": "php-cs-fixer fix --using-cache=no --ansi", 63 | "test": "phpunit --colors" 64 | }, 65 | "scripts-descriptions": { 66 | "test": "Run all tests.", 67 | "check-style": "Run style checks (only dry run - no fixing!).", 68 | "fix-style": "Run style checks and fix violations." 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/overtrue/laravel-passport-cache-client/b9ec82de0a30c671157aeba6ddcea8ca5d30ae97/migrations/.gitkeep -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | ./tests/ 14 | vendor 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/CacheClientRepository.php: -------------------------------------------------------------------------------- 1 | cacheKeyPrefix = sprintf('%s_client_', $cacheKey ?? 'passport'); 61 | $this->expiresInSeconds = $expiresInSeconds ?? 5 * 60; 62 | $this->cacheTags = \array_merge($tags, [$this->cacheTag]); 63 | $this->cacheStore = $store ?? \config('cache.default'); 64 | } 65 | 66 | /** 67 | * Get a client by the given ID. 68 | * 69 | * @param int $id 70 | * 71 | * @return \Laravel\Passport\Client|null 72 | */ 73 | public function find($id) 74 | { 75 | return $this->cacheStore()->remember( 76 | $this->cacheKeyForClient($id), 77 | \now()->addSeconds($this->expiresInSeconds), 78 | function () use ($id) { 79 | $client = Passport::client(); 80 | 81 | return $client->where($client->getKeyName(), $id)->first(); 82 | } 83 | ); 84 | } 85 | 86 | /** 87 | * Get a client instance for the given ID and user ID. 88 | * 89 | * @param int $clientId 90 | * @param mixed $userId 91 | * 92 | * @return \Laravel\Passport\Client|null 93 | */ 94 | public function findForUser($clientId, $userId) 95 | { 96 | return $this->cacheStore()->remember( 97 | $this->cacheKeyForUserClient($userId, $clientId), 98 | \now()->addSeconds($this->expiresInSeconds), 99 | function () use ($clientId, $userId) { 100 | $client = Passport::client(); 101 | 102 | return $client->where($client->getKeyName(), $clientId)->where('user_id', $userId)->first(); 103 | } 104 | ); 105 | } 106 | 107 | /** 108 | * Get the token instances for the given user ID. 109 | * 110 | * @param mixed $userId 111 | * 112 | * @return \Illuminate\Database\Eloquent\Collection 113 | */ 114 | public function forUser($userId): Collection 115 | { 116 | return $this->cacheStore()->remember( 117 | $this->cacheKeyForUser($userId), 118 | \now()->addSeconds($this->expiresInSeconds), 119 | function () use ($userId) { 120 | return Passport::client() 121 | ->where('user_id', $userId) 122 | ->orderBy('name', 'asc')->get(); 123 | } 124 | ); 125 | } 126 | 127 | /** 128 | * Get the personal access token client for the application. 129 | * 130 | * @return \Laravel\Passport\Client 131 | * 132 | * @throws \RuntimeException 133 | */ 134 | public function personalAccessClient() 135 | { 136 | if ($this->personalAccessClientId) { 137 | return $this->find($this->personalAccessClientId); 138 | } 139 | 140 | $client = Passport::personalAccessClient(); 141 | 142 | if (!$client->exists()) { 143 | throw new \RuntimeException('Personal access client not found. Please create one.'); 144 | } 145 | 146 | return $client->orderBy($client->getKeyName(), 'desc')->first()->client; 147 | } 148 | 149 | public function update(Client $client, $name, $redirect) 150 | { 151 | $client = parent::update($client, $name, $redirect); 152 | 153 | $this->removeClientCache($client); 154 | 155 | return $client; 156 | } 157 | 158 | public function delete(Client $client) 159 | { 160 | parent::delete($client); 161 | 162 | $this->removeClientCache($client); 163 | } 164 | 165 | protected function removeClientCache(Client $client) 166 | { 167 | $keys = [ 168 | $this->cacheKeyForClient($client->getKey()), 169 | $this->cacheKeyForUser($client->user_id), 170 | $this->cacheKeyForUserClient($client->user_id, $client->getKey()), 171 | ]; 172 | 173 | if ($this->personalAccessClientId) { 174 | $keys[] = $this->cacheKeyForClient($this->personalAccessClientId); 175 | } 176 | 177 | $this->cacheStore()->deleteMultiple($keys); 178 | } 179 | 180 | public function cacheKeyForUser($userId): string 181 | { 182 | return $this->cacheKeyPrefix .':for_user:'. $userId; 183 | } 184 | 185 | public function cacheKeyForClient(string $clientId): string 186 | { 187 | return $this->cacheKeyPrefix .':for_client:'. $clientId; 188 | } 189 | 190 | public function cacheKeyForUserClient($userId, string $clientId): string 191 | { 192 | return $this->cacheKeyPrefix .':for_user_client:'. $userId . '_' . $clientId; 193 | } 194 | 195 | public function cacheStore(): Repository 196 | { 197 | $store = Cache::store($this->cacheStore); 198 | 199 | return $store->getStore() instanceof TaggableStore ? $store->tags($this->cacheTags) : $store; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/CacheClientServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->singleton( 13 | ClientRepository::class, 14 | function ($container) { 15 | $config = $container->make('config')->get('passport.personal_access_client'); 16 | 17 | return new CacheClientRepository( 18 | $config['id'] ?? null, 19 | $config['secret'] ?? null, 20 | \config('passport.cache.prefix'), 21 | \config('passport.cache.expires_in'), 22 | \config('passport.cache.tags', []), 23 | \config('passport.cache.store', \config('cache.default')) 24 | ); 25 | } 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/FeatureTest.php: -------------------------------------------------------------------------------- 1 | withoutExceptionHandling(); 16 | 17 | $password = 'Pa55w0rd!'; 18 | $user = new User(); 19 | $user->email = 'foo@gmail.com'; 20 | $user->password = $this->app->make(Hasher::class)->make($password); 21 | $user->save(); 22 | 23 | /** @var Client $client */ 24 | $client = Client::create([ 25 | 'user_id' => null, 26 | 'name' => 'Foo', 27 | 'secret' => Str::random(40), 28 | 'redirect' => 'http://localhost/', 29 | 'personal_access_client' => false, 30 | 'password_client' => true, 31 | 'revoked' => false, 32 | ]); 33 | 34 | $response = $this->post( 35 | '/oauth/token', 36 | [ 37 | 'grant_type' => 'client_credentials', 38 | 'client_id' => $client->id, 39 | 'client_secret' => $client->secret, 40 | ] 41 | ); 42 | 43 | $this->assertInstanceOf(CacheClientRepository::class, app(ClientRepository::class)); 44 | $response->assertOk(); 45 | } 46 | 47 | public function test_it_can_cache_client() 48 | { 49 | $password = 'foobar123'; 50 | $user = new User(); 51 | $user->email = 'foo@gmail.com'; 52 | $user->password = $this->app->make(Hasher::class)->make($password); 53 | $user->save(); 54 | $repository = app(ClientRepository::class); 55 | 56 | /** @var Client $client */ 57 | $client = $repository->createPersonalAccessClient($user->id, 'Personal Token Client', 'http://localhost'); 58 | 59 | $query = $this->getQueryLog(function () use ($repository, $client) { 60 | $repository->find($client->id); 61 | $repository->find($client->id); 62 | $repository->find($client->id); 63 | $repository->find($client->id); 64 | $repository->find($client->id); 65 | }); 66 | 67 | $this->assertTrue($repository->cacheStore()->has($repository->cacheKeyForClient($client->id))); 68 | 69 | $this->assertSame('select * from "oauth_clients" where "id" = ? limit 1', $query[0]['sql']); 70 | $this->assertSame($client->getKey(), $query[0]['bindings'][0]); 71 | $this->assertCount(1, $query); 72 | } 73 | 74 | public function test_it_can_cache_client_for_user() 75 | { 76 | $password = 'foobar123'; 77 | $user = new User(); 78 | $user->email = 'foo@gmail.com'; 79 | $user->password = $this->app->make(Hasher::class)->make($password); 80 | $user->save(); 81 | $repository = app(ClientRepository::class); 82 | 83 | /** @var Client $client */ 84 | $client = $repository->createPersonalAccessClient($user->id, 'Personal Token Client', 'http://localhost'); 85 | 86 | $query = $this->getQueryLog(function () use ($repository, $user, $client) { 87 | $repository->findForUser($user->id, $client->id); 88 | }); 89 | 90 | $this->assertTrue($repository->cacheStore()->has($repository->cacheKeyForUserClient($user->id, $client->id))); 91 | 92 | $this->assertSame('select * from "oauth_clients" where "id" = ? and "user_id" = ? limit 1', $query[0]['sql']); 93 | $this->assertSame($client->getKey(), $query[0]['bindings'][0]); 94 | $this->assertCount(1, $query); 95 | 96 | $query = $this->getQueryLog(function () use ($repository, $user, $client) { 97 | $repository->findForUser($user->id, $client->id); 98 | $repository->findForUser($user->id, $client->id); 99 | $repository->findForUser($user->id, $client->id); 100 | $repository->findForUser($user->id, $client->id); 101 | $repository->findForUser($user->id, $client->id); 102 | }); 103 | 104 | $this->assertEmpty($query); 105 | } 106 | 107 | public function test_it_will_remove_cache_on_client_updated() 108 | { 109 | $password = 'foobar123'; 110 | $user = new User(); 111 | $user->email = 'foo@gmail.com'; 112 | $user->password = $this->app->make(Hasher::class)->make($password); 113 | $user->save(); 114 | $repository = app(ClientRepository::class); 115 | 116 | /** @var Client $client */ 117 | $client = $repository->createPersonalAccessClient($user->id, 'Personal Token Client', 'http://localhost'); 118 | $client2 = $repository->createPersonalAccessClient($user->id, 'Personal Token Client', 'http://localhost'); 119 | 120 | $repository->find($client->id); 121 | $repository->findForUser($user->id, $client->id); 122 | app(ClientRepository::class, [$client2->id, $client2->secret])->find($client2->id); 123 | 124 | $this->assertTrue($repository->cacheStore()->has($repository->cacheKeyForClient($client->id))); 125 | $this->assertTrue($repository->cacheStore()->has($repository->cacheKeyForClient($client2->id))); 126 | $this->assertTrue($repository->cacheStore()->has($repository->cacheKeyForUserClient($user->id, $client->id))); 127 | 128 | // update client1 129 | $repository->update($client, 'Personal Token Client 2', 'http://localhost'); 130 | $this->assertFalse($repository->cacheStore()->has($repository->cacheKeyForClient($client->id))); 131 | $this->assertFalse($repository->cacheStore()->has($repository->cacheKeyForUserClient($user->id, $client->id))); 132 | 133 | $this->assertTrue($repository->cacheStore()->has($repository->cacheKeyForClient($client2->id))); 134 | } 135 | 136 | public function test_it_can_cache_personal_client() 137 | { 138 | $password = 'foobar123'; 139 | $user = new User(); 140 | $user->email = 'foo@gmail.com'; 141 | $user->password = $this->app->make(Hasher::class)->make($password); 142 | $user->save(); 143 | 144 | 145 | /** @var Client $client */ 146 | $client = app(ClientRepository::class)->createPersonalAccessClient($user->id, 'Personal Token Client', 'http://localhost'); 147 | 148 | $repository = new CacheClientRepository($client->id, $client->secret); 149 | 150 | $query = $this->getQueryLog(function () use ($repository) { 151 | $repository->personalAccessClient(); 152 | $repository->personalAccessClient(); 153 | $repository->personalAccessClient(); 154 | $repository->personalAccessClient(); 155 | $repository->personalAccessClient(); 156 | }); 157 | $this->assertTrue($repository->cacheStore()->has($repository->cacheKeyForClient($client->id))); 158 | 159 | $this->assertSame('select * from "oauth_clients" where "id" = ? limit 1', $query[0]['sql']); 160 | $this->assertSame($client->getKey(), $query[0]['bindings'][0]); 161 | $this->assertCount(1, $query); 162 | 163 | // on updated 164 | $repository->update($client, 'Personal Token Client 2', 'http://localhost'); 165 | $this->assertFalse($repository->cacheStore()->has($repository->cacheKeyForClient($client->id))); 166 | } 167 | 168 | protected function getQueryLog(\Closure $callback): \Illuminate\Support\Collection 169 | { 170 | $sqls = \collect([]); 171 | \DB::listen(function ($query) use ($sqls) { 172 | $sqls->push(['sql' => $query->sql, 'bindings' => $query->bindings]); 173 | }); 174 | 175 | $callback(); 176 | 177 | return $sqls; 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | artisan('migrate:fresh'); 26 | 27 | Passport::routes(); 28 | 29 | @unlink(self::PUBLIC_KEY); 30 | @unlink(self::PRIVATE_KEY); 31 | 32 | $this->artisan('passport:keys'); 33 | 34 | Schema::create('users', function (Blueprint $table) { 35 | $table->increments('id'); 36 | $table->string('email')->unique(); 37 | $table->string('password'); 38 | $table->dateTime('created_at'); 39 | $table->dateTime('updated_at'); 40 | }); 41 | } 42 | 43 | protected function tearDown(): void 44 | { 45 | Schema::dropIfExists('users'); 46 | 47 | parent::tearDown(); 48 | } 49 | 50 | protected function getEnvironmentSetUp($app) 51 | { 52 | $config = $app->make(Repository::class); 53 | 54 | $config->set('auth.defaults.provider', 'users'); 55 | 56 | if (($userClass = $this->getUserClass()) !== null) { 57 | $config->set('auth.providers.users.model', $userClass); 58 | } 59 | 60 | $config->set('auth.guards.api', ['driver' => 'passport', 'provider' => 'users']); 61 | 62 | $app['config']->set('database.default', 'testbench'); 63 | 64 | $app['config']->set('cache.default', 'array'); 65 | 66 | $app['config']->set('passport.storage.database.connection', 'testbench'); 67 | 68 | $app['config']->set('database.connections.testbench', [ 69 | 'driver' => 'sqlite', 70 | 'database' => ':memory:', 71 | 'prefix' => '', 72 | ]); 73 | 74 | $app['config']->set('app.key', 'base64:L7o69H49J7GM4qVX/lfI2DOPW8fqCfw+kiahKsxL62g='); 75 | } 76 | 77 | protected function getPackageProviders($app) 78 | { 79 | return [PassportServiceProvider::class, CacheClientServiceProvider::class]; 80 | } 81 | 82 | /** 83 | * Get the Eloquent user model class name. 84 | * 85 | * @return string|null 86 | */ 87 | protected function getUserClass() 88 | { 89 | return User::class; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /tests/User.php: -------------------------------------------------------------------------------- 1 |