Repository: olssonm/l5-very-basic-auth Branch: master Commit: 59ae0468eb07 Files: 21 Total size: 36.1 KB Directory structure: gitextract_4ggz17vw/ ├── .editorconfig ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ └── test.yml ├── .gitignore ├── LICENSE.md ├── README.jp.md ├── README.md ├── composer.json ├── phpstan.neon ├── phpunit.xml ├── src/ │ ├── Console/ │ │ └── GeneratePassword.php │ ├── Handlers/ │ │ ├── DefaultResponseHandler.php │ │ └── ResponseHandler.php │ ├── Http/ │ │ └── Middleware/ │ │ └── VeryBasicAuth.php │ ├── VeryBasicAuthServiceProvider.php │ ├── config.php │ └── resources/ │ └── views/ │ └── default.blade.php └── tests/ ├── Fixtures/ │ └── CustomResponseHandler.php ├── Pest.php ├── TestCase.php └── VeryBasicAuthTest.php ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ ; This file is for unifying the coding style for different editors and IDEs. ; More information at http://editorconfig.org root = true [*] charset = utf-8 indent_size = 4 indent_style = space end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [*.md] trim_trailing_whitespace = false [*.{yml,yaml}] indent_size = 2 ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: [olssonm] ================================================ FILE: .github/workflows/test.yml ================================================ name: Run tests on: push: pull_request: jobs: php-tests: runs-on: ubuntu-latest strategy: matrix: php: [8.5, 8.4, 8.3, 8.2] laravel: ['12.*', '13.*'] exclude: - laravel: 13.* php: 8.2 - laravel: 12.* php: 8.5 name: P${{ matrix.php }} - L${{ matrix.laravel }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} extensions: dom, curl, libxml, mbstring, zip tools: composer:v2 coverage: none - name: Install dependencies run: | composer require "illuminate/support:${{ matrix.laravel }}" --no-interaction --no-progress --no-suggest composer update --prefer-dist --no-interaction --no-progress - name: Execute tests run: composer test ================================================ FILE: .gitignore ================================================ /vendor composer.lock .DS_Store .phpunit.result.cache .phpunit.cache phpunit-output ================================================ FILE: LICENSE.md ================================================ # The MIT License (MIT) Copyright (c) 2024 Marcus Olsson > Permission is hereby granted, free of charge, to any person obtaining a copy > of this software and associated documentation files (the "Software"), to deal > in the Software without restriction, including without limitation the rights > to use, copy, modify, merge, publish, distribute, sublicense, and/or sell > copies of the Software, and to permit persons to whom the Software is > furnished to do so, subject to the following conditions: > > The above copyright notice and this permission notice shall be included in > all copies or substantial portions of the Software. > > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR > IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, > FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE > AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER > LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, > OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN > THE SOFTWARE. ================================================ FILE: README.jp.md ================================================ # Laravel Very Basic Auth [![Latest Version on Packagist][ico-version]][link-packagist] [![Total downloads][ico-downloads]][link-packagist] [![Software License][ico-license]](LICENSE.md) [![Build Status][ico-build]][link-build] ![very-basic-auth](https://user-images.githubusercontent.com/907114/40575964-331559ce-60ef-11e8-8366-aba700fc5567.png) **利用可能なドキュメントは以下です:** 🇬🇧 [English](README.md) 🇯🇵 [日本語](README.jp.md) Laravel 5 Very Basic AuthはLaravel標準の`auth.basic`とは違い、実際のデータベースの情報を使うことなくBasic認証を追加します。 Screenshot 例えば、開発中のサイトにユーザーをアクセスさせたい時や、まだデータベースやモデルを用意していない時に使うと便利です。あなたのサイトがデータベースを利用していない場合でも、アクセスを制御することができます。 認証に失敗した場合には、"401 Unauthorized"のレスポンスを返します。 #### 注意点 Basic認証は望まないユーザーからのアクセスを排除することができますが、ブルートフォース攻撃に対しては厳密には安全ではありません。もしこのパッケージをセキュリティのために単独で利用するのであれば、ログインの試行回数を制限するために、少なくともApacheかNginxのrate-limitersを確認するべきです。 ## インストール Composer経由 ``` bash $ composer require olssonm/l5-very-basic-auth ``` このパッケージのv4.* (for Laravel 5.5)以降では、サービスプロバイダーからパッケージを読み込むのに、パッケージのオートディスカバリーを使用しています。パッケージをインストールすると、以下のメッセージが表示されるはずです。 ``` Discovered Package: olssonm/l5-very-basic-auth ``` もしも手動でプロバイダーに追加したい場合は、composer.jsonファイルでオートディスカバリーを切って、 ``` json "extra": { "laravel": { "dont-discover": [ "olssonm/l5-very-basic-auth" ] } }, ``` (`config/app.php`)のprovidersにプロバイダーを追加してください。 ``` php 'providers' => [ Olssonm\VeryBasicAuth\VeryBasicAuthServiceProvider::class ] ``` ## 設定 `$ php artisan vendor:publish`のコマンドを実行し、`Provider: Olssonm\VeryBasicAuth\VeryBasicAuthServiceProvider`を選んで設定ファイルを公開してください。`$ php artisan vendor:publish --provider="Olssonm\VeryBasicAuth\VeryBasicAuthServiceProvider"`でも設定ファイルを公開することができます。 `very_basic_auth.php`のファイルがあなたの`app/config`ディレクトリにコピーされます。ここにusernameやpasswordなどの幾つかの設定を置くことができます。 ### 注意 **デフォルトのパスワードはありません。** セキュリティのために(誰もが同じパスワードになってしまわないように)、インストール時にランダムなパスワードが設定されます。個別のパスワードを設定するためにもパッケージ設定の公開をして下さい。 #### ビューとメッセージ `very_basic_auth.php`ファイルでは、メッセージの代わりにカスタマイズしたビューを設定することができます。 ``` php // ユーザーがオプトアウトするか、キャンセルを押した場合に表示されるメッセージ 'error_message' => 'You have to supply your credentials to access this resource.', // エラーメッセージの代わりにviewを使いたい場合は"error_view"のコメントアウトを外して下さい。 // この場合、あなたのデフォルトのレスポンスメッセージよりもエラービューが優先されます。 // 'error_view' => 'very_basic_auth::default' ``` `error_view`のコメントアウトを外した場合、ミドルウェアは指定されたviewを探そうとします。このビュー名は通常と同じように`.blade.php`の拡張子無しで設定してください。 *以前のバージョンから2.1にアップグレードする場合、このkeyとvalueは公開された設定には存在しないので、自分自身で設定を追加する必要があります。* ## 使い方 このミドルウェアはルートを保護するのに`auth.very_basic`の短縮キーを使います。`Route::group()`に適用して複数のルートを保護することもできますし、個別に保護するルートを選ぶこともできます。 **グループを使う場合** ``` php Route::group(['middleware' => 'auth.very_basic'], function() { Route::get('/', ['as' => 'start', 'uses' => 'StartController@index']); Route::get('/page', ['as' => 'page', 'uses' => 'StartController@page']); }); ``` **単独で使う場合** ``` php Route::get('/', [ 'as' => 'start', 'uses' => 'StartController@index', 'middleware' => 'auth.very_basic' ]); ``` 認証情報をルート上に記述することもできます。 ``` php Route::get('/', [ 'as' => 'start', 'uses' => 'StartController@index', 'middleware' => 'auth.very_basic:username,password' ]); ``` *Note:* 認証情報をルート上に記述した場合、設定ファイルの`very_basic_auth.php`より優先されます。 ## テスト ``` bash $ composer test ``` または ``` bash $ phpunit ``` テストを実行する際は、Laravelは常にenvironmentの値を"testing"にします。`testing`が`very_basic_auth.php`の`envs`配列内に存在することを確認して下さい。 ## ライセンス MITライセンスです。 詳しくはこちらを見てください。[License File](LICENSE.md) © 2024 [Marcus Olsson](https://marcusolsson.me). [ico-version]: https://img.shields.io/packagist/v/olssonm/l5-very-basic-auth.svg?style=flat-square [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square [ico-build]: https://img.shields.io/github/workflow/status/olssonm/l5-very-basic-auth/Run%20tests.svg?style=flat-square&label=tests [ico-downloads]: https://img.shields.io/packagist/dt/olssonm/l5-very-basic-auth.svg?style=flat-square [link-packagist]: https://packagist.org/packages/olssonm/l5-very-basic-auth [link-build]: https://github.com/olssonm/l5-very-basic-auth/actions?query=workflow%3A%22Run+tests%22 ================================================ FILE: README.md ================================================ # Laravel Very Basic Auth [![Latest Version on Packagist][ico-version]][link-packagist] [![Total downloads][ico-downloads]][link-packagist] [![Software License][ico-license]](LICENSE.md) [![Build Status][ico-build]][link-build] ![very-basic-auth](https://user-images.githubusercontent.com/907114/40575964-331559ce-60ef-11e8-8366-aba700fc5567.png) **Documentation available in:** 🇬🇧 [English](README.md) 🇯🇵 [日本語](README.jp.md) This package allows you to add a HTTP Basic Auth filter on your routes, without the need to use a database – which the Laravel default `auth.basic`-middleware relies on. Screenshot Perfect when you want to give your clients access to your development site before you have yet to set up your database and/or models. Or perhaps your site doesn't even use a database and you still wish to keep it protected. On failed authentication the user will get a "401 Unauthorized" response. #### A thing to note While HTTP Basic Auth does give you a protection layer against unwanted visitors, it is still not strictly safe from brute-force attacks. If you are solely using this package for security, you should at least consider looking into Apache or Nginx rate-limiters to limit login attempts. ## Installation Via Composer ``` bash $ composer require olssonm/l5-very-basic-auth ``` Since v4.* (for Laravel 5.5) this package uses Package Auto-Discovery for loading the service provider. Once installed you should see the message ``` Discovered Package: olssonm/l5-very-basic-auth ``` If you would like to manually add the provider, turn off Auto-Discovery for the package in your composer.json-file: ``` json "extra": { "laravel": { "dont-discover": [ "olssonm/l5-very-basic-auth" ] } }, ``` And then add the provider in the providers array (`config/app.php`). ``` php 'providers' => [ Olssonm\VeryBasicAuth\VeryBasicAuthServiceProvider::class ] ``` ## Configuration Run the command `$ php artisan vendor:publish` and select `Provider: Olssonm\VeryBasicAuth\VeryBasicAuthServiceProvider` to publish the configuration. You could also type `$ php artisan vendor:publish --provider="Olssonm\VeryBasicAuth\VeryBasicAuthServiceProvider"` to directly publish the files. The file `very_basic_auth.php` will then be copied to your `app/config`-folder – here you can set various options such as username and password. #### Note **There is no default password**. Upon installation you will need to set your own username and password. Please publish the packages configuration to have the ability to set these. **If left empty, basic auth will not be active**. ### Environments You may set the environments that the package should be applied for. You may simply use "`*`" to use in all environments (this is also the default). ``` php 'envs' => [ '*' ], ``` Or ``` php 'envs' => [ 'production', 'development', 'local' ], ``` ### Response handlers When the authentication fails the response handler sends out an error response (see "Views and messages" for more about these options). By default the handler will be `\Olssonm\VeryBasicAuth\Handlers\DefaultResponseHandler` (see `response_handler` in `very_basic_auth.php`). You may however write your own response-logic if you so choose. The only requirement is that it implements the `\Olssonm\VeryBasicAuth\Handlers\ResponseHandler`-interface, and has an `__invoke`-method that accepts a request-object, like so: ``` php use Illuminate\Http\Request; use Olssonm\VeryBasicAuth\Handlers\ResponseHandler; class CustomResponseHandler implements ResponseHandler { public function __invoke(Request $request) { // Do some stuff return response('Custom response', 401); } } ``` ### Views and messages In the `very_basic_auth.php`-configuration you have the ability to set a custom view instead of a message. ``` php // Message to display if the user "opts out"/clicks "cancel" 'error_message' => 'You have to supply your credentials to access this resource.', // If you prefer to use a view with your error message you can uncomment "error_view". // This will supersede your default response message // 'error_view' => 'very_basic_auth::default' ``` If you uncomment `error_view`, the middleware will try to find your specified view. You supply this value as usual (without the `.blade.php`-extention). ## Usage The middleware uses the `auth.very_basic`-filter to protect routes. You can either use `Route::group()` to protect multiple routes, or chose just to protect them individually. **Group** ``` php Route::group(['middleware' => 'auth.very_basic'], function() { Route::get('/', ['as' => 'start', 'uses' => 'StartController@index']); Route::get('/page', ['as' => 'page', 'uses' => 'StartController@page']); }); ``` **Single** ``` php Route::get('/', [ 'as' => 'start', 'uses' => 'StartController@index', 'middleware' => 'auth.very_basic' ]); ``` You may also set the credentials inline; ``` php Route::get('/', [ 'as' => 'start', 'uses' => 'StartController@index', 'middleware' => 'auth.very_basic:username,password' ]); ``` *Note:* inline credentials always take president over the `very_basic_auth.php`-configuration file. ### Generating hash for password If you want to generate a hash for your password, you can use the `artisan`-command: ``` bash php artisan very-basic-auth:generate-password ``` It will ask you for a password, and then automatically insert the hash into your `.env`-file ## Testing ``` bash $ composer test ``` or ``` bash $ phpunit ``` Laravel always runs in the "testing" environment while running tests. Make sure that `testing` is set in the `envs`-array in `very_basic_auth.php`. ## Thank you A big thank you to the people who has contributed to this package, among others: **[kazuhei](https://github.com/kazuhei)** – for providing the awesome Japanese translation **[freekmurze](https://github.com/freekmurze)** – for additional information on package/vendor installations **[faiare](https://github.com/faiare)** – for pointing out and implementing the `realm`-attribute ([RFC7235](https://tools.ietf.org/html/rfc7235#section-2.2)) ## License The MIT License (MIT). Please see [License File](LICENSE.md) for more information. © 2024 [Marcus Olsson](https://marcusolsson.me). [ico-version]: https://img.shields.io/packagist/v/olssonm/l5-very-basic-auth.svg?style=flat-square [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square [ico-build]: https://img.shields.io/github/actions/workflow/status/olssonm/l5-very-basic-auth/test.yml?branch=master&style=flat-square [ico-downloads]: https://img.shields.io/packagist/dt/olssonm/l5-very-basic-auth.svg?style=flat-square [link-packagist]: https://packagist.org/packages/olssonm/l5-very-basic-auth [link-build]: https://github.com/olssonm/l5-very-basic-auth/actions?query=workflow%3A%22Run+tests%22 ================================================ FILE: composer.json ================================================ { "name": "olssonm/l5-very-basic-auth", "description": "Laravel stateless HTTP basic auth without the need for a database", "license": "MIT", "keywords": [ "olssonm", "laravel", "authentication", "http basic auth" ], "authors": [ { "name": "Marcus Olsson", "email": "contact@marcusolsson.me", "homepage": "https://marcusolsson.me" } ], "homepage": "https://github.com/olssonm/l5-very-basic-auth", "require": { "php": "^8.2", "illuminate/support": "^12.0 || ^13.0" }, "require-dev": { "laravel/helpers": "^1.1", "orchestra/testbench": "^10.0 || ^11.0", "pestphp/pest": "^3.7 || ^4.4", "pestphp/pest-plugin-laravel": "^3.1 || ^4.1", "phpstan/phpstan": "^2.0", "phpunit/phpunit": "^11.5.3 || ^12.5.12", "squizlabs/php_codesniffer": "^3.5 || ^4.0" }, "minimum-stability": "dev", "prefer-stable": true, "autoload": { "psr-4": { "Olssonm\\VeryBasicAuth\\": "src" } }, "autoload-dev": { "psr-4": { "Olssonm\\VeryBasicAuth\\Tests\\": "tests" } }, "config": { "allow-plugins": { "pestphp/pest-plugin": true }, "sort-packages": true }, "extra": { "branch-alias": { "dev-master": "11.x-dev" }, "laravel": { "providers": [ "Olssonm\\VeryBasicAuth\\VeryBasicAuthServiceProvider" ] } }, "scripts": { "phpfix": "vendor/bin/phpcbf --standard=\"PSR12\" ./src --ignore=./src/resources/*", "phpsniff": "vendor/bin/phpcs --standard=\"PSR12\" ./src --ignore=./src/resources/*", "phpstan": "./vendor/bin/phpstan", "test": "vendor/bin/pest" } } ================================================ FILE: phpstan.neon ================================================ parameters: level: 8 paths: - src ignoreErrors: - identifier: missingType.generics ================================================ FILE: phpunit.xml ================================================ tests src src ================================================ FILE: src/Console/GeneratePassword.php ================================================ promptForValidPassword(); if ($this->writeNewEnvironmentFileWith($this->hashPassword($password))) { $this->info('The password has been set successfully.'); } return static::SUCCESS; } /** * Keep asking for a password until a valid one is provided. */ protected function promptForValidPassword(): string { while (true) { $password = (string) $this->secret('Please enter a password for the very basic auth'); if (!$this->isPasswordAccepted($password)) { $this->error('The password must be at least 8 characters.'); continue; } if (!$this->passwordsMatch($password)) { $this->error('The passwords do not match. Please try again.'); continue; } return $password; } } /** * Determine if the provided password fulfills minimum requirements. */ protected function isPasswordAccepted(?string $password): bool { return !empty($password) && strlen($password) >= 8; } /** * Ask for confirmation and ensure it matches. */ protected function passwordsMatch(string $password): bool { return $password === (string) $this->secret('Please confirm your password'); } /** * Hash the password using Laravel's configured hasher. */ protected function hashPassword(string $password): string { return app()->make('hash')->make($password); } /** * Write a new environment file with the given password. * * @param string $password * @return bool */ protected function writeNewEnvironmentFileWith(string $password): bool { $envPath = $this->laravel->environmentFilePath(); $input = file_get_contents($envPath); $replaced = preg_replace_callback( $this->passwordReplacementPattern(), fn($matches) => 'BASIC_AUTH_PASSWORD="' . $password . '"', $input ); if ($replaced === $input || $replaced === null) { $this->error('Unable to set password. No BASIC_AUTH_PASSWORD variable was found in the .env file. Please add it first'); return false; } file_put_contents($envPath, $replaced); return true; } /** * Get a regex pattern that will match env BASIC_AUTH_PASSWORD with any password. * * @return string */ protected function passwordReplacementPattern(): string { return '/^BASIC_AUTH_PASSWORD=.*$/m'; } } ================================================ FILE: src/Handlers/DefaultResponseHandler.php ================================================ sprintf( 'Basic realm="%s", charset="UTF-8"', config('very_basic_auth.realm', 'Basic Auth') ), ]; // View $view = config('very_basic_auth.error_view'); // If the request want's JSON, else view if ($request->wantsJson()) { return response()->json([ 'message' => config('very_basic_auth.error_message'), ], 401, $header); } elseif (isset($view)) { return response()->view($view, [], 401) ->withHeaders($header); } // Return default message return response()->make(config('very_basic_auth.error_message'), 401, $header); } } ================================================ FILE: src/Handlers/ResponseHandler.php ================================================ responseHandler = $responseHandler; } /** * Handle an incoming request * * @param mixed $username * @param mixed $password */ public function handle(Request $request, Closure $next, $username = null, $password = null): Response { $active = (count(array_intersect([ '*', app()->environment(), ], config('very_basic_auth.envs'))) > 0); // Check if middleware is in use in current environment if ($active) { $authUsername = $username ?? config('very_basic_auth.user'); $authPassword = $password ?? config('very_basic_auth.password'); if (! $authUsername && ! $authPassword) { return $next($request); } $plainPassword = $request->getPassword(); $isCorrectPassword = $plainPassword === $authPassword; if (! $isCorrectPassword) { try { $isCorrectPassword = Hash::check($plainPassword, $authPassword); } catch (\Throwable $e) { // If the password is not hashed, we will just return false $isCorrectPassword = false; } } if ($request->getUser() !== $authUsername || !$isCorrectPassword) { return $this->deniedResponse($request); } } return $next($request); } /** * Return a error response * * @return \Illuminate\Http\Response|\Illuminate\Http\JsonResponse */ private function deniedResponse(Request $request): Response|JsonResponse { return ($this->responseHandler)($request); } } ================================================ FILE: src/VeryBasicAuthServiceProvider.php ================================================ config = __DIR__ . '/config.php'; parent::__construct($app); } /** * Perform post-registration booting of services. * * @return void */ public function boot(\Illuminate\Routing\Router $router) { // Publishing of configuration $this->publishes([ $this->config => config_path('very_basic_auth.php'), ]); // Load default view/s $this->loadViewsFrom(__DIR__ . '/resources/views', 'very_basic_auth'); // Register middleware $router->aliasMiddleware('auth.very_basic', \Olssonm\VeryBasicAuth\Http\Middleware\VeryBasicAuth::class); // Register commands if ($this->app->runningInConsole()) { $this->commands([ GeneratePassword::class, ]); } } /** * Register any package services. * * @return void */ public function register() { // If the user doesn't set their own config, load default $this->mergeConfigFrom( $this->config, 'very_basic_auth' ); $this->app->bind( ResponseHandler::class, config('very_basic_auth.response_handler', DefaultResponseHandler::class) ); } } ================================================ FILE: src/config.php ================================================ env('BASIC_AUTH_USERNAME', ''), // Password 'password' => env('BASIC_AUTH_PASSWORD', ''), // Environments where the middleware is active. Use "*" to protect all envs 'envs' => [ '*', ], // Response handler for the error responses 'response_handler' => \Olssonm\VeryBasicAuth\Handlers\DefaultResponseHandler::class, // Message to display if the user "opts out"/clicks "cancel" 'error_message' => 'You have to supply your credentials to access this resource.', // Message to display in the auth dialog in some browsers (mainly Internet Explorer). // Realm is also used to define a "space" that should share credentials. 'realm' => 'Basic Auth', // If you prefer to use a view with your error message you can uncomment "error_view". // This will supersede your default response message // 'error_view' => 'very_basic_auth::default' ]; ================================================ FILE: src/resources/views/default.blade.php ================================================ Error

This is the default view for the olssonm/l5-very-basic-auth-package.

To customize this, simply edit your very_basic_auth-configuration file and set error_view to your own custom blade-template (the default is very_basic_auth::default).

================================================ FILE: tests/Fixtures/CustomResponseHandler.php ================================================ beforeEach(function () { // Set default config for testing config()->set('very_basic_auth.user', 'test'); config()->set('very_basic_auth.password', 'test'); Route::get('/', fn () => 'ok')->middleware(VeryBasicAuth::class)->name('default'); Route::get('/test', fn () => 'ok')->middleware(VeryBasicAuth::class); Route::get('/inline', fn () => 'ok')->middleware( sprintf('auth.very_basic:%s,%s', config('very_basic_auth.user'), config('very_basic_auth.password')) )->name('inline'); }) ->in(__DIR__); ================================================ FILE: tests/TestCase.php ================================================ app->router->getMiddleware()))->toBeTrue(); expect(array_key_exists('auth.very_basic', $this->app->router->getMiddleware())); }); test('config file is installed', function () { expect(file_exists(__DIR__.'/../src/config.php'))->toBeTrue(); }); test('install package', function () { $this->artisan('vendor:publish', [ '--provider' => 'Olssonm\VeryBasicAuth\VeryBasicAuthServiceProvider', ])->assertExitCode(0); expect(file_exists(config_path('very_basic_auth.php')))->toBeTrue(); }); test('request with no credentials and no config passes', function () { config()->set('very_basic_auth.user', ''); config()->set('very_basic_auth.password', ''); $response = get('/'); expect($response->getStatusCode())->toEqual(200); expect($response->headers->get('WWW-Authenticate'))->toEqual(null); }); test('request with no credentials fails', function () { $response = get('/'); expect($response->getStatusCode())->toEqual(401); expect($response->headers->get('WWW-Authenticate'))->toEqual(sprintf('Basic realm="%s", charset="UTF-8"', config('very_basic_auth.realm'))); expect($response->getContent())->toEqual(config('very_basic_auth.error_message')); }); test('request with incorrect credentials fails - text/html', function () { $response = withHeaders([ 'PHP_AUTH_USER' => str_random(20), 'PHP_AUTH_PW' => str_random(20), ])->get('/'); expect($response->getStatusCode())->toEqual(401); expect(requestHeader($response))->toEqual('text/html; charset=utf-8'); expect($response->headers->get('WWW-Authenticate'))->toEqual(sprintf('Basic realm="%s", charset="UTF-8"', config('very_basic_auth.realm'))); expect($response->getContent())->toEqual(config('very_basic_auth.error_message')); }); test('request with incorrect credentials fails - hashed password', function () { config()->set('very_basic_auth.user', 'test'); config()->set('very_basic_auth.password', app()->make('hash')->make('test')); $response = withHeaders([ 'PHP_AUTH_USER' => str_random(20), 'PHP_AUTH_PW' => str_random(20), ])->get('/'); expect($response->getStatusCode())->toEqual(401); }); test('request with incorrect credentials fails - json', function () { $response = withHeaders([ 'PHP_AUTH_USER' => str_random(20), 'PHP_AUTH_PW' => str_random(20), 'Accept' => 'application/json', ])->get('/'); $content = json_decode($response->getContent()); expect($response->getStatusCode())->toEqual(401); expect($response->headers->get('content-type'))->toEqual('application/json'); expect(json_last_error())->toEqual(JSON_ERROR_NONE); expect($content->message)->toEqual(config('very_basic_auth.error_message')); expect($response->headers->get('WWW-Authenticate'))->toEqual(sprintf('Basic realm="%s", charset="UTF-8"', config('very_basic_auth.realm'))); }); test('request with incorrect credentials fails - view', function () { config()->set('very_basic_auth.error_view', 'very_basic_auth::default'); $response = withHeaders([ 'PHP_AUTH_USER' => str_random(20), 'PHP_AUTH_PW' => str_random(20), ])->get('/'); expect($response->getStatusCode())->toEqual(401); expect(requestHeader($response))->toEqual('text/html; charset=utf-8'); expect($response->headers->get('WWW-Authenticate'))->toEqual(sprintf('Basic realm="%s", charset="UTF-8"', config('very_basic_auth.realm'))); $this->assertStringContainsStringIgnoringCase('This is the default view for the olssonm/l5-very-basic-auth-package', $response->getContent()); }); test('request with correct credentials passes', function () { $response = withHeaders([ 'PHP_AUTH_USER' => config('very_basic_auth.user'), 'PHP_AUTH_PW' => config('very_basic_auth.password'), ])->get('/'); expect($response->getStatusCode())->toEqual(200); expect($response->getContent())->toEqual('ok'); }); test('request with correct credentials passes - hashed password', function () { config()->set('very_basic_auth.user', 'test'); config()->set('very_basic_auth.password', app()->make('hash')->make('test')); $response = withHeaders([ 'PHP_AUTH_USER' => 'test', 'PHP_AUTH_PW' => 'test', ])->get('/'); expect($response->getStatusCode())->toEqual(200); expect($response->getContent())->toEqual('ok'); }); test('environments', function () { config()->set('very_basic_auth.envs', ['production']); get('/')->assertStatus(200); config()->set('very_basic_auth.envs', ['local']); get('/')->assertStatus(200); config()->set('very_basic_auth.envs', ['*']); get('/')->assertStatus(401); config()->set('very_basic_auth.envs', ['testing']); get('/')->assertStatus(401); }); test('request with incorrect inline credentials fails', function () { $response = withHeaders([ 'PHP_AUTH_USER' => str_random(20), 'PHP_AUTH_PW' => str_random(20), ])->get('/inline'); expect($response->getStatusCode())->toEqual(401); expect($response->getContent())->toEqual(config('very_basic_auth.error_message')); }); test('request with correct inline credentials passes', function () { $response = withHeaders([ 'PHP_AUTH_USER' => config('very_basic_auth.user'), 'PHP_AUTH_PW' => config('very_basic_auth.password'), ])->get('/inline'); expect($response->getStatusCode())->toEqual(200); expect($response->getContent())->toEqual('ok'); }); test('test response handlers', function () { // Custom response handler app()->bind( ResponseHandler::class, CustomResponseHandler::class ); $response = get('/test'); expect($response->getStatusCode())->toEqual(401); expect($response->getContent())->toEqual('Custom response'); // Default response handler app()->bind( ResponseHandler::class, DefaultResponseHandler::class ); $response = get('/test'); expect($response->getStatusCode())->toEqual(401); expect($response->getContent())->toEqual(config('very_basic_auth.error_message')); }); // Test for the console command PasswordGenerateCommand test('console command sets password in .env file', function () { $envPath = base_path('.env'); // Clean up any existing .env before the test if (file_exists($envPath)) { unlink($envPath); } // Create a fresh .env file_put_contents($envPath, "APP_NAME=Laravel\nBASIC_AUTH_PASSWORD=test"); $password = 'password' . uniqid(); // Simulate user input for the console command $this->artisan('very-basic-auth:generate-password') ->expectsQuestion('Please enter a password for the very basic auth', $password) ->expectsQuestion('Please confirm your password', $password) ->assertExitCode(0); // Reload env-variables to make sure the newest value is available $this->artisan('config:cache'); $hashedPassword = config('very_basic_auth.password'); expect($hashedPassword)->not->toBeNull(); expect(app()->make('hash')->check($password, $hashedPassword))->toBeTrue(); expect(config('app.name'))->toEqual('Laravel'); }); function requestHeader($response): string { return Str::lower($response->headers->get('content-type')); }