Improve email verification flow by adding verified email pre-fill, validation handling, and dialog-based verification support.

This commit is contained in:
2025-08-23 22:32:44 +02:00
parent 641083732d
commit 4ccce7e190
5 changed files with 73 additions and 37 deletions
@@ -73,33 +73,23 @@ public class AppealController implements AppealsApi {
log.debug("Retrieving mail by uuid and address");
EmailVerification verifiedMail = sqlSession.getMapper(EmailVerificationMapper.class)
.findByUserAndEmail(appeal.uuid(), appeal.email());
.findByUserAndEmail(appeal.uuid(), appeal.email().toLowerCase());
emailVerificationCompletableFuture.complete(Optional.ofNullable(verifiedMail));
});
Optional<EmailVerification> optionalEmailVerification = emailVerificationCompletableFuture.join();
if (optionalEmailVerification.isEmpty()) {
return ResponseEntity.ok().body(new AppealResponseDto(
appeal.id().toString(),
"Your appeal has been saved and a verification mail has been send, please verify your email " +
"address by clicking the link in your email. Once it is verified we will review your appeal.",
false));
return ResponseEntity.badRequest().build();
}
EmailVerification emailVerification = optionalEmailVerification.get();
if (!emailVerification.verified()) {
return ResponseEntity.ok().body(new AppealResponseDto(
appeal.id().toString(),
"Your appeal has been saved and a verification mail has been resend, please verify your email " +
"address by clicking the link in your email. Once it is verified we will review your appeal.",
false
));
} else {
AppealResponseDto appealResponseDto = new AppealResponseDto(
appeal.id().toString(),
"Your appeal has been submitted. You will be notified when it has been reviewed.",
true);
return ResponseEntity.ok().body(appealResponseDto);
return ResponseEntity.badRequest().build();
}
AppealResponseDto appealResponseDto = new AppealResponseDto(
appeal.id().toString(),
"Your appeal has been submitted. You will be notified when it has been reviewed.",
true);
return ResponseEntity.ok().body(appealResponseDto);
}
@Override
@@ -42,19 +42,20 @@ public class MailVerificationService {
public EmailVerification submitEmail(UUID userUuid, String email) {
String code = generateCode();
Instant now = Instant.now();
final String finalEmail = email.toLowerCase();
CompletableFuture<EmailVerification> future = new CompletableFuture<>();
Connection.getConnection(Databases.DEFAULT)
.runQuery(sql -> {
.runQuery(sql -> {
EmailVerificationMapper mapper = sql.getMapper(EmailVerificationMapper.class);
EmailVerification existing = mapper.findByUserAndEmail(userUuid, email);
EmailVerification existing = mapper.findByUserAndEmail(userUuid, finalEmail);
EmailVerification toPersist;
if (existing == null) {
toPersist = new EmailVerification(UUID.randomUUID(), userUuid, email, code, false, now, null, now);
toPersist = new EmailVerification(UUID.randomUUID(), userUuid, finalEmail, code, false, now, null, now);
mapper.insert(toPersist);
} else {
mapper.updateCodeAndLastSent(existing.id(), code, now);
toPersist = new EmailVerification(existing.id(), userUuid, email, code, false, existing.createdAt(), null, now);
toPersist = new EmailVerification(existing.id(), userUuid, finalEmail, code, false, existing.createdAt(), null, now);
}
future.complete(toPersist);
});
@@ -82,14 +83,16 @@ public class MailVerificationService {
public EmailVerification resend(UUID userUuid, String email) {
String code = generateCode();
Instant now = Instant.now();
final String finalEmail = email.toLowerCase();
CompletableFuture<EmailVerification> future = new CompletableFuture<>();
Connection.getConnection(Databases.DEFAULT)
.runQuery(sql -> {
.runQuery(sql -> {
EmailVerificationMapper mapper = sql.getMapper(EmailVerificationMapper.class);
EmailVerification existing = mapper.findByUserAndEmail(userUuid, email);
EmailVerification existing = mapper.findByUserAndEmail(userUuid, finalEmail);
if (existing != null) {
mapper.updateCodeAndLastSent(existing.id(), code, now);
future.complete(new EmailVerification(existing.id(), userUuid, email, code, false, existing.createdAt(), null, now));
future.complete(new EmailVerification(existing.id(), userUuid,
finalEmail, code, false, existing.createdAt(), null, now));
} else {
future.complete(null);
}
@@ -102,13 +105,14 @@ public class MailVerificationService {
}
public boolean delete(UUID userUuid, String email) {
final String finalEmail = email.toLowerCase();
CompletableFuture<Boolean> future = new CompletableFuture<>();
Connection.getConnection(Databases.DEFAULT)
.runQuery(sql -> {
EmailVerificationMapper mapper = sql.getMapper(EmailVerificationMapper.class);
EmailVerification existing = mapper.findByUserAndEmail(userUuid, email);
EmailVerification existing = mapper.findByUserAndEmail(userUuid, finalEmail);
if (existing != null) {
mapper.deleteByUserAndEmail(userUuid, email);
mapper.deleteByUserAndEmail(userUuid, finalEmail);
future.complete(true);
} else {
future.complete(false);
@@ -122,7 +126,7 @@ public class MailVerificationService {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setFrom(fromEmail);
helper.setTo(emailVerification.email());
helper.setTo(emailVerification.email().toLowerCase());
helper.setSubject("Your verification code");
helper.setText("Your verification code is: " + emailVerification.verificationCode(), false);
mailSender.send(message);