Skip to content

[4.x]: Custom 2FA implementation not redirecting on CP login (shows undefined error) #18134

@ks-arshitk

Description

@ks-arshitk

What happened?

Description

I have implemented a custom 2FA module in Craft CMS 4.15.2.
Frontend 2FA flow is working correctly, but on the Control Panel login, after entering a valid username & password, Craft should redirect to my custom 2FA screen.

Instead of redirecting, the CP shows a plain "undefined" error.
No errors appear in storage/logs/error.log or info.log.

The expected CP redirect page exists at:
craft/modules/TwoFactorAuth/src/templates/index.twig

Steps to reproduce

  1. Create custom module (folder: modules/TwoFactorAuth)
  2. Enable 2FA for a user
  3. Login to Control Panel
  4. After entering credentials, instead of redirecting to two-factor-auth, CP shows "undefined"

Expected behavior

  • After CP login, Craft should redirect to:
    cpUrl('two-factor-auth')
  • The modules/TwoFactorAuth/templates/index.twig page should load normally.

Actual behavior

  • The CP shows "undefined" on white screen
  • No PHP/log errors
  • handleLoginAttempt() is triggered, but redirect does not render the template.

Module code

`
class TwoFactorAuth extends Module {
public static $instance;
public function init()
{
parent::init();

    self::$instance = $this;

    Craft::setAlias('@twofactorauth', __DIR__);

    $this->setComponents([
        'twoFactor' => TwoFactor::class,
    ]);

    // Register URL rules
    Craft::$app->getUrlManager()->addRules([
        'two-factor-auth/verify-code' => 'two-factor-auth/auth/verify-code',
        'two-factor-auth/verify-link/<token:[a-zA-Z0-9]+>' => 'two-factor-auth/auth/verify-link',
    ], false);

    // Intercept login to check for 2FA
    Event::on(
        User::class,
        User::EVENT_AFTER_LOGIN,
        function(UserEvent $event) {
            $user = Craft::$app->users->getUserById($event->identity->id);

            if ($user) {
                // Get the service instance
                $service = self::getInstance()->twoFactor;

                // Handle the login attempt
                $service->handleLoginAttempt($user);
            }
        }
    );

    // Register CP template roots for Two Factor Auth module
    Event::on(
        View::class,
        View::EVENT_REGISTER_CP_TEMPLATE_ROOTS,
        function(RegisterTemplateRootsEvent $event) {
            $event->roots['two-factor-auth'] = __DIR__ . '/templates';
        }
    );
}

public static function getInstance(): ?TwoFactorAuth
{
    return self::$instance;
}

}
`

`
class TwoFactor extends Component
{
public static function handleLoginAttempt($user)
{
if (Craft::$app->session->get('skip2FACheck')) {
return;
}

    if (self::isTwoFactorEnabled($user)) {
        if (Craft::$app->getRequest()->getIsCpRequest()) {
            Craft::$app->response->redirect(UrlHelper::cpUrl('two-factor-auth'))->send();
        } else {
            Craft::$app->response->redirect(UrlHelper::siteUrl('two-factor-auth'))->send();
        }

        Craft::$app->session->set('pending2FAUserId', $user->id);

        self::generateAndSend2FAToken($user);

        Craft::$app->user->logout(false);
    }
}

}
`

Craft CMS version

4.15.2

PHP version

8.0.30

Operating system and version

No response

Database type and version

mysql

Image driver and version

No response

Installed plugins and versions

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions