diff --git a/app/Http/Middleware/BasicAuth.php b/app/Http/Middleware/BasicAuth.php new file mode 100644 index 0000000..f18fd8d --- /dev/null +++ b/app/Http/Middleware/BasicAuth.php @@ -0,0 +1,29 @@ +getUser() ?? '') || ! hash_equals($password, $request->getPassword() ?? '')) { + return response('Unauthorized', 401, [ + 'WWW-Authenticate' => 'Basic realm="Protected Area"', + ]); + } + + return $next($request); + } +} diff --git a/resources/views/examples/basic-auth.blade.php b/resources/views/examples/basic-auth.blade.php new file mode 100644 index 0000000..e21b051 --- /dev/null +++ b/resources/views/examples/basic-auth.blade.php @@ -0,0 +1,59 @@ +@use('Illuminate\Support\Str') + + + + + + + Protected Page - Basic Auth Example + + @vite(['resources/css/app.css', 'resources/js/app.js']) + + +
+
+
+ + + +
+

Authentication Successful

+

+ You have successfully authenticated using HTTP Basic Authentication. +

+
+ +
+

Request Details

+ +
+
+ Authenticated User + {{ request()->getUser() }} +
+
+ Authentication Method + HTTP Basic +
+
+ Your IP Address + {{ request()->ip() }} +
+
+ User Agent + {{ Str::limit(request()->userAgent(), 50) }} +
+
+
+ +
+ + + + + Back to homepage + +
+
+ + diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 3b7f16f..250e408 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -334,6 +334,14 @@
Test form validation and browser automation
+
+ +
HTTP Basic Auth protected page
+ +
@@ -637,6 +645,23 @@ + + +
+

Test HTTP Basic Authentication

+
+
+
Command
+
+
# Access a page protected by HTTP Basic Auth
+curl -u ohdear:best-monitoring-service-ever {{ url('/basic-auth') }}
+
+
Response
+
# Returns HTML page confirming successful authentication
+# Without credentials, returns 401 Unauthorized
+
+
+
diff --git a/routes/web.php b/routes/web.php index 300cb16..20e0ad0 100644 --- a/routes/web.php +++ b/routes/web.php @@ -1,6 +1,7 @@ name('examples.contact'); }); +Route::view('/basic-auth', 'examples.basic-auth') + ->middleware([NoIndexHeader::class, BasicAuth::class]) + ->name('basic-auth'); + Route::get('/sitemap.xml', function () { $baseUrl = config('app.url'); diff --git a/tests/Feature/BasicAuthTest.php b/tests/Feature/BasicAuthTest.php new file mode 100644 index 0000000..1903b4a --- /dev/null +++ b/tests/Feature/BasicAuthTest.php @@ -0,0 +1,34 @@ +get('/basic-auth'); + + $response->assertUnauthorized() + ->assertHeader('WWW-Authenticate', 'Basic realm="Protected Area"'); +}); + +it('returns 401 with wrong credentials', function () { + $response = $this->withHeaders([ + 'Authorization' => 'Basic ' . base64_encode('wrong:credentials'), + ])->get('/basic-auth'); + + $response->assertUnauthorized(); +}); + +it('returns 200 with correct credentials', function () { + $response = $this->withHeaders([ + 'Authorization' => 'Basic ' . base64_encode('ohdear:best-monitoring-service-ever'), + ])->get('/basic-auth'); + + $response->assertOk() + ->assertViewIs('examples.basic-auth') + ->assertSee('Authentication Successful'); +}); + +it('returns noindex header', function () { + $response = $this->withHeaders([ + 'Authorization' => 'Basic ' . base64_encode('ohdear:best-monitoring-service-ever'), + ])->get('/basic-auth'); + + $response->assertHeader('X-Robots-Tag', 'noindex, nofollow'); +});