Documents
Auth Verification Email Flow – /v1/auth/resend-verification-email
Auth Verification Email Flow – /v1/auth/resend-verification-email
Type
Document
Status
Published
Created
Dec 11, 2025
Updated
Dec 11, 2025
Updated by
Dosu Bot

Candidate Email Verification Flow#

This document explains the candidate email verification flow in the StudentHub backend, focusing on the POST /v1/auth/resend-verification-email endpoint. It details routing, controller logic, data flow, verification code generation, email sending, response handling, and security checks to help developers debug cases where users see “code sent” in the UI but never receive an email.


Routing & Controller#

Endpoint and Controller#

  • Endpoint: POST /v1/auth/resend-verification-email
  • Controller: candidate\modules\v1\controllers\AuthController
  • Action: actionResendVerificationEmail
  • File path: candidate/modules/v1/controllers/AuthController.php

The related endpoint for sending the verification code during signup is:

  • Endpoint: POST /v1/auth/register
  • Controller: Same as above
  • Action: actionSignup

Route Definition#

The route is defined in candidate/config/main.php using a Yii2 REST URL rule:

[
    'class' => 'yii\rest\UrlRule',
    'controller' => 'v1/auth',
    'pluralize' => false,
    'patterns' => [
        // ...
        'POST resend-verification-email' => 'resend-verification-email',
        'POST register' => 'signup',
        // ...
    ]
],

See source


Data Flow#

Request Data#

POST /v1/auth/resend-verification-email expects a JSON body with:

  • email (string, required): The candidate's email address.
  • token (string, optional): Recaptcha token (currently not enforced; see Security section).

Example request:

{
  "email": "user@example.com",
  "token": "recaptcha-token-optional"
}

Controller Logic#

  1. The controller retrieves the email and token from the request body.
  2. (Recaptcha verification is present but commented out.)
  3. It looks up the candidate by email.
  4. If the candidate is not found, it returns an error.
  5. If the candidate's email is already verified, it returns an error.
  6. If a verification email was sent less than 1 minute ago, it returns an error with a message indicating the wait time.
  7. If eligible, it calls $candidate->sendVerificationEmail() to generate a new code and send the email.
  8. Returns a JSON response indicating success or error.

See controller logic

Verification Code Generation & Persistence#

  • The verification code is generated by Candidate::generateAuthKey(), which sets the candidate_auth_key field to a random string.
  • The code is persisted in the candidate_auth_key column of the Candidate model.
  • The timestamp for the last sent email is updated in candidate_limit_email.

These operations occur in Candidate::sendVerificationEmail() in common/models/Candidate.php.

See code generation and persistence


Email Sending Logic#

Where the Email is Sent#

The email is sent in Candidate::sendVerificationEmail() (common/models/Candidate.php):

public function sendVerificationEmail() {
    $this->generateAuthKey();
    $this->candidate_limit_email = new Expression('NOW()');
    $this->save(false);

    $email = $this->candidate_new_email ?: $this->candidate_email;

    $ml = new MailLog();
    $ml->to = $email;
    $ml->from = \Yii::$app->params['supportEmail'];
    $ml->subject = 'Please confirm your email address';
    if (!$ml->save()) {
        Yii::error('Failed to save mail log :' . print_r($ml->errors, true));
    }

    $mailer = Yii::$app->mailer->compose(
        ['html' => 'candidate/verify-email-html', 'text' => 'candidate/verify-email-text'],
        ['candidate' => $this]
    )
    ->setFrom([\Yii::$app->params['supportEmail'] => \Yii::$app->params['appName']])
    ->setTo($email)
    ->setSubject('Please confirm your email address');

    if(\Yii::$app->params['elasticMailIpPool']) {
        $mailer->setHeader ("poolName", \Yii::$app->params['elasticMailIpPool']);
    }

    try {
        return $mailer->send();
    } catch (\Symfony\Component\Mailer\Exception\TransportExceptionInterface $e) {
        Yii::error("Failed to send email: " . $e->getMessage());
    } catch (\Exception $e) {
        Yii::error("An error occurred: " . $e->getMessage());
    }
}

See full method

Failure Handling#

  • If saving the mail log fails, the error is logged.
  • If sending the email fails (due to transport or other exceptions), the error is logged via Yii::error, but the exception is not propagated.
  • The method does not return false or throw on failure; it only logs the error.

No queue/job is used for email sending; it is sent synchronously via Yii::$app->mailer.


Response Handling#

The controller action always returns a JSON response. If errors occur (already verified, too soon, or candidate not found), it returns an error response with a code and message. If no errors occur, it returns success with a generic message:

return [
    'operation' => 'success',
    'message' => Yii::t('candidate', 'Please click on the link sent to you by email to verify your account'),
];

Important: The controller does not check the result of sendVerificationEmail(); it assumes success if no exception is thrown. If email sending fails silently (e.g., SMTP issues), the user still sees “code sent” in the UI, but no email is delivered. Errors are only logged on the server.

See response handling


Security / Recaptcha#

There is commented-out code for recaptcha verification in both actionResendVerificationEmail and actionSignup. The code expects a token parameter and would verify it using Yii::$app->reCaptcha->verify($token). Currently, this check is disabled (commented out), so no recaptcha or anti-bot protection is enforced at this endpoint.

See recaptcha code


Debugging: Why “Code Sent” But No Email?#

  • The endpoint always returns success if the candidate exists, is not verified, and is not throttled, regardless of whether the email was actually sent.
  • Email sending failures (e.g., SMTP issues, misconfiguration, blacklisting) are only logged on the server and not surfaced to the client.
  • To debug, check the application logs for errors from sendVerificationEmail() and ensure the mailer is correctly configured.
  • If recaptcha is required in the future, uncomment and enforce the recaptcha check in the controller.

The signup endpoint (POST /v1/auth/register) uses the same logic: after creating the candidate, it calls $model->signup(), which in turn calls sendVerificationEmail() if the email is not already verified.


Summary Diagram#


References: