diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml new file mode 100644 index 0000000..18b32b3 --- /dev/null +++ b/.github/workflows/pull-requests.yml @@ -0,0 +1,12 @@ +name: pull requests + +on: + pull_request_target: + types: [opened] + +permissions: + pull-requests: write + +jobs: + uneditable: + uses: laravel/.github/.github/workflows/pull-requests.yml@main diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5d68ae5..6fecee5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -13,15 +13,21 @@ jobs: strategy: fail-fast: true matrix: - php: [7.2, 7.3, 7.4, 8.0, 8.1] - laravel: [^6.0, ^7.0, ^8.0] + php: [7.2, 7.3, 7.4, '8.0', 8.1] + laravel: [6, 7, 8, 9] exclude: - php: 7.2 - laravel: ^8.0 + laravel: 8 + - php: 7.2 + laravel: 9 + - php: 7.3 + laravel: 9 + - php: 7.4 + laravel: 9 - php: 8.1 - laravel: ^6.0 + laravel: 6 - php: 8.1 - laravel: ^7.0 + laravel: 7 name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} @@ -34,12 +40,13 @@ jobs: with: php-version: ${{ matrix.php }} extensions: dom, curl, libxml, mbstring, zip + ini-values: error_reporting=E_ALL tools: composer:v2 coverage: none - name: Install dependencies run: | - composer require "illuminate/contracts=${{ matrix.laravel }}" --no-update + composer require "illuminate/contracts=^${{ matrix.laravel }}" --no-update composer update --prefer-dist --no-interaction --no-progress - name: Execute tests diff --git a/.github/workflows/update-changelog.yml b/.github/workflows/update-changelog.yml new file mode 100644 index 0000000..1625bda --- /dev/null +++ b/.github/workflows/update-changelog.yml @@ -0,0 +1,9 @@ +name: update changelog + +on: + release: + types: [released] + +jobs: + update: + uses: laravel/.github/.github/workflows/update-changelog.yml@main diff --git a/CHANGELOG.md b/CHANGELOG.md index 71143e7..a4333e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,249 +1,282 @@ # Release Notes -## [Unreleased](https://github.com/laravel/sanctum/compare/v2.13.0...master) +## [Unreleased](https://github.com/laravel/sanctum/compare/v2.15.1...master) +## [v2.15.1](https://github.com/laravel/sanctum/compare/v2.15.0...v2.15.1) - 2022-04-08 + +### Changed + +- Added custom auth token header support by @CodesignDev in https://github.com/laravel/sanctum/pull/354 + +## [v2.15.0](https://github.com/laravel/sanctum/compare/v2.14.2...v2.15.0) - 2022-03-28 + +### Added + +- Add sanctum:prune-expired command for removing expired tokens. by @yuraplohov in https://github.com/laravel/sanctum/pull/348 + +### Fixed + +- Add exit codes to command by @driesvints in https://github.com/laravel/sanctum/pull/351 + +## [v2.14.2](https://github.com/laravel/sanctum/compare/v2.14.1...v2.14.2) - 2022-02-22 + +### Changed + +- Use config function by @taylorotwell in [commit](https://github.com/laravel/sanctum/commit/dc5d749ba9bfcfd68d8f5c272238f88bea223e66) + +## [v2.14.1](https://github.com/laravel/sanctum/compare/v2.14.0...v2.14.1) - 2022-02-15 + +### Changed + +- Add helper for current app url with port ([5702317](https://github.com/laravel/sanctum/commit/57023176c5a77d5108cee2fcadceabda00af814b)) + +## [v2.14.0 (2022-01-12)](https://github.com/laravel/sanctum/compare/v2.13.0...v2.14.0) + +### Changed + +- Laravel 9 support ([#329](https://github.com/laravel/sanctum/pull/329)) ## [v2.13.0 (2021-12-14)](https://github.com/laravel/sanctum/compare/v2.12.2...v2.13.0) ### Added -- Add an event on successful token validation ([#327](https://github.com/laravel/sanctum/pull/327), [b656bc1](https://github.com/laravel/sanctum/commit/b656bc1cc0010d0ac00f00dbc88b02a8940f8860)) +- Add an event on successful token validation ([#327](https://github.com/laravel/sanctum/pull/327), [b656bc1](https://github.com/laravel/sanctum/commit/b656bc1cc0010d0ac00f00dbc88b02a8940f8860)) ## [v2.12.2 (2021-11-16)](https://github.com/laravel/sanctum/compare/v2.12.1...v2.12.2) ### Changed -- Add guard to config ([f811d5c](https://github.com/laravel/sanctum/commit/f811d5c1e8123acf2626aa4a774a890efcc39d3f)) +- Add guard to config ([f811d5c](https://github.com/laravel/sanctum/commit/f811d5c1e8123acf2626aa4a774a890efcc39d3f)) ## [v2.12.1 (2021-10-26)](https://github.com/laravel/sanctum/compare/v2.12.0...v2.12.1) ### Changed -- Rename `CheckScopes` and `CheckForAnyScope` to `CheckAbilities` and `CheckForAnyAbility` ([#312](https://github.com/laravel/sanctum/pull/312)) +- Rename `CheckScopes` and `CheckForAnyScope` to `CheckAbilities` and `CheckForAnyAbility` ([#312](https://github.com/laravel/sanctum/pull/312)) ## [v2.12.0 (2021-10-19)](https://github.com/laravel/sanctum/compare/v2.11.4...v2.12.0) ### Added -- Add CheckScopes and CheckForAnyScope Middleware ([#310](https://github.com/laravel/sanctum/pull/310)) +- Add CheckScopes and CheckForAnyScope Middleware ([#310](https://github.com/laravel/sanctum/pull/310)) ## [v2.11.4 (2021-10-13)](https://github.com/laravel/sanctum/compare/v2.11.3...v2.11.4) ### Fixed -- Revert "fix: replace hardcoded "web" guard by `config('sanctum.guard')`" ([#309](https://github.com/laravel/sanctum/pull/309)) +- Revert "fix: replace hardcoded "web" guard by `config('sanctum.guard')`" ([#309](https://github.com/laravel/sanctum/pull/309)) ## [v2.11.3 (2021-10-12)](https://github.com/laravel/sanctum/compare/v2.11.2...v2.11.3) ### Fixed -- Replace hardcoded "web" guard by `config('sanctum.guard')` ([#307](https://github.com/laravel/sanctum/pull/307)) +- Replace hardcoded "web" guard by `config('sanctum.guard')` ([#307](https://github.com/laravel/sanctum/pull/307)) ## [v2.11.2 (2021-06-15)](https://github.com/laravel/sanctum/compare/v2.11.1...v2.11.2) ### Fixed + - Ignore updating `last_used_at` for deciding the DB connection host ([#283](https://github.com/laravel/sanctum/pull/283), [2c8b9a1](https://github.com/laravel/sanctum/commit/2c8b9a1071b87c1911ba99448d1173dd75e97c9f)) - Fix resolving wrong app instance on Octane ([#285](https://github.com/laravel/sanctum/pull/285), [#286](https://github.com/laravel/sanctum/pull/286)) - ## [v2.11.1 (2021-05-25)](https://github.com/laravel/sanctum/compare/v2.11.0...v2.11.1) ### Changed -- Only parse APP_URL for default stateful domains when it's set ([#279](https://github.com/laravel/sanctum/pull/279)) +- Only parse APP_URL for default stateful domains when it's set ([#279](https://github.com/laravel/sanctum/pull/279)) ## [v2.11.0 (2021-05-11)](https://github.com/laravel/sanctum/compare/v2.10.0...v2.11.0) ### Added -- `Sanctum::$accessTokenAuthenticationCallback` callback for more granular control over access token validation ([#275](https://github.com/laravel/sanctum/pull/275), [9c07921](https://github.com/laravel/sanctum/commit/9c079213d3e748fa0d784a17b6ef2f5cde92a286), [#276](https://github.com/laravel/sanctum/pull/276)) +- `Sanctum::$accessTokenAuthenticationCallback` callback for more granular control over access token validation ([#275](https://github.com/laravel/sanctum/pull/275), [9c07921](https://github.com/laravel/sanctum/commit/9c079213d3e748fa0d784a17b6ef2f5cde92a286), [#276](https://github.com/laravel/sanctum/pull/276)) ## [v2.10.0 (2021-04-20)](https://github.com/laravel/sanctum/compare/v2.9.4...v2.10.0) ### Added -- Add HasApiTokens contract to complement trait ([#270](https://github.com/laravel/sanctum/pull/270)) +- Add HasApiTokens contract to complement trait ([#270](https://github.com/laravel/sanctum/pull/270)) ## [v2.9.4 (2021-04-06)](https://github.com/laravel/sanctum/compare/v2.9.3...v2.9.4) ### Changed -- Use app helper ([60f2809](https://github.com/laravel/sanctum/commit/60f280995c3f878de0e6422eaacd1c30d37d263e)) +- Use app helper ([60f2809](https://github.com/laravel/sanctum/commit/60f280995c3f878de0e6422eaacd1c30d37d263e)) ## [v2.9.3 (2021-03-30)](https://github.com/laravel/sanctum/compare/v2.9.2...v2.9.3) ### Changed -- Environment APP_URL added into the default sanctum.stateful configuration ([#264](https://github.com/laravel/sanctum/pull/264)) +- Environment APP_URL added into the default sanctum.stateful configuration ([#264](https://github.com/laravel/sanctum/pull/264)) ## [v2.9.2 (2021-03-23)](https://github.com/laravel/sanctum/compare/v2.9.1...v2.9.2) ### Fixed -- Changed Primary Key will not be used in created token's plainTextToken ([#262](https://github.com/laravel/sanctum/pull/262)) +- Changed Primary Key will not be used in created token's plainTextToken ([#262](https://github.com/laravel/sanctum/pull/262)) ## [v2.9.1 (2021-03-09)](https://github.com/laravel/sanctum/compare/v2.9.0...v2.9.1) ### Fixed -- Avoid running string functions when domain is null ([#258](https://github.com/laravel/sanctum/pull/258)) +- Avoid running string functions when domain is null ([#258](https://github.com/laravel/sanctum/pull/258)) ## [v2.9.0 (2021-01-26)](https://github.com/laravel/sanctum/compare/v2.8.2...v2.9.0) ### Added + - Add multiple guard support for SPA auth ([#246](https://github.com/laravel/sanctum/pull/246), [f5695ae](https://github.com/laravel/sanctum/commit/f5695aecc547138c76bc66aaede73ba549dabdc5)) ### Fixed -- Return json response when the request expects a json ([#247](https://github.com/laravel/sanctum/pull/247)) -### Added -- Expiration Dates ([#252](https://github.com/laravel/sanctum/pull/252)) +- Return json response when the request expects a json ([#247](https://github.com/laravel/sanctum/pull/247)) ## [v2.8.2 (2020-11-24)](https://github.com/laravel/sanctum/compare/v2.8.1...v2.8.2) ### Fixed -- Fix user provider in `sanctum` guard ([#225](https://github.com/laravel/sanctum/pull/225)) +- Fix user provider in `sanctum` guard ([#225](https://github.com/laravel/sanctum/pull/225)) ## [v2.8.1 (2020-11-17)](https://github.com/laravel/sanctum/compare/v2.8.0...v2.8.1) ### Changed -- Add default nextjs address to stateful ([e86d3e0](https://github.com/laravel/sanctum/commit/e86d3e01ade3325438fe1e64ddd64ec53f828dc4)) +- Add default nextjs address to stateful ([e86d3e0](https://github.com/laravel/sanctum/commit/e86d3e01ade3325438fe1e64ddd64ec53f828dc4)) ## [v2.8.0 (2020-11-03)](https://github.com/laravel/sanctum/compare/v2.7.0...v2.8.0) ### Added -- PHP 8 Support ([#213](https://github.com/laravel/sanctum/pull/213)) +- PHP 8 Support ([#213](https://github.com/laravel/sanctum/pull/213)) ## [v2.7.0 (2020-10-20)](https://github.com/laravel/sanctum/compare/v2.6.0...v2.7.0) ### Added -- Adds origin header fallback ([#204](https://github.com/laravel/sanctum/pull/204)) +- Adds origin header fallback ([#204](https://github.com/laravel/sanctum/pull/204)) ## [v2.6.0 (2020-09-01)](https://github.com/laravel/sanctum/compare/v2.5.0...v2.6.0) ### Changed -- Shorten tokens ([#186](https://github.com/laravel/sanctum/pull/186)) +- Shorten tokens ([#186](https://github.com/laravel/sanctum/pull/186)) ## [v2.5.0 (2020-08-25)](https://github.com/laravel/sanctum/compare/v2.4.2...v2.5.0) ### Added -- Laravel 8 support ([#184](https://github.com/laravel/sanctum/pull/184)) +- Laravel 8 support ([#184](https://github.com/laravel/sanctum/pull/184)) ## [v2.4.2 (2020-06-16)](https://github.com/laravel/sanctum/compare/v2.4.1...v2.4.2) ### Fixed -- Use the correct `Str::endsWith` parameter order ([#163](https://github.com/laravel/sanctum/pull/163)) +- Use the correct `Str::endsWith` parameter order ([#163](https://github.com/laravel/sanctum/pull/163)) ## [v2.4.1 (2020-06-11)](https://github.com/laravel/sanctum/compare/v2.4.0...v2.4.1) ### Fixed -- Fix default statefull domains ([#158](https://github.com/laravel/sanctum/pull/158), [2aac713](https://github.com/laravel/sanctum/commit/2aac713ced04e6e7f046748833dea5ab4c98b621)) +- Fix default statefull domains ([#158](https://github.com/laravel/sanctum/pull/158), [2aac713](https://github.com/laravel/sanctum/commit/2aac713ced04e6e7f046748833dea5ab4c98b621)) ## [v2.4.0 (2020-06-09)](https://github.com/laravel/sanctum/compare/v2.3.3...v2.4.0) ### Added + - Added Multiple Provider Support ([#149](https://github.com/laravel/sanctum/pull/149)) ### Fixed -- Fixed Host Problem ([#155](https://github.com/laravel/sanctum/pull/155)) +- Fixed Host Problem ([#155](https://github.com/laravel/sanctum/pull/155)) ## [v2.3.3 (2020-05-26)](https://github.com/laravel/sanctum/compare/v2.3.2...v2.3.3) ### Fixed -- EncryptCookies middleware option in config/sanctum.php ([#147](https://github.com/laravel/sanctum/pull/147)) +- EncryptCookies middleware option in config/sanctum.php ([#147](https://github.com/laravel/sanctum/pull/147)) ## [v2.3.2 (2020-05-21)](https://github.com/laravel/sanctum/compare/v2.3.1...v2.3.2) ### Added -- Add routes config option ([6cf798f](https://github.com/laravel/sanctum/commit/6cf798ff69d43fb2a714986cf028b5b5fa5612f2)) +- Add routes config option ([6cf798f](https://github.com/laravel/sanctum/commit/6cf798ff69d43fb2a714986cf028b5b5fa5612f2)) ## [v2.3.1 (2020-05-12)](https://github.com/laravel/sanctum/compare/v2.3.0...v2.3.1) ### Fixed -- 419 Exception with requests without referrer ([#139](https://github.com/laravel/sanctum/pull/139)) +- 419 Exception with requests without referrer ([#139](https://github.com/laravel/sanctum/pull/139)) ## [v2.3.0 (2020-05-05)](https://github.com/laravel/sanctum/compare/v2.2.0...v2.3.0) ### Changed -- More performant tokens lookup ([#136](https://github.com/laravel/sanctum/pull/136)) +- More performant tokens lookup ([#136](https://github.com/laravel/sanctum/pull/136)) ## [v2.2.1 (2020-04-21)](https://github.com/laravel/sanctum/compare/v2.2.0...v2.2.1) ### Fixed -- No need to specify a provider ([#129](https://github.com/laravel/sanctum/pull/129)) +- No need to specify a provider ([#129](https://github.com/laravel/sanctum/pull/129)) ## [v2.2.0 (2020-04-14)](https://github.com/laravel/sanctum/compare/v2.1.2...v2.2.0) ### Added -- Allow customizing the query used to get the token ([#124](https://github.com/laravel/sanctum/pull/124)) +- Allow customizing the query used to get the token ([#124](https://github.com/laravel/sanctum/pull/124)) ## [v2.1.2 (2020-04-09)](https://github.com/laravel/sanctum/compare/v2.1.1...v2.1.2) ### Fixed -- Enhance supportsTokens check ([#123](https://github.com/laravel/sanctum/pull/123)) +- Enhance supportsTokens check ([#123](https://github.com/laravel/sanctum/pull/123)) ## [v2.1.1 (2020-04-07)](https://github.com/laravel/sanctum/compare/v2.1.0...v2.1.1) ### Fixed -- `actingAs` any ability ([#120](https://github.com/laravel/sanctum/pull/120)) +- `actingAs` any ability ([#120](https://github.com/laravel/sanctum/pull/120)) ## [v2.1.0 (2020-03-24)](https://github.com/laravel/sanctum/compare/v2.0.0...v2.1.0) ### Added -- Make the guard configurable ([#110](https://github.com/laravel/sanctum/pull/110)) +- Make the guard configurable ([#110](https://github.com/laravel/sanctum/pull/110)) ## [v2.0.0 (2020-03-20)](https://github.com/laravel/sanctum/compare/v1.0.1...v2.0.0) ### Changed -- Renamed package to Sanctum +- Renamed package to Sanctum ## [v1.0.1 (2020-03-12)](https://github.com/laravel/sanctum/compare/v1.0.0...v1.0.1) ### Fixed + - Allow localhost ip access by default ([#81](https://github.com/laravel/sanctum/pull/81)) - Update minimum Laravel version to ^6.9 ([#89](https://github.com/laravel/sanctum/pull/89)) - Fix wildcard matching ([d8de232](https://github.com/laravel/sanctum/commit/d8de2323b49e9e408c7e5e302bcad392ed0989cb), [9a66e76](https://github.com/laravel/sanctum/commit/9a66e767e203bbee83cd5fcda7ce265835468f84)) - ## [v1.0.0 (2020-03-03)](https://github.com/laravel/sanctum/compare/v0.2.1...v1.0.0) First stable release. - ## [v0.2.1 (2020-02-12)](https://github.com/laravel/sanctum/compare/v0.2.0...v0.2.1) ### Changed -- Allow .env configuration of stateful domains ([#70](https://github.com/laravel/sanctum/pull/70)) +- Allow .env configuration of stateful domains ([#70](https://github.com/laravel/sanctum/pull/70)) ## [v0.2.0 (2020-01-28)](https://github.com/laravel/sanctum/compare/v0.1.0...v0.2.0) ### Added + - Added user mocking using actingAs ([#51](https://github.com/laravel/sanctum/pull/51)) - Add a CSRF middleware config variable ([#54](https://github.com/laravel/sanctum/pull/54), [4f77acd](https://github.com/laravel/sanctum/commit/4f77acd5e60d241b0bb8196b1986e6f59946af1d), [7df454d](https://github.com/laravel/sanctum/commit/7df454d03868d4329915a4d105b067df0d0a924d)) ### Changed -- Modify PersonalAccessToken Model to be polymorphic ([#49](https://github.com/laravel/sanctum/pull/49)) +- Modify PersonalAccessToken Model to be polymorphic ([#49](https://github.com/laravel/sanctum/pull/49)) ## v0.1.0 (2020-01-20) diff --git a/composer.json b/composer.json index 86b2ed7..3ea7d31 100644 --- a/composer.json +++ b/composer.json @@ -16,13 +16,14 @@ "require": { "php": "^7.2|^8.0", "ext-json": "*", - "illuminate/contracts": "^6.9|^7.0|^8.0", - "illuminate/database": "^6.9|^7.0|^8.0", - "illuminate/support": "^6.9|^7.0|^8.0" + "illuminate/console": "^6.9|^7.0|^8.0|^9.0", + "illuminate/contracts": "^6.9|^7.0|^8.0|^9.0", + "illuminate/database": "^6.9|^7.0|^8.0|^9.0", + "illuminate/support": "^6.9|^7.0|^8.0|^9.0" }, "require-dev": { "mockery/mockery": "^1.0", - "orchestra/testbench": "^4.0|^5.0|^6.0", + "orchestra/testbench": "^4.0|^5.0|^6.0|^7.0", "phpunit/phpunit": "^8.0|^9.3" }, "autoload": { diff --git a/config/sanctum.php b/config/sanctum.php index 9281c92..529cfdc 100644 --- a/config/sanctum.php +++ b/config/sanctum.php @@ -1,5 +1,7 @@ explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf( '%s%s', 'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1', - env('APP_URL') ? ','.parse_url(env('APP_URL'), PHP_URL_HOST) : '' + Sanctum::currentApplicationUrlWithPort() ))), /* diff --git a/src/Console/Commands/PruneExpired.php b/src/Console/Commands/PruneExpired.php new file mode 100644 index 0000000..0e07bfb --- /dev/null +++ b/src/Console/Commands/PruneExpired.php @@ -0,0 +1,47 @@ +option('hours'); + + $model::where('created_at', '<', now()->subMinutes($expiration + ($hours * 60)))->delete(); + + $this->info("Tokens expired for more than {$hours} hours pruned successfully."); + + return 0; + } + + $this->warn('Expiration value not specified in configuration file.'); + + return 1; + } +} diff --git a/src/Guard.php b/src/Guard.php index d6c441a..73ac4b3 100644 --- a/src/Guard.php +++ b/src/Guard.php @@ -61,7 +61,7 @@ public function __invoke(Request $request) } } - if ($token = $request->bearerToken()) { + if ($token = $this->getTokenFromRequest($request)) { $model = Sanctum::$personalAccessTokenModel; $accessToken = $model::findToken($token); @@ -105,6 +105,21 @@ protected function supportsTokens($tokenable = null) )); } + /** + * Get the token from the request. + * + * @param \Illuminate\Http\Request $request + * @return string|null + */ + protected function getTokenFromRequest(Request $request) + { + if (is_callable(Sanctum::$accessTokenRetrievalCallback)) { + return (string) (Sanctum::$accessTokenRetrievalCallback)($request); + } + + return $request->bearerToken(); + } + /** * Determine if the provided access token is valid. * diff --git a/src/Sanctum.php b/src/Sanctum.php index 826293c..09063e4 100644 --- a/src/Sanctum.php +++ b/src/Sanctum.php @@ -13,6 +13,13 @@ class Sanctum */ public static $personalAccessTokenModel = 'Laravel\\Sanctum\\PersonalAccessToken'; + /** + * A callback that can get the token from the request. + * + * @var callable|null + */ + public static $accessTokenRetrievalCallback; + /** * A callback that can add to the validation of the access token. * @@ -27,6 +34,18 @@ class Sanctum */ public static $runsMigrations = true; + /** + * Get the current application URL from the "APP_URL" environment variable - with port. + * + * @return string + */ + public static function currentApplicationUrlWithPort() + { + $appUrl = config('app.url'); + + return $appUrl ? ','.parse_url($appUrl, PHP_URL_HOST).(parse_url($appUrl, PHP_URL_PORT) ? ':'.parse_url($appUrl, PHP_URL_PORT) : '') : ''; + } + /** * Set the current user for the application with the given abilities. * @@ -71,6 +90,17 @@ public static function usePersonalAccessTokenModel($model) static::$personalAccessTokenModel = $model; } + /** + * Specify a callback that should be used to fetch the access token from the request. + * + * @param callable $callback + * @return void + */ + public static function getAccessTokenFromRequestUsing(callable $callback) + { + static::$accessTokenRetrievalCallback = $callback; + } + /** * Specify a callback that should be used to authenticate access tokens. * diff --git a/src/SanctumServiceProvider.php b/src/SanctumServiceProvider.php index 28e1bbf..27c0072 100644 --- a/src/SanctumServiceProvider.php +++ b/src/SanctumServiceProvider.php @@ -7,6 +7,7 @@ use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; +use Laravel\Sanctum\Console\Commands\PruneExpired; use Laravel\Sanctum\Http\Controllers\CsrfCookieController; use Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful; @@ -48,6 +49,10 @@ public function boot() $this->publishes([ __DIR__.'/../config/sanctum.php' => config_path('sanctum.php'), ], 'sanctum-config'); + + $this->commands([ + PruneExpired::class, + ]); } $this->defineRoutes(); diff --git a/tests/DefaultConfigContainsAppUrlTest.php b/tests/DefaultConfigContainsAppUrlTest.php index a93faf6..d747e6e 100644 --- a/tests/DefaultConfigContainsAppUrlTest.php +++ b/tests/DefaultConfigContainsAppUrlTest.php @@ -11,6 +11,8 @@ class DefaultConfigContainsAppUrlTest extends TestCase protected function getEnvironmentSetUp($app) { putenv('APP_URL=https://www.example.com'); + $app['config']->set('app.url', 'https://www.example.com'); + $config = require __DIR__.'/../config/sanctum.php'; $app['config']->set('sanctum.stateful', $config['stateful']); @@ -28,18 +30,22 @@ public function test_default_config_contains_app_url() public function test_app_url_is_not_parsed_when_missing_from_env() { putenv('APP_URL'); + config(['app.url' => null]); $config = require __DIR__.'/../config/sanctum.php'; $this->assertNull(env('APP_URL')); $this->assertNotContains('', $config['stateful']); + + putenv('APP_URL=https://www.example.com'); + config(['app.url' => 'https://www.example.com']); } public function test_request_from_app_url_is_stateful_with_default_config() { $request = Request::create('/'); - $request->headers->set('referer', env('APP_URL')); + $request->headers->set('referer', config('app.url')); $this->assertTrue(EnsureFrontendRequestsAreStateful::fromFrontend($request)); } diff --git a/tests/GuardTest.php b/tests/GuardTest.php index 65f299f..d0831b8 100644 --- a/tests/GuardTest.php +++ b/tests/GuardTest.php @@ -360,6 +360,139 @@ public function test_authentication_fails_if_callback_returns_false() $user = $requestGuard->setRequest($request)->user(); $this->assertNull($user); + + Sanctum::$accessTokenAuthenticationCallback = null; + } + + public function test_authentication_is_successful_with_token_in_custom_header() + { + $this->loadLaravelMigrations(['--database' => 'testbench']); + $this->artisan('migrate', ['--database' => 'testbench'])->run(); + + $factory = Mockery::mock(AuthFactory::class); + + $guard = new Guard($factory, null); + + $webGuard = Mockery::mock(stdClass::class); + + $factory->shouldReceive('guard') + ->with('web') + ->andReturn($webGuard); + + $webGuard->shouldReceive('user')->once()->andReturn(null); + + $request = Request::create('/', 'GET'); + $request->headers->set('X-Auth-Token', 'test'); + + $user = User::forceCreate([ + 'name' => 'Taylor Otwell', + 'email' => 'taylor@laravel.com', + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', + 'remember_token' => Str::random(10), + ]); + + $token = PersonalAccessToken::forceCreate([ + 'tokenable_id' => $user->id, + 'tokenable_type' => get_class($user), + 'name' => 'Test', + 'token' => hash('sha256', 'test'), + ]); + + Sanctum::getAccessTokenFromRequestUsing(function (Request $request) { + return $request->header('X-Auth-Token'); + }); + + $returnedUser = $guard->__invoke($request); + + $this->assertEquals($user->id, $returnedUser->id); + $this->assertEquals($token->id, $returnedUser->currentAccessToken()->id); + $this->assertInstanceOf(DateTimeInterface::class, $returnedUser->currentAccessToken()->last_used_at); + + Sanctum::$accessTokenRetrievalCallback = null; + } + + public function test_authentication_fails_with_token_in_authorization_header_when_using_custom_header() + { + $this->loadLaravelMigrations(['--database' => 'testbench']); + $this->artisan('migrate', ['--database' => 'testbench'])->run(); + + $factory = Mockery::mock(AuthFactory::class); + + $guard = new Guard($factory, null); + + $webGuard = Mockery::mock(stdClass::class); + + $factory->shouldReceive('guard') + ->with('web') + ->andReturn($webGuard); + + $webGuard->shouldReceive('user')->once()->andReturn(null); + + $request = Request::create('/', 'GET'); + $request->headers->set('Authorization', 'Bearer test'); + + $user = User::forceCreate([ + 'name' => 'Taylor Otwell', + 'email' => 'taylor@laravel.com', + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', + 'remember_token' => Str::random(10), + ]); + + $token = PersonalAccessToken::forceCreate([ + 'tokenable_id' => $user->id, + 'tokenable_type' => get_class($user), + 'name' => 'Test', + 'token' => hash('sha256', 'test'), + ]); + + Sanctum::getAccessTokenFromRequestUsing(function (Request $request) { + return $request->header('X-Auth-Token'); + }); + + $returnedUser = $guard->__invoke($request); + + $this->assertNull($returnedUser); + + Sanctum::$accessTokenRetrievalCallback = null; + } + + public function test_authentication_fails_with_token_in_custom_header_when_using_default_authorization_header() + { + $this->loadLaravelMigrations(['--database' => 'testbench']); + $this->artisan('migrate', ['--database' => 'testbench'])->run(); + + $factory = Mockery::mock(AuthFactory::class); + + $guard = new Guard($factory, null); + + $webGuard = Mockery::mock(stdClass::class); + + $factory->shouldReceive('guard') + ->with('web') + ->andReturn($webGuard); + + $webGuard->shouldReceive('user')->once()->andReturn(null); + + $request = Request::create('/', 'GET'); + $request->headers->set('X-Auth-Token', 'test'); + + $user = User::forceCreate([ + 'name' => 'Taylor Otwell', + 'email' => 'taylor@laravel.com', + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', + 'remember_token' => Str::random(10), + ]); + + $token = PersonalAccessToken::forceCreate([ + 'tokenable_id' => $user->id, + 'tokenable_type' => get_class($user), + 'name' => 'Test', + 'token' => hash('sha256', 'test'), + ]); + + $returnedUser = $guard->__invoke($request); + + $this->assertNull($returnedUser); } protected function getPackageProviders($app) diff --git a/tests/PruneExpiredTest.php b/tests/PruneExpiredTest.php new file mode 100644 index 0000000..b28b08a --- /dev/null +++ b/tests/PruneExpiredTest.php @@ -0,0 +1,108 @@ +set('database.default', 'testbench'); + + $app['config']->set('database.connections.testbench', [ + 'driver' => 'sqlite', + 'database' => ':memory:', + 'prefix' => '', + ]); + } + + public function test_can_delete_expired_tokens_with_integer_expiration() + { + $this->loadLaravelMigrations(['--database' => 'testbench']); + $this->artisan('migrate', ['--database' => 'testbench'])->run(); + + config(['sanctum.expiration' => 60]); + + $user = UserForPruneExpiredTest::forceCreate([ + 'name' => 'Taylor Otwell', + 'email' => 'taylor@laravel.com', + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', + ]); + + $token_1 = PersonalAccessToken::forceCreate([ + 'tokenable_id' => $user->id, + 'tokenable_type' => get_class($user), + 'name' => 'Test_1', + 'token' => hash('sha256', 'test_1'), + 'created_at' => now()->subMinutes(181), + ]); + + $token_2 = PersonalAccessToken::forceCreate([ + 'tokenable_id' => $user->id, + 'tokenable_type' => get_class($user), + 'name' => 'Test_2', + 'token' => hash('sha256', 'test_2'), + 'created_at' => now()->subMinutes(179), + ]); + + $token_3 = PersonalAccessToken::forceCreate([ + 'tokenable_id' => $user->id, + 'tokenable_type' => get_class($user), + 'name' => 'Test_3', + 'token' => hash('sha256', 'test_3'), + 'created_at' => now()->subMinutes(121), + ]); + + $this->artisan('sanctum:prune-expired --hours=2') + ->expectsOutput('Tokens expired for more than 2 hours pruned successfully.'); + + $this->assertDatabaseMissing('personal_access_tokens', ['name' => 'Test_1']); + $this->assertDatabaseHas('personal_access_tokens', ['name' => 'Test_2']); + $this->assertDatabaseHas('personal_access_tokens', ['name' => 'Test_3']); + } + + public function test_cant_delete_expired_tokens_with_null_expiration() + { + $this->loadLaravelMigrations(['--database' => 'testbench']); + $this->artisan('migrate', ['--database' => 'testbench'])->run(); + + config(['sanctum.expiration' => null]); + + $user = UserForPruneExpiredTest::forceCreate([ + 'name' => 'Taylor Otwell', + 'email' => 'taylor@laravel.com', + 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', + ]); + + $token = PersonalAccessToken::forceCreate([ + 'tokenable_id' => $user->id, + 'tokenable_type' => get_class($user), + 'name' => 'Test', + 'token' => hash('sha256', 'test'), + 'created_at' => now()->subMinutes(70), + ]); + + $this->artisan('sanctum:prune-expired --hours=2') + ->expectsOutput('Expiration value not specified in configuration file.'); + + $this->assertDatabaseHas('personal_access_tokens', ['name' => 'Test']); + } + + protected function getPackageProviders($app) + { + return [SanctumServiceProvider::class]; + } +} + +class UserForPruneExpiredTest extends User implements HasApiTokensContract +{ + use HasApiTokens; + + protected $table = 'users'; +}