Skip to content

Commit 2bfa815

Browse files
committed
fix: adjust WebAuthn code to new major version
Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
1 parent c3988d3 commit 2bfa815

6 files changed

Lines changed: 210 additions & 178 deletions

File tree

apps/settings/lib/Controller/WebAuthnController.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@
1919
use OCP\AppFramework\Http\Attribute\OpenAPI;
2020
use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
2121
use OCP\AppFramework\Http\Attribute\UseSession;
22+
use OCP\AppFramework\Http\DataDisplayResponse;
2223
use OCP\AppFramework\Http\JSONResponse;
24+
use OCP\AppFramework\Http\Response;
2325
use OCP\IRequest;
2426
use OCP\ISession;
2527
use OCP\IUserSession;
2628
use Psr\Log\LoggerInterface;
27-
use Webauthn\PublicKeyCredentialCreationOptions;
2829

2930
#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]
3031
class WebAuthnController extends Controller {
@@ -45,15 +46,17 @@ public function __construct(
4546
#[PasswordConfirmationRequired]
4647
#[UseSession]
4748
#[NoCSRFRequired]
48-
public function startRegistration(): JSONResponse {
49+
public function startRegistration(): Response {
4950
$this->logger->debug('Starting WebAuthn registration');
5051

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

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

56-
return new JSONResponse($credentialOptions);
57+
$response = new DataDisplayResponse($credentialOptions);
58+
$response->addHeader('Content-Type', 'application/json; charset=utf-8');
59+
return $response;
5760
}
5861

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

7174
// Obtain the publicKeyCredentialOptions from when we started the registration
72-
$publicKeyCredentialCreationOptions = PublicKeyCredentialCreationOptions::createFromArray($this->session->get(self::WEBAUTHN_REGISTRATION));
73-
75+
$registrationOptions = $this->session->get(self::WEBAUTHN_REGISTRATION);
7476
$this->session->remove(self::WEBAUTHN_REGISTRATION);
7577

76-
return new JSONResponse($this->manager->finishRegister($publicKeyCredentialCreationOptions, $name, $data));
78+
return new JSONResponse($this->manager->finishRegister($registrationOptions, $name, $data));
7779
}
7880

7981
#[NoSubAdminRequired]

build/psalm-baseline.xml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2337,11 +2337,6 @@
23372337
<code><![CDATA[validateMailAddress]]></code>
23382338
</DeprecatedMethod>
23392339
</file>
2340-
<file src="apps/settings/lib/Controller/WebAuthnController.php">
2341-
<DeprecatedMethod>
2342-
<code><![CDATA[PublicKeyCredentialCreationOptions::createFromArray($this->session->get(self::WEBAUTHN_REGISTRATION))]]></code>
2343-
</DeprecatedMethod>
2344-
</file>
23452340
<file src="apps/settings/lib/Hooks.php">
23462341
<DeprecatedMethod>
23472342
<code><![CDATA[getAppValue]]></code>
@@ -3205,7 +3200,6 @@
32053200
</file>
32063201
<file src="core/Controller/WebAuthnController.php">
32073202
<DeprecatedMethod>
3208-
<code><![CDATA[PublicKeyCredentialRequestOptions::createFromString($this->session->get(self::WEBAUTHN_LOGIN))]]></code>
32093203
<code><![CDATA[Util::emitHook(
32103204
'\OCA\Files_Sharing\API\Server2Server',
32113205
'preLoginNameUsedAsUserName',

core/Controller/WebAuthnController.php

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,13 @@
1818
use OCP\AppFramework\Http\Attribute\FrontpageRoute;
1919
use OCP\AppFramework\Http\Attribute\PublicPage;
2020
use OCP\AppFramework\Http\Attribute\UseSession;
21+
use OCP\AppFramework\Http\DataDisplayResponse;
2122
use OCP\AppFramework\Http\JSONResponse;
23+
use OCP\AppFramework\Http\Response;
2224
use OCP\IRequest;
2325
use OCP\ISession;
2426
use OCP\Util;
2527
use Psr\Log\LoggerInterface;
26-
use Webauthn\PublicKeyCredentialRequestOptions;
2728

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

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

5960
$publicKeyCredentialRequestOptions = $this->webAuthnManger->startAuthentication($uid, $this->request->getServerHost());
60-
$this->session->set(self::WEBAUTHN_LOGIN, json_encode($publicKeyCredentialRequestOptions));
61+
$this->session->set(self::WEBAUTHN_LOGIN, $publicKeyCredentialRequestOptions);
6162
$this->session->set(self::WEBAUTHN_LOGIN_UID, $uid);
6263

63-
return new JSONResponse($publicKeyCredentialRequestOptions);
64+
$response = new DataDisplayResponse($publicKeyCredentialRequestOptions);
65+
$response->addHeader('Content-Type', 'application/json; charset=utf-8');
66+
return $response;
6467
}
6568

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

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

lib/private/Authentication/WebAuthn/CredentialRepository.php

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,44 +12,47 @@
1212
use OC\Authentication\WebAuthn\Db\PublicKeyCredentialEntity;
1313
use OC\Authentication\WebAuthn\Db\PublicKeyCredentialMapper;
1414
use OCP\AppFramework\Db\IMapperException;
15-
use Webauthn\PublicKeyCredentialSource;
16-
use Webauthn\PublicKeyCredentialSourceRepository;
15+
use Webauthn\AttestationStatement\AttestationStatementSupportManager;
16+
use Webauthn\AttestationStatement\NoneAttestationStatementSupport;
17+
use Webauthn\CredentialRecord;
18+
use Webauthn\Denormalizer\WebauthnSerializerFactory;
1719
use Webauthn\PublicKeyCredentialUserEntity;
1820

19-
class CredentialRepository implements PublicKeyCredentialSourceRepository {
21+
class CredentialRepository {
22+
private WebauthnSerializerFactory $serializerFactory;
23+
2024
public function __construct(
2125
private PublicKeyCredentialMapper $credentialMapper,
2226
) {
27+
$attestationStatementSupportManager = AttestationStatementSupportManager::create();
28+
$attestationStatementSupportManager->add(NoneAttestationStatementSupport::create());
29+
$this->serializerFactory = new WebauthnSerializerFactory($attestationStatementSupportManager);
2330
}
2431

25-
#[\Override]
26-
public function findOneByCredentialId(string $publicKeyCredentialId): ?PublicKeyCredentialSource {
32+
public function findOneByCredentialId(string $publicKeyCredentialId): ?CredentialRecord {
2733
try {
2834
$entity = $this->credentialMapper->findOneByCredentialId($publicKeyCredentialId);
29-
return $entity->toPublicKeyCredentialSource();
30-
} catch (IMapperException $e) {
35+
return $this->mapToCredentialRecord($entity);
36+
} catch (IMapperException) {
3137
return null;
3238
}
3339
}
3440

3541
/**
36-
* @return PublicKeyCredentialSource[]
42+
* @return CredentialRecord[]
3743
*/
38-
#[\Override]
3944
public function findAllForUserEntity(PublicKeyCredentialUserEntity $publicKeyCredentialUserEntity): array {
40-
$uid = $publicKeyCredentialUserEntity->getId();
45+
$uid = $publicKeyCredentialUserEntity->id;
4146
$entities = $this->credentialMapper->findAllForUid($uid);
4247

43-
return array_map(function (PublicKeyCredentialEntity $entity) {
44-
return $entity->toPublicKeyCredentialSource();
45-
}, $entities);
48+
return array_map($this->mapToCredentialRecord(...), $entities);
4649
}
4750

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

5154
try {
52-
$oldEntity = $this->credentialMapper->findOneByCredentialId($publicKeyCredentialSource->getPublicKeyCredentialId());
55+
$oldEntity = $this->credentialMapper->findOneByCredentialId($credentialRecord->publicKeyCredentialId);
5356
} catch (IMapperException $e) {
5457
}
5558

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

62-
$entity = PublicKeyCredentialEntity::fromPublicKeyCrendentialSource($name, $publicKeyCredentialSource, $userVerification);
65+
$credentialId = base64_encode($credentialRecord->publicKeyCredentialId);
66+
$entity = new PublicKeyCredentialEntity();
67+
$entity->setName($name);
68+
$entity->setUid($credentialRecord->userHandle);
69+
$entity->setUserVerification($userVerification);
70+
$entity->setPublicKeyCredentialId($credentialId);
71+
$entity->setData($this->serializeCredentialRecord($credentialRecord));
6372

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

79-
#[\Override]
80-
public function saveCredentialSource(PublicKeyCredentialSource $publicKeyCredentialSource, ?string $name = null): void {
81-
$this->saveAndReturnCredentialSource($publicKeyCredentialSource, $name);
88+
public function mapToCredentialRecord(PublicKeyCredentialEntity $entity): CredentialRecord {
89+
$serializer = $this->serializerFactory->create();
90+
return $serializer->deserialize(
91+
$entity->getData(),
92+
CredentialRecord::class,
93+
'json',
94+
);
95+
}
96+
97+
private function serializeCredentialRecord(CredentialRecord $credentialRecord): string {
98+
$serializer = $this->serializerFactory->create();
99+
return $serializer->serialize($credentialRecord, 'json');
82100
}
83101
}

lib/private/Authentication/WebAuthn/Db/PublicKeyCredentialEntity.php

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
use JsonSerializable;
1313
use OCP\AppFramework\Db\Entity;
14-
use Webauthn\PublicKeyCredentialSource;
1514

1615
/**
1716
* @since 19.0.0
@@ -24,26 +23,21 @@
2423
* @method void setPublicKeyCredentialId(string $id);
2524
* @method string getData();
2625
* @method void setData(string $data);
27-
*
28-
* @since 30.0.0 Add userVerification attribute
2926
* @method bool|null getUserVerification();
3027
* @method void setUserVerification(bool $userVerification);
28+
*
29+
* @since 30.0.0 Add userVerification attribute
3130
*/
3231
class PublicKeyCredentialEntity extends Entity implements JsonSerializable {
33-
/** @var string */
34-
protected $name;
32+
protected ?string $name = null;
3533

36-
/** @var string */
37-
protected $uid;
34+
protected ?string $uid = null;
3835

39-
/** @var string */
40-
protected $publicKeyCredentialId;
36+
protected ?string $publicKeyCredentialId = null;
4137

42-
/** @var string */
43-
protected $data;
38+
protected ?string $data = null;
4439

45-
/** @var bool|null */
46-
protected $userVerification;
40+
protected ?bool $userVerification = null;
4741

4842
public function __construct() {
4943
$this->addType('name', 'string');
@@ -53,24 +47,6 @@ public function __construct() {
5347
$this->addType('userVerification', 'boolean');
5448
}
5549

56-
public static function fromPublicKeyCrendentialSource(string $name, PublicKeyCredentialSource $publicKeyCredentialSource, bool $userVerification): PublicKeyCredentialEntity {
57-
$publicKeyCredentialEntity = new self();
58-
59-
$publicKeyCredentialEntity->setName($name);
60-
$publicKeyCredentialEntity->setUid($publicKeyCredentialSource->getUserHandle());
61-
$publicKeyCredentialEntity->setPublicKeyCredentialId(base64_encode($publicKeyCredentialSource->getPublicKeyCredentialId()));
62-
$publicKeyCredentialEntity->setData(json_encode($publicKeyCredentialSource));
63-
$publicKeyCredentialEntity->setUserVerification($userVerification);
64-
65-
return $publicKeyCredentialEntity;
66-
}
67-
68-
public function toPublicKeyCredentialSource(): PublicKeyCredentialSource {
69-
return PublicKeyCredentialSource::createFromArray(
70-
json_decode($this->getData(), true)
71-
);
72-
}
73-
7450
/**
7551
* @inheritDoc
7652
*/

0 commit comments

Comments
 (0)