diff --git a/core/Migrations/Version29000Date20240324113502.php b/core/Migrations/Version29000Date20240324113502.php new file mode 100644 index 0000000000000..96537ba975495 --- /dev/null +++ b/core/Migrations/Version29000Date20240324113502.php @@ -0,0 +1,46 @@ +. + * + */ + +namespace OC\Core\Migrations; + +use Closure; +use OCP\DB\ISchemaWrapper; +use OCP\DB\Types; +use OCP\Migration\IOutput; +use OCP\Migration\SimpleMigrationStep; + +/** + * Create new column and index for lazy loading in appconfig for the new IAppConfig API. + */ +class Version29000Date20240324113502 extends SimpleMigrationStep { + public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper { + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + $table = $schema->getTable('webauthn'); + $table->addColumn('user_verification', Types::BOOLEAN, ['notnull' => true, 'default' => false]); + return $schema; + } +} diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index b575228a7c2d9..0e25fafe3e28d 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1264,6 +1264,7 @@ 'OC\\Core\\Migrations\\Version29000Date20240124132201' => $baseDir . '/core/Migrations/Version29000Date20240124132201.php', 'OC\\Core\\Migrations\\Version29000Date20240124132202' => $baseDir . '/core/Migrations/Version29000Date20240124132202.php', 'OC\\Core\\Migrations\\Version29000Date20240131122720' => $baseDir . '/core/Migrations/Version29000Date20240131122720.php', + 'OC\\Core\\Migrations\\Version29000Date20240324113502' => $baseDir . '/core/Migrations/Version29000Date20240324113502.php', 'OC\\Core\\Notification\\CoreNotifier' => $baseDir . '/core/Notification/CoreNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => $baseDir . '/core/Service/LoginFlowV2Service.php', 'OC\\DB\\Adapter' => $baseDir . '/lib/private/DB/Adapter.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index d03df46f06e65..b283b4034b543 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1297,6 +1297,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Migrations\\Version29000Date20240124132201' => __DIR__ . '/../../..' . '/core/Migrations/Version29000Date20240124132201.php', 'OC\\Core\\Migrations\\Version29000Date20240124132202' => __DIR__ . '/../../..' . '/core/Migrations/Version29000Date20240124132202.php', 'OC\\Core\\Migrations\\Version29000Date20240131122720' => __DIR__ . '/../../..' . '/core/Migrations/Version29000Date20240131122720.php', + 'OC\\Core\\Migrations\\Version29000Date20240324113502' => __DIR__ . '/../../..' . '/core/Migrations/Version29000Date20240324113502.php', 'OC\\Core\\Notification\\CoreNotifier' => __DIR__ . '/../../..' . '/core/Notification/CoreNotifier.php', 'OC\\Core\\Service\\LoginFlowV2Service' => __DIR__ . '/../../..' . '/core/Service/LoginFlowV2Service.php', 'OC\\DB\\Adapter' => __DIR__ . '/../../..' . '/lib/private/DB/Adapter.php', diff --git a/lib/private/Authentication/WebAuthn/CredentialRepository.php b/lib/private/Authentication/WebAuthn/CredentialRepository.php index e5c3fcf16189f..b8600ad93e888 100644 --- a/lib/private/Authentication/WebAuthn/CredentialRepository.php +++ b/lib/private/Authentication/WebAuthn/CredentialRepository.php @@ -61,7 +61,7 @@ public function findAllForUserEntity(PublicKeyCredentialUserEntity $publicKeyCre }, $entities); } - public function saveAndReturnCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, string $name = null): PublicKeyCredentialEntity { + public function saveAndReturnCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, string $name = null, bool $userVerification = false): PublicKeyCredentialEntity { $oldEntity = null; try { @@ -75,7 +75,7 @@ public function saveAndReturnCredentialSource(PublicKeyCredentialSource $publicK $name = 'default'; } - $entity = PublicKeyCredentialEntity::fromPublicKeyCrendentialSource($name, $publicKeyCredentialSource); + $entity = PublicKeyCredentialEntity::fromPublicKeyCrendentialSource($name, $publicKeyCredentialSource, $userVerification); if ($oldEntity) { $entity->setId($oldEntity->getId()); diff --git a/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialEntity.php b/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialEntity.php index 6f97ded483d71..601bc6d0d2e95 100644 --- a/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialEntity.php +++ b/lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialEntity.php @@ -41,6 +41,9 @@ * @method void setPublicKeyCredentialId(string $id); * @method string getData(); * @method void setData(string $data); + * @since 29.0.0 + * @method bool getUserVerification(); + * @method void setUserVerification(bool $userVerification); */ class PublicKeyCredentialEntity extends Entity implements JsonSerializable { /** @var string */ @@ -55,20 +58,25 @@ class PublicKeyCredentialEntity extends Entity implements JsonSerializable { /** @var string */ protected $data; + /** @var bool */ + protected $userVerification; + public function __construct() { $this->addType('name', 'string'); $this->addType('uid', 'string'); $this->addType('publicKeyCredentialId', 'string'); $this->addType('data', 'string'); + $this->addType('userVerification', 'boolean'); } - public static function fromPublicKeyCrendentialSource(string $name, PublicKeyCredentialSource $publicKeyCredentialSource): PublicKeyCredentialEntity { + public static function fromPublicKeyCrendentialSource(string $name, PublicKeyCredentialSource $publicKeyCredentialSource, bool $userVerification): PublicKeyCredentialEntity { $publicKeyCredentialEntity = new self(); $publicKeyCredentialEntity->setName($name); $publicKeyCredentialEntity->setUid($publicKeyCredentialSource->getUserHandle()); $publicKeyCredentialEntity->setPublicKeyCredentialId(base64_encode($publicKeyCredentialSource->getPublicKeyCredentialId())); $publicKeyCredentialEntity->setData(json_encode($publicKeyCredentialSource)); + $publicKeyCredentialEntity->setUserVerification($userVerification); return $publicKeyCredentialEntity; } diff --git a/lib/private/Authentication/WebAuthn/Manager.php b/lib/private/Authentication/WebAuthn/Manager.php index 5a97a573b9945..3bb6e7a62b0f1 100644 --- a/lib/private/Authentication/WebAuthn/Manager.php +++ b/lib/private/Authentication/WebAuthn/Manager.php @@ -107,9 +107,9 @@ public function startRegistration(IUser $user, string $serverHost): PublicKeyCre ]; $authenticatorSelectionCriteria = new AuthenticatorSelectionCriteria( - null, + AuthenticatorSelectionCriteria::AUTHENTICATOR_ATTACHMENT_NO_PREFERENCE, false, - AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED + AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_PREFERRED ); return new PublicKeyCredentialCreationOptions( @@ -149,7 +149,9 @@ public function finishRegister(PublicKeyCredentialCreationOptions $publicKeyCred try { // Load the data $publicKeyCredential = $publicKeyCredentialLoader->load($data); + /** @var AuthenticatorAttestationResponse $response */ $response = $publicKeyCredential->getResponse(); + $userVerification = $response->getAttestationObject()->getAuthData()->isUserVerified(); // Check if the response is an Authenticator Attestation Response if (!$response instanceof AuthenticatorAttestationResponse) { @@ -168,7 +170,7 @@ public function finishRegister(PublicKeyCredentialCreationOptions $publicKeyCred } // Persist the data - return $this->repository->saveAndReturnCredentialSource($publicKeyCredentialSource, $name); + return $this->repository->saveAndReturnCredentialSource($publicKeyCredentialSource, $name, $userVerification); } private function stripPort(string $serverHost): string { @@ -177,7 +179,11 @@ private function stripPort(string $serverHost): string { public function startAuthentication(string $uid, string $serverHost): PublicKeyCredentialRequestOptions { // List of registered PublicKeyCredentialDescriptor classes associated to the user - $registeredPublicKeyCredentialDescriptors = array_map(function (PublicKeyCredentialEntity $entity) { + $userVerificationRequirement = AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_REQUIRED; + $registeredPublicKeyCredentialDescriptors = array_map(function (PublicKeyCredentialEntity $entity) use (&$userVerificationRequirement) { + if ($entity->getUserVerification() !== true) { + $userVerificationRequirement = AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED; + } $credential = $entity->toPublicKeyCredentialSource(); return new PublicKeyCredentialDescriptor( $credential->getType(), @@ -191,7 +197,7 @@ public function startAuthentication(string $uid, string $serverHost): PublicKeyC 60000, // Timeout $this->stripPort($serverHost), // Relying Party ID $registeredPublicKeyCredentialDescriptors, // Registered PublicKeyCredentialDescriptor classes - AuthenticatorSelectionCriteria::USER_VERIFICATION_REQUIREMENT_DISCOURAGED + $userVerificationRequirement ); }