diff --git a/src/webauthn/src/CeremonyStep/CheckAllowedOrigins.php b/src/webauthn/src/CeremonyStep/CheckAllowedOrigins.php index 95e3b106..a31f0175 100644 --- a/src/webauthn/src/CeremonyStep/CheckAllowedOrigins.php +++ b/src/webauthn/src/CeremonyStep/CheckAllowedOrigins.php @@ -23,19 +23,12 @@ final readonly class CheckAllowedOrigins implements CeremonyStep { /** - * Full origin entries (scheme://host[:port]) from allowed origins that include a scheme. + * Full origin entries (scheme://host[:port]) from allowed origins. * * @var string[] */ private array $fullOrigins; - /** - * Host-only entries from allowed origins without a scheme (backward compatibility). - * - * @var string[] - */ - private array $hostOrigins; - /** * @param string[] $allowedOrigins * @param string[] $securedRelyingPartyId RP IDs that are allowed to use HTTP (e.g. localhost for development) @@ -46,19 +39,19 @@ public function __construct( private array $securedRelyingPartyId = [], ) { $fullOrigins = []; - $hostOrigins = []; foreach ($allowedOrigins as $allowedOrigin) { $parsed = parse_url($allowedOrigin); $parsed !== false || throw new InvalidArgumentException(sprintf('Invalid origin: %s', $allowedOrigin)); if (isset($parsed['scheme'], $parsed['host'])) { $fullOrigins[] = self::buildOrigin($parsed['scheme'], $parsed['host'], $parsed['port'] ?? null); } else { - $hostOrigins[] = $parsed['host'] ?? $allowedOrigin; + // Host-only entries are normalized to https:// since WebAuthn requires TLS + $host = $parsed['host'] ?? $allowedOrigin; + $fullOrigins[] = self::buildOrigin('https', $host, null); } } $this->fullOrigins = array_unique($fullOrigins); - $this->hostOrigins = array_unique($hostOrigins); } public function process( @@ -77,7 +70,7 @@ public function process( ); $originHost = $parsedOrigin['host'] ?? $C->origin; - $hasAllowedOrigins = count($this->fullOrigins) !== 0 || count($this->hostOrigins) !== 0; + $hasAllowedOrigins = count($this->fullOrigins) !== 0; if ($hasAllowedOrigins) { // Full origin match (scheme + host + port) @@ -92,15 +85,8 @@ public function process( } } - // Host-only match (backward compatibility for entries without scheme) - if (in_array($originHost, $this->hostOrigins, true)) { - return; - } - // Subdomain matching - $isFullOriginSubdomain = $this->isSubdomainOfFullOrigins($parsedOrigin); - $isHostSubdomain = $this->isSubdomain($this->hostOrigins, $originHost); - $isSubDomain = $isFullOriginSubdomain || $isHostSubdomain; + $isSubDomain = $this->isSubdomainOfFullOrigins($parsedOrigin); if ($this->allowSubdomains && $isSubDomain) { return; @@ -214,17 +200,4 @@ private function getFacetId( return (is_string($appId) && $wasUsed === true) ? $appId : $rpId; } - - /** - * @param string[] $origins - */ - private function isSubdomain(array $origins, string $domain): bool - { - foreach ($origins as $allowedOrigin) { - if ($this->isSubdomainOf($domain, $allowedOrigin)) { - return true; - } - } - return false; - } }