Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion 3rdparty
Submodule 3rdparty updated 699 files
14 changes: 8 additions & 6 deletions apps/settings/lib/Controller/WebAuthnController.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
use OCP\AppFramework\Http\Attribute\UseSession;
use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\Response;
use OCP\IRequest;
use OCP\ISession;
use OCP\IUserSession;
use Psr\Log\LoggerInterface;
use Webauthn\PublicKeyCredentialCreationOptions;

#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
class WebAuthnController extends Controller {
Expand All @@ -45,15 +46,17 @@ public function __construct(
#[PasswordConfirmationRequired]
#[UseSession]
#[NoCSRFRequired]
public function startRegistration(): JSONResponse {
public function startRegistration(): Response {
$this->logger->debug('Starting WebAuthn registration');

$credentialOptions = $this->manager->startRegistration($this->userSession->getUser(), $this->request->getServerHost());

// Set this in the session since we need it on finish
$this->session->set(self::WEBAUTHN_REGISTRATION, $credentialOptions);

return new JSONResponse($credentialOptions);
$response = new DataDisplayResponse($credentialOptions);
$response->addHeader('Content-Type', 'application/json; charset=utf-8');
return $response;
}

#[NoSubAdminRequired]
Expand All @@ -69,11 +72,10 @@ public function finishRegistration(string $name, string $data): JSONResponse {
}

// Obtain the publicKeyCredentialOptions from when we started the registration
$publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::createFromArray($this->session->get(self::WEBAUTHN_REGISTRATION));

$registrationOptions = $this->session->get(self::WEBAUTHN_REGISTRATION);
$this->session->remove(self::WEBAUTHN_REGISTRATION);

return new JSONResponse($this->manager->finishRegister($publicKeyCredentialCreationOptions, $name, $data));
return new JSONResponse($this->manager->finishRegister($registrationOptions, $name, $data));
}

#[NoSubAdminRequired]
Expand Down
6 changes: 0 additions & 6 deletions build/psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2337,11 +2337,6 @@
<code><![CDATA[validateMailAddress]]></code>
</DeprecatedMethod>
</file>
<file src="apps/settings/lib/Controller/WebAuthnController.php">
<DeprecatedMethod>
<code><![CDATA[PublicKeyCredentialCreationOptions::createFromArray($this->session->get(self::WEBAUTHN_REGISTRATION))]]></code>
</DeprecatedMethod>
</file>
<file src="apps/settings/lib/Hooks.php">
<DeprecatedMethod>
<code><![CDATA[getAppValue]]></code>
Expand Down Expand Up @@ -3205,7 +3200,6 @@
</file>
<file src="core/Controller/WebAuthnController.php">
<DeprecatedMethod>
<code><![CDATA[PublicKeyCredentialRequestOptions::createFromString($this->session->get(self::WEBAUTHN_LOGIN))]]></code>
<code><![CDATA[Util::emitHook(
'\OCA\Files_Sharing\API\Server2Server',
'preLoginNameUsedAsUserName',
Expand Down
13 changes: 8 additions & 5 deletions core/Controller/WebAuthnController.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\Attribute\UseSession;
use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\Response;
use OCP\IRequest;
use OCP\ISession;
use OCP\Util;
use Psr\Log\LoggerInterface;
use Webauthn\PublicKeyCredentialRequestOptions;

class WebAuthnController extends Controller {
private const string WEBAUTHN_LOGIN = 'webauthn_login';
Expand All @@ -44,7 +45,7 @@ public function __construct(
#[PublicPage]
#[UseSession]
#[FrontpageRoute(verb: 'POST', url: 'login/webauthn/start')]
public function startAuthentication(string $loginName): JSONResponse {
public function startAuthentication(string $loginName): Response {
$this->logger->debug('Starting WebAuthn login');

$this->logger->debug('Converting login name to UID');
Expand All @@ -57,10 +58,12 @@ public function startAuthentication(string $loginName): JSONResponse {
$this->logger->debug('Got UID: ' . $uid);

$publicKeyCredentialRequestOptions = $this->webAuthnManger->startAuthentication($uid, $this->request->getServerHost());
$this->session->set(self::WEBAUTHN_LOGIN, json_encode($publicKeyCredentialRequestOptions));
$this->session->set(self::WEBAUTHN_LOGIN, $publicKeyCredentialRequestOptions);
$this->session->set(self::WEBAUTHN_LOGIN_UID, $uid);

return new JSONResponse($publicKeyCredentialRequestOptions);
$response = new DataDisplayResponse($publicKeyCredentialRequestOptions);
$response->addHeader('Content-Type', 'application/json; charset=utf-8');
return $response;
}

#[PublicPage]
Expand All @@ -75,7 +78,7 @@ public function finishAuthentication(string $data): JSONResponse {
}

// Obtain the publicKeyCredentialOptions from when we started the registration
$publicKeyCredentialRequestOptions = PublicKeyCredentialRequestOptions::createFromString($this->session->get(self::WEBAUTHN_LOGIN));
$publicKeyCredentialRequestOptions = $this->session->get(self::WEBAUTHN_LOGIN);
$uid = $this->session->get(self::WEBAUTHN_LOGIN_UID);
$authenticatorData = $this->webAuthnManger->finishAuthentication($publicKeyCredentialRequestOptions, $data, $uid);

Expand Down
56 changes: 37 additions & 19 deletions lib/private/Authentication/WebAuthn/CredentialRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,44 +12,47 @@
use OC\Authentication\WebAuthn\Db\PublicKeyCredentialEntity;
use OC\Authentication\WebAuthn\Db\PublicKeyCredentialMapper;
use OCP\AppFramework\Db\IMapperException;
use Webauthn\PublicKeyCredentialSource;
use Webauthn\PublicKeyCredentialSourceRepository;
use Webauthn\AttestationStatement\AttestationStatementSupportManager;
use Webauthn\AttestationStatement\NoneAttestationStatementSupport;
use Webauthn\CredentialRecord;
use Webauthn\Denormalizer\WebauthnSerializerFactory;
use Webauthn\PublicKeyCredentialUserEntity;

class CredentialRepository implements PublicKeyCredentialSourceRepository {
class CredentialRepository {
private WebauthnSerializerFactory $serializerFactory;

public function __construct(
private PublicKeyCredentialMapper $credentialMapper,
) {
$attestationStatementSupportManager = AttestationStatementSupportManager::create();
$attestationStatementSupportManager->add(NoneAttestationStatementSupport::create());
$this->serializerFactory = new WebauthnSerializerFactory($attestationStatementSupportManager);
}

#[\Override]
public function findOneByCredentialId(string $publicKeyCredentialId): ?PublicKeyCredentialSource {
public function findOneByCredentialId(string $publicKeyCredentialId): ?CredentialRecord {
try {
$entity = $this->credentialMapper->findOneByCredentialId($publicKeyCredentialId);
return $entity->toPublicKeyCredentialSource();
} catch (IMapperException $e) {
return $this->mapToCredentialRecord($entity);
} catch (IMapperException) {
return null;
}
}

/**
* @return PublicKeyCredentialSource[]
* @return CredentialRecord[]
*/
#[\Override]
public function findAllForUserEntity(PublicKeyCredentialUserEntity $publicKeyCredentialUserEntity): array {
$uid = $publicKeyCredentialUserEntity->getId();
$uid = $publicKeyCredentialUserEntity->id;
$entities = $this->credentialMapper->findAllForUid($uid);

return array_map(function (PublicKeyCredentialEntity $entity) {
return $entity->toPublicKeyCredentialSource();
}, $entities);
return array_map($this->mapToCredentialRecord(...), $entities);
}

public function saveAndReturnCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, ?string $name = null, bool $userVerification = false): PublicKeyCredentialEntity {
public function saveCredentialSource(CredentialRecord $credentialRecord, ?string $name = null, bool $userVerification = false): PublicKeyCredentialEntity {
$oldEntity = null;

try {
$oldEntity = $this->credentialMapper->findOneByCredentialId($publicKeyCredentialSource->getPublicKeyCredentialId());
$oldEntity = $this->credentialMapper->findOneByCredentialId($credentialRecord->publicKeyCredentialId);
} catch (IMapperException $e) {
}

Expand All @@ -59,7 +62,13 @@ public function saveAndReturnCredentialSource(PublicKeyCredentialSource $publicK
$name = 'default';
}

$entity = PublicKeyCredentialEntity::fromPublicKeyCrendentialSource($name, $publicKeyCredentialSource, $userVerification);
$credentialId = base64_encode($credentialRecord->publicKeyCredentialId);
$entity = new PublicKeyCredentialEntity();
$entity->setName($name);
$entity->setUid($credentialRecord->userHandle);
$entity->setUserVerification($userVerification);
$entity->setPublicKeyCredentialId($credentialId);
$entity->setData($this->serializeCredentialRecord($credentialRecord));

if ($oldEntity) {
$entity->setId($oldEntity->getId());
Expand All @@ -76,8 +85,17 @@ public function saveAndReturnCredentialSource(PublicKeyCredentialSource $publicK
return $this->credentialMapper->insertOrUpdate($entity);
}

#[\Override]
public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, ?string $name = null): void {
$this->saveAndReturnCredentialSource($publicKeyCredentialSource, $name);
public function mapToCredentialRecord(PublicKeyCredentialEntity $entity): CredentialRecord {
$serializer = $this->serializerFactory->create();
return $serializer->deserialize(
$entity->getData(),
CredentialRecord::class,
'json',
);
}

private function serializeCredentialRecord(CredentialRecord $credentialRecord): string {
$serializer = $this->serializerFactory->create();
return $serializer->serialize($credentialRecord, 'json');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

use JsonSerializable;
use OCP\AppFramework\Db\Entity;
use Webauthn\PublicKeyCredentialSource;

/**
* @since 19.0.0
Expand All @@ -24,26 +23,21 @@
* @method void setPublicKeyCredentialId(string $id);
* @method string getData();
* @method void setData(string $data);
*
* @since 30.0.0 Add userVerification attribute
* @method bool|null getUserVerification();
* @method void setUserVerification(bool $userVerification);
*
* @since 30.0.0 Add userVerification attribute
*/
class PublicKeyCredentialEntity extends Entity implements JsonSerializable {
/** @var string */
protected $name;
protected ?string $name = null;

/** @var string */
protected $uid;
protected ?string $uid = null;

/** @var string */
protected $publicKeyCredentialId;
protected ?string $publicKeyCredentialId = null;

/** @var string */
protected $data;
protected ?string $data = null;

/** @var bool|null */
protected $userVerification;
protected ?bool $userVerification = null;

public function __construct() {
$this->addType('name', 'string');
Expand All @@ -53,24 +47,6 @@ public function __construct() {
$this->addType('userVerification', 'boolean');
}

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;
}

public function toPublicKeyCredentialSource(): PublicKeyCredentialSource {
return PublicKeyCredentialSource::createFromArray(
json_decode($this->getData(), true)
);
}

/**
* @inheritDoc
*/
Expand Down
Loading
Loading