Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Twofactor auth via email at login #357

Open
wants to merge 10 commits into
base: v2
Choose a base branch
from
70 changes: 70 additions & 0 deletions lib/Provider/AtLoginProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

declare(strict_types = 1);

namespace OCA\TwoFactorEmail\Provider;

use OCA\TwoFactorEmail\Provider\Email as EmailProvider;
use OCA\TwoFactorEmail\Service\StateStorage;
use OCP\Authentication\TwoFactorAuth\ILoginSetupProvider;
use OCP\Template;
use OCP\IUser;
use OCA\TwoFactorEmail\Service\Email as EmailService;
use OCP\IConfig as OCCONFIG;
use OCP\Authentication\TwoFactorAuth\IRegistry;

class AtLoginProvider implements ILoginSetupProvider {

/** @var IUser */
private $myUser;

/** @var EmailService */
private $emailService;

/** @var OCCONFIG */
private $occonfig;

/** @var IRegistry */
private $registry;

/** @var Email */
private $provider;

private $mySecret;
private $stateStorage;

public function __construct(IUser $myUser, $mySecret, EmailService $EmailService, OCCONFIG $occonfig, IRegistry $registry, Email $provider, StateStorage $stateStorage) {
$this->myUser = $myUser;
$this->mySecret = $mySecret;
$this->emailService = $EmailService;
$this->occonfig = $occonfig;
$this->registry = $registry;
$this->provider = $provider;
$this->stateStorage = $stateStorage;
}

private function setEnabledActivity() {
$isEnforced = $this->occonfig->getSystemValue('twofactor_enforced');
if ($isEnforced) {
$this->registry->enableProviderFor($this->provider, $this->myUser);
}
}

public function getBody(): Template {
if($this->myUser->getEMailAddress() === null) {
return new Template('twofactor_email', 'error_email_empty');
}
try {
$this->emailService->send($this->myUser, $this->mySecret);
} catch (\Exception $ex) {
return new Template('twofactor_email', 'error');
}
$this->setEnabledActivity();
$this->stateStorage->persist(
State::verifying($this->myUser, $this->mySecret)
);
$tmpl = new Template('twofactor_email', 'challenge_forFirstConfig');
$tmpl->assign('emailAddress', $this->myUser->getEmailAddress());
return $tmpl;
}
}
24 changes: 19 additions & 5 deletions lib/Provider/Email.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
use OCA\TwoFactorEmail\Service\Email as EmailService;
use OCA\TwoFactorEmail\Service\StateStorage;
use OCA\TwoFactorEmail\Settings\PersonalSettings;

use OCP\Authentication\TwoFactorAuth\IActivatableAtLogin;
use OCP\Authentication\TwoFactorAuth\ILoginSetupProvider;
use OCP\Authentication\TwoFactorAuth\IPersonalProviderSettings;
use OCP\Authentication\TwoFactorAuth\IProvider;
use OCP\Authentication\TwoFactorAuth\IProvidesIcons;
Expand All @@ -20,8 +21,11 @@
use OCP\IUser;
use OCP\Security\ISecureRandom;
use OCP\Template;
use OCP\AppFramework\IAppContainer;
use OCP\IConfig as OCCONFIG;
use OCP\Authentication\TwoFactorAuth\IRegistry;

class Email implements IProvider, IProvidesIcons, IProvidesPersonalSettings {
class Email implements IProvider, IProvidesIcons, IProvidesPersonalSettings, IActivatableAtLogin {
public const STATE_DISABLED = 0;
public const STATE_VERIFYING = 1;
public const STATE_ENABLED = 2;
Expand Down Expand Up @@ -53,14 +57,20 @@ public function __construct(EmailService $emailService,
ISecureRandom $secureRandom,
IL10N $l10n,
IInitialStateService $initialStateService,
IURLGenerator $urlGenerator) {
IURLGenerator $urlGenerator,
IAppContainer $container,
OCCONFIG $occonfig,
IRegistry $registry) {
$this->emailService = $emailService;
$this->stateStorage = $stateStorage;
$this->session = $session;
$this->secureRandom = $secureRandom;
$this->l10n = $l10n;
$this->initialStateService = $initialStateService;
$this->urlGenerator = $urlGenerator;
$this->container = $container;
$this->occonfig = $occonfig;
$this->registry = $registry;
}

private function getSessionKey(): string {
Expand Down Expand Up @@ -142,10 +152,14 @@ public function getPersonalSettings(IUser $user): IPersonalProviderSettings {
}

public function getLightIcon(): String {
return $this->urlGenerator->imagePath(Application::APP_NAME, 'app.svg');
return $this->urlGenerator->imagePath(Application::APP_NAME, 'app.svg') ?? '';
}

public function getDarkIcon(): String {
return $this->urlGenerator->imagePath(Application::APP_NAME, 'app-dark.svg');
return $this->urlGenerator->imagePath(Application::APP_NAME, 'app-dark.svg') ?? '';
}

public function getLoginSetup(IUser $user): ILoginSetupProvider {
return new AtLoginProvider($user, $this->getSecret(), $this->emailService, $this->occonfig, $this->registry, $this, $this->stateStorage);
}
}
9 changes: 9 additions & 0 deletions templates/challenge_forFirstConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<img class="two-factor-icon" src="<?php print_unescaped(image_path('twofactor_email', 'app.svg')); ?>" alt="<?php p($l->t('Two-Factor Email app icon')); ?>">
<p><?php p($l->t('A code has been sent to your email address.')) ?></p>
<p><?php p($l->t('Verify your email address to enable Two-Factor Email (for your account)')) ?></p>
<form method="POST" class="twofactor-email-form">
<input type="tel" minlength="6" maxlength="6" name="challenge" required="required" autofocus autocomplete="one-time-code" autocapitalize="off" placeholder="<?php p($l->t('Authentication code')) ?>">
<button class="primary two-factor-submit" type="submit">
<?php p($l->t('Submit')); ?>
</button>
</form>
3 changes: 3 additions & 0 deletions templates/error_email_empty.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<fieldset class="warning">
<?php p($l->t('Error: you do not have an email address set. Please use a different 2FA method or ask your administrator to set up your email address for you.')); ?>
</fieldset>