startPasswordReset method

Future<UuidValue> startPasswordReset(
  1. Session session, {
  2. required String email,
  3. required Transaction transaction,
})

Sends out a password reset email for the given account, if it exists and returns a password reset request ID, which can be used to complete the reset.

Each reset request will be logged to the database outside of the transaction and can not be rolled back, so the throttling will always be enforced.

Can throw EmailPasswordResetTooManyAttemptsException if the user has made too many attempts to request a password reset.

Can throw EmailPasswordResetEmailNotFoundException if the email account does not exist. In this case, the caller should not expose this detail to the client, so that this method can not be misused to check which emails are registered.

Implementation

Future<UuidValue> startPasswordReset(
  final Session session, {
  required String email,
  required final Transaction transaction,
}) async {
  email = email.normalizedEmail;

  if (await _rateLimitUtil.hasTooManyAttempts(session, nonce: email)) {
    throw EmailPasswordResetTooManyAttemptsException();
  }

  final account = await EmailAccount.db.findFirstRow(
    session,
    where: (final t) => t.email.equals(email),
    transaction: transaction,
  );

  if (account == null) {
    throw EmailPasswordResetEmailNotFoundException();
  }

  await deletePasswordResetRequests(
    session,
    olderThan: Duration.zero,
    emailAccountId: account.id!,
    transaction: transaction,
  );

  final verificationCode = _config.passwordResetVerificationCodeGenerator();

  final challenge = await _challengeUtil.createChallenge(
    session,
    verificationCode: verificationCode,
    transaction: transaction,
  );

  final resetRequest = await EmailAccountPasswordResetRequest.db.insertRow(
    session,
    EmailAccountPasswordResetRequest(
      emailAccountId: account.id!,
      challengeId: challenge.id!,
      createdAt: clock.now(),
    ),
    transaction: transaction,
  );

  final passwordResetRequestId = resetRequest.id!;
  _config.sendPasswordResetVerificationCode?.call(
    session,
    email: email,
    passwordResetRequestId: passwordResetRequestId,
    verificationCode: verificationCode,
    transaction: transaction,
  );

  return passwordResetRequestId;
}