Skip to content

Commit

Permalink
Merge pull request #33 from sandstorm/feature/show-otp-secret-code-wh…
Browse files Browse the repository at this point in the history
…en-creating-new-second-factor

FEATURE: Show OTP secret code at 2FA setup after login and in backend module
  • Loading branch information
JamesAlias authored Sep 26, 2024
2 parents abaa3c4 + acf2729 commit c5c4792
Show file tree
Hide file tree
Showing 11 changed files with 441 additions and 37 deletions.
17 changes: 17 additions & 0 deletions Classes/Controller/BackendController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use Neos\Error\Messages\Message;
use Neos\Flow\Annotations as Flow;
use Neos\Flow\Configuration\ConfigurationManager;
use Neos\Flow\Configuration\Exception\InvalidConfigurationTypeException;
use Neos\Flow\I18n\Translator;
use Neos\Flow\Mvc\Exception\StopActionException;
use Neos\Flow\Mvc\FlashMessage\FlashMessageService;
Expand Down Expand Up @@ -118,6 +120,8 @@ public function newAction(): void
$qrCode = $this->tOTPService->generateQRCodeForTokenAndAccount($otp, $this->securityContext->getAccount());

$this->view->assignMultiple([
'styles' => array_filter($this->getNeosSettings()['userInterface']['backendLoginForm']['stylesheets']),
'scripts' => array_filter($this->getNeosSettings()['userInterface']['backendLoginForm']['scripts']),
'secret' => $secret,
'qrCode' => $qrCode,
'flashMessages' => $this->flashMessageService
Expand Down Expand Up @@ -221,4 +225,17 @@ public function deleteAction(SecondFactor $secondFactor): void

$this->redirect('index');
}

/**
* @return array
* @throws InvalidConfigurationTypeException
*/
protected function getNeosSettings(): array
{
$configurationManager = $this->objectManager->get(ConfigurationManager::class);
return $configurationManager->getConfiguration(
ConfigurationManager::CONFIGURATION_TYPE_SETTINGS,
'Neos.Neos'
);
}
}
2 changes: 2 additions & 0 deletions Classes/Controller/LoginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public function askForSecondFactorAction(?string $username = null): void

$this->view->assignMultiple([
'styles' => array_filter($this->getNeosSettings()['userInterface']['backendLoginForm']['stylesheets']),
'scripts' => array_filter($this->getNeosSettings()['userInterface']['backendLoginForm']['scripts']),
'username' => $username,
'site' => $currentSite,
'flashMessages' => $this->flashMessageService
Expand Down Expand Up @@ -162,6 +163,7 @@ public function setupSecondFactorAction(?string $username = null): void

$this->view->assignMultiple([
'styles' => array_filter($this->getNeosSettings()['userInterface']['backendLoginForm']['stylesheets']),
'scripts' => array_filter($this->getNeosSettings()['userInterface']['backendLoginForm']['scripts']),
'username' => $username,
'site' => $currentSite,
'secret' => $secret,
Expand Down
1 change: 0 additions & 1 deletion Classes/Domain/Model/Dto/SecondFactorDto.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
namespace Sandstorm\NeosTwoFactorAuthentication\Domain\Model\Dto;

use Neos\Neos\Domain\Model\User;
use Neos\Party\Domain\Model\Person;
use Sandstorm\NeosTwoFactorAuthentication\Domain\Model\SecondFactor;

class SecondFactorDto
Expand Down
7 changes: 7 additions & 0 deletions Configuration/Settings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ Neos:
label: 'Sandstorm.NeosTwoFactorAuthentication:Backend:module.label'
description: 'Sandstorm.NeosTwoFactorAuthentication:Backend:module.description'
icon: 'fas fa-qrcode'
additionalResources:
styleSheets:
- 'resource://Sandstorm.NeosTwoFactorAuthentication/Public/Styles/Login.css'
javaScripts:
- 'resource://Sandstorm.NeosTwoFactorAuthentication/Public/index.js'

userInterface:
translation:
Expand All @@ -21,6 +26,8 @@ Neos:
backendLoginForm:
stylesheets:
'Sandstorm.NeosTwoFactorAuthentication:AdditionalStyles': 'resource://Sandstorm.NeosTwoFactorAuthentication/Public/Styles/Login.css'
scripts:
'Sandstorm.NeosTwoFactorAuthentication:AdditionalScripts': 'resource://Sandstorm.NeosTwoFactorAuthentication/Public/index.js'

Flow:
http:
Expand Down
119 changes: 94 additions & 25 deletions Resources/Private/Fusion/Integration/Controller/Backend/New.fusion
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,100 @@ Sandstorm.NeosTwoFactorAuthentication.BackendController.new = Sandstorm.NeosTwoF

content = Neos.Fusion:Component {
renderer = afx`
<Neos.Fusion.Form:Form form.target.action="create">
<Neos.Fusion.Form:Hidden field.name="secret" field.value={secret}/>

<div class="neos-control-group">
<img src={qrCode} style="width: 100%; max-width: 400px"/>
</div>

<div class="neos-control-group">
<Neos.Fusion.Form:Input
field.name="secondFactorFromApp"
attributes.required="required"
attributes.id="secondFactorFromApp"
attributes.placeholder={I18n.id('otp-placeholder').package('Sandstorm.NeosTwoFactorAuthentication')}
attributes.class="neos-span5"
attributes.aria-label={I18n.id('otp-placeholder').package('Sandstorm.NeosTwoFactorAuthentication')}
attributes.autocomplete="off"
/>
</div>

<div class="neos-control-group">
<Neos.Fusion.Form:Button>
{I18n.id('module.new.submit-otp').package('Sandstorm.NeosTwoFactorAuthentication').source('Backend').translate()}
</Neos.Fusion.Form:Button>
</div>
</Neos.Fusion.Form:Form>
<Neos.Fusion.Form:Form form.target.action="create">
<div class="neos-control-group">
<img src={qrCode} style="width: 100%; max-width: 400px"/>
</div>

<div class="neos-control-group neos-two-factor__secret-wrapper">
<Neos.Fusion.Form:Hidden attributes.id="secret" field.name="secret" field.value={secret}/>

<div class="neos-actions">
<button type="button" class="neos-button neos-login-btn neos-two-factor__secret__show__button">
{I18n.id('form.secret.show').package('Sandstorm.NeosTwoFactorAuthentication')}
</button>
</div>

<dialog>
<div>
<div class="neos-two-factor__secret">
<p>
{
Array.join(
Array.map(
String.split(secret, ''),
char => Type.isNumeric(char) ? '<span class="neos-two-factor__secret__number">' + char + '</span>' : '<span>' + char + '</span>'
),
''
)
}
</p>

<div class="neos-two-factor__secret__overflow-indicator--left" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<path d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256 246.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z"/>
</svg>
</div>
<div class="neos-two-factor__secret__overflow-indicator--right" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<path d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z"/>
</svg>
</div>
</div>

<div class="neos-two-factor__dialog__actions neos-actions">
<button type="button" class="neos-two-factor__secret__copy__button neos-button neos-login-btn">
<span class="neos-two-factor__secret__copy__button__icon">
<i>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512">
<!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<path d="M280 64l40 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 128C0 92.7 28.7 64 64 64l40 0 9.6 0C121 27.5 153.3 0 192 0s71 27.5 78.4 64l9.6 0zM64 112c-8.8 0-16 7.2-16 16l0 320c0 8.8 7.2 16 16 16l256 0c8.8 0 16-7.2 16-16l0-320c0-8.8-7.2-16-16-16l-16 0 0 24c0 13.3-10.7 24-24 24l-88 0-88 0c-13.3 0-24-10.7-24-24l0-24-16 0zm128-8a24 24 0 1 0 0-48 24 24 0 1 0 0 48z"/>
</svg>
</i>
</span>
<span class="neos-two-factor__secret__copy__button__icon neos-two-factor__hidden">
<i>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512">
<!--!Font Awesome Free 6.6.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
<path d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/>
</svg>
</i>
</span>
{I18n.id('form.secret.copy').package('Sandstorm.NeosTwoFactorAuthentication')}
</button>
<button type="button" class="neos-two-factor__secret__close__button neos-button neos-login-btn">
{I18n.id('form.secret.close').package('Sandstorm.NeosTwoFactorAuthentication')}
</button>
</div>
</div>
</dialog>
</div>

<div class="neos-control-group">
<Neos.Fusion.Form:Input
field.name="secondFactorFromApp"
attributes.required="required"
attributes.id="secondFactorFromApp"
attributes.placeholder={I18n.id('otp-placeholder').package('Sandstorm.NeosTwoFactorAuthentication')}
attributes.aria-label={I18n.id('otp-placeholder').package('Sandstorm.NeosTwoFactorAuthentication')}
attributes.autocomplete="off"
/>
</div>

<div class="neos-control-group">
<Neos.Fusion.Form:Button>
{I18n.id('module.new.submit-otp').package('Sandstorm.NeosTwoFactorAuthentication').source('Backend').translate()}
</Neos.Fusion.Form:Button>
</div>
</Neos.Fusion.Form:Form>

<Neos.Fusion:Loop items={props.scripts}>
<script @children="attributes.src">
<Neos.Fusion:ResourceUri path={item}/>
</script>
</Neos.Fusion:Loop>
`
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
Sandstorm.NeosTwoFactorAuthentication.LoginController.setupSecondFactor = Sandstorm.NeosTwoFactorAuthentication:Page.SetupSecondFactorPage {
site = ${site}
styles = ${styles}
scripts = ${scripts}
username = ${username}
flashMessages = ${flashMessages}
qrCode = ${qrCode}
secret = ${secret}
}
Loading

0 comments on commit c5c4792

Please sign in to comment.