Skip to content

Commit 40a2e6d

Browse files
author
Bastian Waidelich
authored
Merge pull request #1 from yeebase/major-rework
!!! Major Rework
2 parents be24dd8 + 39919bb commit 40a2e6d

18 files changed

Lines changed: 823 additions & 459 deletions

.gitignore

Lines changed: 0 additions & 1 deletion
This file was deleted.

Classes/Controller/AbstractTwoFactorAuthenticationController.php

Lines changed: 0 additions & 12 deletions
This file was deleted.

Classes/Controller/AbstractTwoFactorAuthenticationManagementController.php

Lines changed: 0 additions & 63 deletions
This file was deleted.

Classes/Domain/Dto/TwoFactorAuthenticationCredentialsSource.php

Lines changed: 0 additions & 60 deletions
This file was deleted.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
declare(strict_types=1);
3+
namespace Yeebase\TwoFactorAuthentication\Domain\ValueObjects;
4+
5+
use BaconQrCode\Renderer\Image\SvgImageBackEnd;
6+
use BaconQrCode\Renderer\ImageRenderer;
7+
use BaconQrCode\Renderer\RendererStyle\RendererStyle;
8+
use BaconQrCode\Writer;
9+
use Neos\Flow\Annotations as Flow;
10+
11+
/**
12+
* A QR Code that encodes a given secret and can be used to activate 2FA with an authenticator app
13+
*
14+
* @Flow\Proxy(false)
15+
*/
16+
final class ActivationQrCode
17+
{
18+
/**
19+
* @var Secret
20+
*/
21+
private $secret;
22+
23+
/**
24+
* @var string
25+
*/
26+
private $qrCodeUrl;
27+
28+
private function __construct(Secret $secret, string $qrCodeUrl)
29+
{
30+
$this->secret = $secret;
31+
$this->qrCodeUrl = $qrCodeUrl;
32+
}
33+
34+
public static function fromSecretAndUrl(Secret $secret, string $qrCodeUrl): self
35+
{
36+
return new static($secret, $qrCodeUrl);
37+
}
38+
39+
public function getSecret(): Secret
40+
{
41+
return $this->secret;
42+
}
43+
44+
public function renderSvg(int $width): string
45+
{
46+
$renderer = new ImageRenderer(
47+
new RendererStyle($width),
48+
new SvgImageBackEnd()
49+
);
50+
$bacon = new Writer($renderer);
51+
return $bacon->writeString(
52+
$this->qrCodeUrl,
53+
'utf-8'
54+
);
55+
}
56+
57+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
declare(strict_types=1);
3+
namespace Yeebase\TwoFactorAuthentication\Domain\ValueObjects;
4+
5+
use Neos\Flow\Annotations as Flow;
6+
7+
/**
8+
* A Code (aka OTP) returned from an 2FA device or app
9+
*
10+
* @Flow\Proxy(false)
11+
*/
12+
final class OneTimePassword
13+
{
14+
/**
15+
* @var string
16+
*/
17+
private $value;
18+
19+
/**
20+
* @internal This constructor is only public so that this VO can be property-mapped
21+
*/
22+
public function __construct(string $otp)
23+
{
24+
if (preg_match('/^\d{6}$/', $otp) !== 1) {
25+
throw new \InvalidArgumentException('OTP must consist of 6 digits', 1549978113);
26+
}
27+
$this->value = $otp;
28+
}
29+
30+
public static function fromString(string $otp): self
31+
{
32+
return new static($otp);
33+
}
34+
35+
public function toString(): string
36+
{
37+
return $this->value;
38+
}
39+
40+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
declare(strict_types=1);
3+
namespace Yeebase\TwoFactorAuthentication\Domain\ValueObjects;
4+
5+
use Neos\Flow\Annotations as Flow;
6+
7+
/**
8+
* The user specific 2FA secret
9+
*
10+
* @Flow\Proxy(false)
11+
*/
12+
final class Secret
13+
{
14+
/**
15+
* @var string
16+
*/
17+
private $value;
18+
19+
private function __construct(string $secret)
20+
{
21+
$secret = trim($secret);
22+
if ($secret === '') {
23+
throw new \InvalidArgumentException('Secret must not be empty', 1549978555);
24+
}
25+
$this->value = $secret;
26+
}
27+
28+
public static function fromString(string $secret): self
29+
{
30+
return new static($secret);
31+
}
32+
33+
public function toString(): string
34+
{
35+
return $this->value;
36+
}
37+
38+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
declare(strict_types=1);
3+
namespace Yeebase\TwoFactorAuthentication\Domain\ValueObjects;
4+
5+
use Neos\Flow\Annotations as Flow;
6+
use Neos\Flow\Security\Cryptography\HashService;
7+
use Neos\Flow\Security\Exception\InvalidArgumentForHashGenerationException;
8+
use Neos\Flow\Security\Exception\InvalidHashException;
9+
10+
/**
11+
* The user specific 2FA secret - with an appended HMAC
12+
*
13+
* @Flow\Proxy(false)
14+
*/
15+
final class SecretWithHmac
16+
{
17+
/**
18+
* @var Secret
19+
*/
20+
private $secret;
21+
22+
/**
23+
* @var string
24+
*/
25+
private $hmac;
26+
27+
/**
28+
* @internal This constructor is only public so that this VO can be property-mapped
29+
* @throws InvalidArgumentForHashGenerationException | InvalidHashException
30+
*/
31+
public function __construct(string $secretWithHmac)
32+
{
33+
$hashService = new HashService();
34+
$this->secret = Secret::fromString($hashService->validateAndStripHmac($secretWithHmac));
35+
$this->hmac = substr($secretWithHmac, -40);
36+
}
37+
38+
/**
39+
* @throws InvalidArgumentForHashGenerationException | InvalidHashException
40+
*/
41+
public static function fromString(string $secretWithHmac): self
42+
{
43+
return new static($secretWithHmac);
44+
}
45+
46+
public static function fromSecret(Secret $secret): self
47+
{
48+
$hashService = new HashService();
49+
$secretWithHmac = $hashService->appendHmac($secret->toString());
50+
return new static($secretWithHmac);
51+
}
52+
53+
public function getSecret(): Secret
54+
{
55+
return $this->secret;
56+
}
57+
58+
public function toString(): string
59+
{
60+
return $this->secret->toString() . $this->hmac;
61+
}
62+
63+
public function __toString(): string
64+
{
65+
return $this->toString();
66+
}
67+
68+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
declare(strict_types=1);
3+
namespace Yeebase\TwoFactorAuthentication\Exception;
4+
5+
final class InvalidOtpException extends \Exception
6+
{
7+
8+
}

0 commit comments

Comments
 (0)