completePasswordReset static method

Future<UuidValue> completePasswordReset(
  1. Session session, {
  2. required UuidValue passwordResetRequestId,
  3. required String verificationCode,
  4. required String newPassword,
  5. Transaction? transaction,
})

Returns the auth user ID for the successfully changed password

Throws an EmailAccountPasswordResetRequestNotFoundException in case no reset request could be found for passwordResetRequestId. Throws an EmailAccountPasswordResetRequestExpiredException in case the reset request has expired. Throws an EmailAccountPasswordPolicyViolationException in case the password does not confirm to the configured policy. Throws an EmailAccountPasswordResetRequestUnauthorizedException in case the verificationCode is not valid.

In case of an invalid verificationCode, the failed password reset completion will be logged to the database outside of the transaction and can not be rolled back.

Implementation

static Future<UuidValue> completePasswordReset(
  final Session session, {
  required final UuidValue passwordResetRequestId,
  required final String verificationCode,
  required final String newPassword,
  final Transaction? transaction,
}) async {
  return DatabaseUtil.runInTransactionOrSavepoint(
    session.db,
    transaction,
    (final transaction) async {
      final resetRequest = await EmailAccountPasswordResetRequest.db.findById(
        session,
        passwordResetRequestId,
        transaction: transaction,
      );

      if (resetRequest == null) {
        throw EmailAccountPasswordResetRequestNotFoundException();
      }

      if (resetRequest.isExpired) {
        await EmailAccountPasswordResetRequest.db.deleteRow(
          session,
          resetRequest,
          // passing no transaction, so this will not be rolled back
        );

        throw EmailAccountPasswordResetRequestExpiredException();
      }

      if (!EmailAccounts.config.passwordValidationFunction(newPassword)) {
        throw EmailAccountPasswordPolicyViolationException();
      }

      if (await _hasTooManyPasswordResetAttempts(
        session,
        passwordResetRequestId: resetRequest.id!,
      )) {
        await EmailAccountPasswordResetRequest.db.deleteRow(
          session,
          resetRequest,
          // passing no transaction, so this will not be rolled back
        );

        throw EmailAccountPasswordResetTooManyAttemptsException();
      }

      if (!await EmailAccountSecretHash.validateHash(
        value: verificationCode,
        hash: resetRequest.verificationCodeHash.asUint8List,
        salt: resetRequest.verificationCodeSalt.asUint8List,
      )) {
        throw EmailAccountPasswordResetRequestUnauthorizedException();
      }

      await EmailAccountPasswordResetRequest.db.deleteRow(
        session,
        resetRequest,
        transaction: transaction,
      );

      final account = (await EmailAccount.db.findById(
        session,
        resetRequest.emailAccountId,
        transaction: transaction,
      ))!;

      final newPasswordHash = await EmailAccountSecretHash.createHash(
        value: newPassword,
      );

      await EmailAccount.db.updateRow(
        session,
        account.copyWith(
          passwordHash: ByteData.sublistView(newPasswordHash.hash),
          passwordSalt: ByteData.sublistView(newPasswordHash.salt),
        ),
        transaction: transaction,
      );

      return account.authUserId;
    },
  );
}