Setting up registration confirmation via email in 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1173
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    745
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Setting Up Registration Confirmation via Email in 1C-Bitrix

The standard registration in 1C-Bitrix by default either does not require email confirmation or uses the built-in mechanism — depending on the settings of the system.auth.registration component. The problem is that the confirmation email is sent via CEvent::Send() with the NEW_USER_CONFIRM template, which looks like a system notification from 2005. Reworking this mechanism to meet modern requirements — transactional email, branded template, proper handling of repeat requests — requires intervention at several points in the system.

How the Standard Mechanism Works

During registration with the EMAIL_CONFIRMATION parameter enabled, the system.auth.registration component creates a user with ACTIVE = N and sends an email via the NEW_USER_CONFIRM event. The email contains a link of the form:

/bitrix/[email protected]&checkword=HASH

When the link is followed, the confirm.php script verifies the CHECKWORD from the b_user table and activates the account (ACTIVE = Y).

CHECKWORD is stored directly in b_user.CHECKWORD — one field for everything. If the user requests a repeat email, the old CHECKWORD is overwritten.

Custom Email Template

The email template is edited in Settings → Mail Events → NEW_USER_CONFIRM. For a branded HTML email:

  1. Create a template of type NEW_USER_CONFIRM with a full HTML body
  2. Use the variables: #EMAIL#, #LOGIN#, #NAME#, #CONFIRM_CODE#, #SITE_URL#
  3. For correct rendering in email clients — table-based layout, inline styles

If a transactional provider (SendGrid, Mailgun, Postmark) is needed instead of PHP mail() — connect it via the main module. Installing a custom send handler:

// In init.php or a module
AddEventHandler('main', 'OnBeforeEventSend', function(array &$eventFields, array &$message, array $siteData) {
    if ($message['EVENT_NAME'] === 'NEW_USER_CONFIRM') {
        // Intercept and send via SendGrid API
        SendGridMailer::send(
            to: $eventFields['EMAIL'],
            subject: $message['SUBJECT'],
            html: $message['BODY']
        );
        return false; // Cancel standard send
    }
});

Resending the Email

The standard component does not provide a "Resend" button. Adding one:

// /local/components/local/auth.resend-confirm/class.php
if ($_POST['resend_email'] ?? false) {
    $email = trim($_POST['email'] ?? '');

    $user = \Bitrix\Main\UserTable::getList([
        'filter' => ['EMAIL' => $email, 'ACTIVE' => 'N'],
        'select' => ['ID', 'LOGIN', 'NAME', 'CHECKWORD', 'CHECKWORD_TIME'],
        'limit'  => 1,
    ])->fetch();

    if (!$user) {
        $this->addError('User not found or already activated');
        return;
    }

    // Rate limit: no more than 1 email in 5 minutes
    $lastSent = strtotime($user['CHECKWORD_TIME']);
    if (time() - $lastSent < 300) {
        $this->addError('Email already sent. Please wait 5 minutes.');
        return;
    }

    // Generate new checkword
    $newCheckword = md5(uniqid('', true));
    \CUser::Update($user['ID'], ['CHECKWORD' => $newCheckword]);

    \CEvent::Send('NEW_USER_CONFIRM', SITE_ID, [
        'EMAIL'        => $email,
        'LOGIN'        => $user['LOGIN'],
        'NAME'         => $user['NAME'],
        'CONFIRM_CODE' => $newCheckword,
        'SITE_URL'     => SITE_SERVER_NAME,
    ]);

    $this->result['SUCCESS'] = true;
}

Link Expiry

By default CHECKWORD has no expiry — a link from a month-old email will still work. To limit the lifespan:

In the handler for confirm.php (or the overridden confirmation component) check CHECKWORD_TIME:

$user = \Bitrix\Main\UserTable::getList([
    'filter' => ['LOGIN' => $login, 'CHECKWORD' => $hash, 'ACTIVE' => 'N'],
    'select' => ['ID', 'CHECKWORD_TIME'],
])->fetch();

if (!$user) {
    ShowError('Link is invalid');
    return;
}

$checkwordAge = time() - strtotime($user['CHECKWORD_TIME']);
if ($checkwordAge > 86400 * 3) { // 3 days
    ShowError('Link has expired. <a href="/resend-confirm/">Request a new email</a>');
    return;
}

\CUser::Confirm($login, $hash);

Auto-deletion of Unactivated Accounts

Users who have not confirmed their email within N days clutter the database. An agent runs daily to delete them:

function DeleteUnconfirmedUsers(): string
{
    $cutoffDate = (new \DateTime())->modify('-7 days')->format('Y-m-d H:i:s');

    $res = \Bitrix\Main\UserTable::getList([
        'filter' => ['ACTIVE' => 'N', '<DATE_REGISTER' => $cutoffDate],
        'select' => ['ID'],
    ]);

    while ($row = $res->fetch()) {
        \CUser::Delete($row['ID']);
    }

    return __FUNCTION__ . '();';
}

Register the agent in the module via \Bitrix\Main\Agent.

Scope of Work

  • Custom HTML email template NEW_USER_CONFIRM
  • Transactional provider connection (optional)
  • Resend component with rate limiting
  • Confirmation link expiry enforcement
  • Agent for auto-deletion of unactivated accounts

Timelines: 3–5 days for a basic rework with a custom template and resend functionality. 1–1.5 weeks when connecting a transactional provider and fully reworking the UX.