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.
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
use Webauthn\PublicKeyCredentialSource as BasePublicKeyCredentialSource;
use Webauthn\TrustPath\TrustPath;
/**
* @ORM\Table(name="public_key_credential_sources")
* @ORM\Entity(repositoryClass="App\Repository\PublicKeyCredentialSourceRepository")
*/
class PublicKeyCredentialSource extends BasePublicKeyCredentialSource
{
/**
* @var string
* @ORM\Id
* @ORM\Column(type="string", length=100)
* @ORM\GeneratedValue(strategy="NONE")
*/
private $id;
public function __construct(string $publicKeyCredentialId, string $type, array $transports, string $attestationType, TrustPath $trustPath, UuidInterface $aaguid, string $credentialPublicKey, string $userHandle, int $counter)
{
$this->id = Uuid::uuid4()->toString();
parent::__construct($publicKeyCredentialId, $type, $transports, $attestationType, $trustPath, $aaguid, $credentialPublicKey, $userHandle, $counter);
}
public function getId(): string
{
return $this->id;
}
}
To ease the integration into your application, the bundle provides a concrete class that you can extend.
We must override the method saveCredentialSource
because we may receive Webauthn\PublicKeyCredentialSource
objects instead of App\Entity\PublicKeyCredentialSource
.
<?php
declare(strict_types=1);
namespace App\Repository;
use App\Entity\PublicKeyCredentialSource;
use Doctrine\Common\Persistence\ManagerRegistry;
use Webauthn\Bundle\Repository\PublicKeyCredentialSourceRepository as BasePublicKeyCredentialSourceRepository;
use Webauthn\PublicKeyCredentialSource as BasePublicKeyCredentialSource;
final class PublicKeyCredentialSourceRepository extends BasePublicKeyCredentialSourceRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, PublicKeyCredentialSource::class);
}
public function saveCredentialSource(BasePublicKeyCredentialSource $publicKeyCredentialSource, bool $flush = true): void
{
if (!$publicKeyCredentialSource instanceof PublicKeyCredentialSource) {
$publicKeyCredentialSource = new PublicKeyCredentialSource(
$publicKeyCredentialSource->getPublicKeyCredentialId(),
$publicKeyCredentialSource->getType(),
$publicKeyCredentialSource->getTransports(),
$publicKeyCredentialSource->getAttestationType(),
$publicKeyCredentialSource->getTrustPath(),
$publicKeyCredentialSource->getAaguid(),
$publicKeyCredentialSource->getCredentialPublicKey(),
$publicKeyCredentialSource->getUserHandle(),
$publicKeyCredentialSource->getCounter()
);
}
parent::saveCredentialSource($publicKeyCredentialSource, $flush);
}
}
This repository should be declared as a Symfony service.
services:
App\Repository\PublicKeyCredentialSourceRepository: ~
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.
<?php
declare(strict_types=1);
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Webauthn\PublicKeyCredentialUserEntity;
/**
* @ORM\Table(name="users")
* @ORM\Entity(repositoryClass="App\Repository\UserRepository")
* @UniqueEntity("name")
*/
class User extends PublicKeyCredentialUserEntity implements UserInterface
{
/**
* @ORM\Id
* @ORM\Column(type="string", length=255)
*/
protected $id;
/**
* @ORM\Column(type="json")
*/
protected $roles;
public function __construct(string $id, string $name, string $displayName, ?string $icon = null, array $roles = [])
{
parent::__construct($name, $id, $displayName, $icon);
$this->roles = $roles;
}
public function getRoles(): array
{
return array_unique($this->roles + ['ROLE_USER']);
}
public function getPassword(): void
{
}
public function getSalt(): void
{
}
public function getUsername(): ?string
{
return $this->name;
}
public function eraseCredentials(): void
{
}
}
The following example uses Doctrine to create, persist or perform queries using the User objects created above.
<?php
declare(strict_types=1);
namespace App\Repository;
use App\Entity\User;
use Doctrine\Common\Persistence\ManagerRegistry;
use Ramsey\Uuid\Uuid;
use Webauthn\Bundle\Repository\AbstractPublicKeyCredentialUserEntityRepository;
use Webauthn\PublicKeyCredentialUserEntity;
final class UserRepository extends AbstractPublicKeyCredentialUserEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
}
public function createUserEntity(string $username, string $displayName, ?string $icon): PublicKeyCredentialUserEntity
{
$id = Uuid::uuid4()->toString();
return new User($id, $username, $displayName, $icon, []);
}
public function saveUserEntity(PublicKeyCredentialUserEntity $userEntity): void
{
if (!$userEntity instanceof User) {
$userEntity = new User(
$userEntity->getId(),
$userEntity->getName(),
$userEntity->getDisplayName(),
$userEntity->getId()
);
}
parent::saveUserEntity($userEntity);
}
public function find(string $username): ?User
{
$qb = $this->getEntityManager()->createQueryBuilder();
return $qb->select('u')
->from(User::class, 'u')
->where('u.name = :name')
->setParameter(':name', $username)
->setMaxResults(1)
->getQuery()
->getOneOrNullResult()
;
}
}
This repository should be declared as a Symfony service.
services:
App\Repository\UserRepository: ~