Entities with Doctrine

Credential Source

The Doctrine Entity

With Doctrine, you have to indicate how to store the Credential Source objects. Hereafter an example of an entity. In this example we add an entity id and a custom field created_at. We also indicate the repository as we will have a custom one.
As the ID must have a fixed length and because the credentialId field of Webauthn\PublicKeyCredentialSource hasn’t such a requirement and is a binary string, thus we need to declare our own id field.
App/Entity/PublicKeyCredentialSource.php
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Entity;
6
7
use Doctrine\ORM\Mapping as ORM;
8
use Ramsey\Uuid\Uuid;
9
use Ramsey\Uuid\UuidInterface;
10
use Webauthn\PublicKeyCredentialSource as BasePublicKeyCredentialSource;
11
use Webauthn\TrustPath\TrustPath;
12
13
/**
14
* @ORM\Table(name="public_key_credential_sources")
15
* @ORM\Entity(repositoryClass="App\Repository\PublicKeyCredentialSourceRepository")
16
*/
17
class PublicKeyCredentialSource extends BasePublicKeyCredentialSource
18
{
19
/**
20
* @var string
21
* @ORM\Id
22
* @ORM\Column(type="string", length=100)
23
* @ORM\GeneratedValue(strategy="NONE")
24
*/
25
private $id;
26
27
public function __construct(string $publicKeyCredentialId, string $type, array $transports, string $attestationType, TrustPath $trustPath, UuidInterface $aaguid, string $credentialPublicKey, string $userHandle, int $counter)
28
{
29
$this->id = Uuid::uuid4()->toString();
30
parent::__construct($publicKeyCredentialId, $type, $transports, $attestationType, $trustPath, $aaguid, $credentialPublicKey, $userHandle, $counter);
31
}
32
33
public function getId(): string
34
{
35
return $this->id;
36
}
37
}
Copied!
Do not forget to update your database schema!

The Repository

To ease the integration into your application, the bundle provides a concrete class that you can extend.
In this following example, we extend that class and add a method to get all credentials for a specific user handle. Feel free to add your own methods.
We must override the method saveCredentialSource because we may receive Webauthn\PublicKeyCredentialSource objects instead of App\Entity\PublicKeyCredentialSource.
App/Repository/PublicKeyCredentialSourceRepository.php
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Repository;
6
7
use App\Entity\PublicKeyCredentialSource;
8
use Doctrine\Common\Persistence\ManagerRegistry;
9
use Webauthn\Bundle\Repository\PublicKeyCredentialSourceRepository as BasePublicKeyCredentialSourceRepository;
10
use Webauthn\PublicKeyCredentialSource as BasePublicKeyCredentialSource;
11
12
final class PublicKeyCredentialSourceRepository extends BasePublicKeyCredentialSourceRepository
13
{
14
public function __construct(ManagerRegistry $registry)
15
{
16
parent::__construct($registry, PublicKeyCredentialSource::class);
17
}
18
19
public function saveCredentialSource(BasePublicKeyCredentialSource $publicKeyCredentialSource, bool $flush = true): void
20
{
21
if (!$publicKeyCredentialSource instanceof PublicKeyCredentialSource) {
22
$publicKeyCredentialSource = new PublicKeyCredentialSource(
23
$publicKeyCredentialSource->getPublicKeyCredentialId(),
24
$publicKeyCredentialSource->getType(),
25
$publicKeyCredentialSource->getTransports(),
26
$publicKeyCredentialSource->getAttestationType(),
27
$publicKeyCredentialSource->getTrustPath(),
28
$publicKeyCredentialSource->getAaguid(),
29
$publicKeyCredentialSource->getCredentialPublicKey(),
30
$publicKeyCredentialSource->getUserHandle(),
31
$publicKeyCredentialSource->getCounter()
32
);
33
}
34
parent::saveCredentialSource($publicKeyCredentialSource, $flush);
35
}
36
}
Copied!
This repository should be declared as a Symfony service.
With Symfony Flex, this is usually done automatically
config/services.yaml
1
services:
2
App\Repository\PublicKeyCredentialSourceRepository: ~
Copied!

User Entity

Doctrine Entity

In a Symfony application context, you usually have to manage several user entities. Thus, in the following example, the user entity class will extend the required calls and implement the interface provided by the Symfony Security component.
Feel free to add the necessary setters as well as other fields you need (creation date, last update at…).
Please note that the ID of the user IS NOT generated by Doctrine and must be a string. We highly recommend you to use UUIDs.
app/Entity/User.php
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Entity;
6
7
use Doctrine\ORM\Mapping as ORM;
8
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
9
use Symfony\Component\Security\Core\User\UserInterface;
10
use Webauthn\PublicKeyCredentialUserEntity;
11
12
/**
13
* @ORM\Table(name="users")
14
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
15
* @UniqueEntity("name")
16
*/
17
class User extends PublicKeyCredentialUserEntity implements UserInterface
18
{
19
/**
20
* @ORM\Id
21
* @ORM\Column(type="string", length=255)
22
*/
23
protected $id;
24
25
/**
26
* @ORM\Column(type="json")
27
*/
28
protected $roles;
29
30
public function __construct(string $id, string $name, string $displayName, ?string $icon = null, array $roles = [])
31
{
32
parent::__construct($name, $id, $displayName, $icon);
33
$this->roles = $roles;
34
}
35
36
public function getRoles(): array
37
{
38
return array_unique($this->roles + ['ROLE_USER']);
39
}
40
41
public function getPassword(): void
42
{
43
}
44
45
public function getSalt(): void
46
{
47
}
48
49
public function getUsername(): ?string
50
{
51
return $this->name;
52
}
53
54
public function eraseCredentials(): void
55
{
56
}
57
}
Copied!

The Repository

The following example uses Doctrine to create, persist or perform queries using the User objects created above.
app/Repository/UserRepository.php
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Repository;
6
7
use App\Entity\User;
8
use Doctrine\Common\Persistence\ManagerRegistry;
9
use Ramsey\Uuid\Uuid;
10
use Webauthn\Bundle\Repository\AbstractPublicKeyCredentialUserEntityRepository;
11
use Webauthn\PublicKeyCredentialUserEntity;
12
13
final class UserRepository extends AbstractPublicKeyCredentialUserEntityRepository
14
{
15
public function __construct(ManagerRegistry $registry)
16
{
17
parent::__construct($registry, User::class);
18
}
19
20
public function createUserEntity(string $username, string $displayName, ?string $icon): PublicKeyCredentialUserEntity
21
{
22
$id = Uuid::uuid4()->toString();
23
24
return new User($id, $username, $displayName, $icon, []);
25
}
26
27
public function saveUserEntity(PublicKeyCredentialUserEntity $userEntity): void
28
{
29
if (!$userEntity instanceof User) {
30
$userEntity = new User(
31
$userEntity->getId(),
32
$userEntity->getName(),
33
$userEntity->getDisplayName(),
34
$userEntity->getId()
35
);
36
}
37
38
parent::saveUserEntity($userEntity);
39
}
40
41
public function find(string $username): ?User
42
{
43
$qb = $this->getEntityManager()->createQueryBuilder();
44
45
return $qb->select('u')
46
->from(User::class, 'u')
47
->where('u.name = :name')
48
->setParameter(':name', $username)
49
->setMaxResults(1)
50
->getQuery()
51
->getOneOrNullResult()
52
;
53
}
54
}
Copied!
This repository should be declared as a Symfony service.
With Symfony 4, this is usually done automatically
config/services.yaml
1
services:
2
App\Repository\UserRepository: ~
Copied!
Last modified 1d ago