Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
a9bdc78
Add WebAuthn (Yubikey) as a second factor - Claude Code assisted
tobiasgruber May 19, 2026
594ddab
WIP first working version
tobiasgruber May 19, 2026
0817390
FIX error message styling - use Neos standard
tobiasgruber May 30, 2026
0e24a39
TASK: add i18n for webauthn error messages
tobiasgruber May 30, 2026
bd87580
FIX: do not render OTP errors in the webauthn section
tobiasgruber May 30, 2026
33f30b1
FIX remove duplicate Login button for OTP after OTP submission
tobiasgruber May 30, 2026
e33bcea
💬 TASK: improve second factor wording for editors
tobiasgruber May 30, 2026
23c3df7
🔥 TASK: remove duplicate loading of JS and unused CSS config
tobiasgruber May 30, 2026
e0530a5
💄 FIX: style error message if adding webauthn second factor fails in …
tobiasgruber May 30, 2026
099c431
TASK: Merge fixups
JamesAlias Jun 18, 2026
89e1bc4
SECURITY: Pin otphp package to ensure security fixed version
JamesAlias Jun 19, 2026
6ddbb8d
TASK: Fix Tests & add tests for WebAuthn
JamesAlias Jun 19, 2026
6721b7b
TASK: Fix Tests
JamesAlias Jun 19, 2026
eb7b346
TASK: Drop Neos 7 & PHP <8.2 support; Handle missing account in WebAu…
JamesAlias Jun 19, 2026
50d4637
TASK: Add test to confirm user can login with OTP when both OTP & Web…
JamesAlias Jun 19, 2026
4563725
TASK: Add test to confirm user is correctly redirected after login
JamesAlias Jun 19, 2026
8c981cd
TASK: Remove obsolete SecondFactorMethodRegistry setup
JamesAlias Jun 19, 2026
522923e
TASK: Improve check for registered 2FAs
JamesAlias Jun 19, 2026
c4af9b3
TASK: Update Readme
JamesAlias Jun 19, 2026
201cfbd
TASK: Fix TS error
JamesAlias Jun 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 44 additions & 6 deletions Classes/Controller/BackendController.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,26 @@ public function indexAction()
}

/**
* show the form to register a new second factor
* Method picker shown when the user clicks "Add second factor" inside the backend module.
*/
public function newAction(): void
{
$account = $this->securityContext->getAccount();
$currentUser = $this->partyService->getAssignedPartyOfAccount($account);

$this->view->assignMultiple([
'currentUser' => $currentUser instanceof User ? $currentUser : null,
'accountIdentifier' => $account->getAccountIdentifier(),
'flashMessages' => $this->flashMessageService
->getFlashMessageContainerForRequest($this->request)
->getMessagesAndFlush(),
]);
}

/**
* TOTP wizard (extracted from the previous newAction).
*/
public function newTotpAction(): void
{
$otp = TOTPService::generateNewTotp();
$secret = $otp->getSecret();
Expand All @@ -130,8 +147,6 @@ public function newAction(): void
$currentUser = $this->partyService->getAssignedPartyOfAccount($account);

$this->view->assignMultiple([
'styles' => array_filter($this->getNeosSettings()['userInterface']['backendLoginForm']['stylesheets']),
'scripts' => array_filter($this->getNeosSettings()['userInterface']['backendLoginForm']['scripts']),
'secret' => $secret,
'qrCode' => $qrCode,
'currentUser' => $currentUser instanceof User ? $currentUser : null,
Expand All @@ -143,7 +158,25 @@ public function newAction(): void
}

/**
* save the registered second factor
* WebAuthn setup wizard. The JS on the page talks to LoginController's
* webAuthnRegister(Options|Verify)Action XHR endpoints.
*/
public function newWebAuthnAction(): void
{
$account = $this->securityContext->getAccount();
$currentUser = $this->partyService->getAssignedPartyOfAccount($account);

$this->view->assignMultiple([
'currentUser' => $currentUser instanceof User ? $currentUser : null,
'accountIdentifier' => $account->getAccountIdentifier(),
'flashMessages' => $this->flashMessageService
->getFlashMessageContainerForRequest($this->request)
->getMessagesAndFlush(),
]);
}

/**
* save the registered second factor (TOTP)
*
* @throws SessionNotStartedException
* @throws IllegalObjectTypeException
Expand All @@ -166,10 +199,15 @@ public function createAction(string $secret, string $secondFactorFromApp, string
'',
Message::SEVERITY_WARNING
);
$this->redirect('new');
$this->redirect('newTotp');
}

$this->secondFactorRepository->createSecondFactorForAccount($secret, $this->securityContext->getAccount(), $name);
$this->secondFactorRepository->createSecondFactorForAccount(
$secret,
$this->securityContext->getAccount(),
SecondFactor::TYPE_TOTP,
$name,
);

$this->secondFactorSessionStorageService->setAuthenticationStatus(AuthenticationStatus::AUTHENTICATED);

Expand Down
Loading