craftcms / craftcms/cms

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

Open
#18,134 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug craft4
Dominant language
PHP
Stars
3.6k
Forks
705
Avg merge
1d 5h
Merged PRs (30d)
134

Description

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

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the custom module's handleLoginAttempt() entry point and the Control Panel login flow in Craft CMS 4.15.2. Inspect how the redirect, response send, session state, and modules/TwoFactorAuth/templates/index.twig interact, then reproduce the login with the supplied steps. Done means the CP reaches the two-factor-auth page instead of displaying undefined.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
authentication, backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
30/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.