feat(webauthn): WebauthnService::available gate (domain + https)

WebAuthn is offered only with an active domain (the rpId) over HTTPS; bare-IP/HTTP
returns false so a ceremony never runs with an invalid IP rpId.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat/v1-foundation
boban 2026-06-14 18:18:43 +02:00
parent 78b1017844
commit 54ebb49941
2 changed files with 59 additions and 0 deletions

View File

@ -0,0 +1,23 @@
<?php
namespace App\Services;
class WebauthnService
{
public function __construct(private DeploymentService $deployment) {}
/**
* WebAuthn needs a secure context AND a domain-based Relying-Party ID a bare IP
* literal is not a valid rpId, so the feature is unavailable on bare-IP/HTTP.
*/
public function available(): bool
{
return $this->deployment->domain() !== null && request()->isSecure();
}
/** The Relying-Party ID = the active domain (asserted present by available()). */
public function rpId(): string
{
return (string) $this->deployment->domain();
}
}

View File

@ -0,0 +1,36 @@
<?php
namespace Tests\Feature;
use App\Services\DeploymentService;
use App\Services\WebauthnService;
use Tests\TestCase;
class WebauthnAvailableTest extends TestCase
{
private function service(?string $domain): WebauthnService
{
$deployment = $this->mock(DeploymentService::class);
$deployment->shouldReceive('domain')->andReturn($domain);
return app(WebauthnService::class);
}
public function test_available_only_with_domain_and_https(): void
{
$this->app['request']->server->set('HTTPS', 'on');
$this->assertTrue($this->service('panel.example.com')->available());
}
public function test_unavailable_without_domain(): void
{
$this->app['request']->server->set('HTTPS', 'on');
$this->assertFalse($this->service(null)->available());
}
public function test_unavailable_without_https(): void
{
$this->app['request']->server->remove('HTTPS');
$this->assertFalse($this->service('panel.example.com')->available());
}
}