Spring Boot Asynchronous OTP Generation and Email Sending
Abhishek Tiwari
Posted on July 30, 2023
In modern web applications, security is of paramount importance. One-time passwords (OTPs) are widely used for secure user authentication and transaction verification. However, generating OTPs and sending them via email can be a time-consuming process, potentially affecting the responsiveness of your application. To address this, we will learn how to implement asynchronous OTP generation and email sending in a Spring Boot application.
By using CompletableFuture
and the @Async
annotation, we can achieve non-blocking behavior, allowing our application to handle other tasks while generating OTPs and sending emails in the background.
Prerequisites:
- Basic knowledge of Java and Spring Boot.
- A working Spring Boot project with a configured JavaMailSender for sending emails.
- An understanding of RESTful APIs and Spring controllers.
This is blog is part of this Banking Portal Api https://github.com/abhi9720/BankingPortal-API/
1. Project Setup: Before we begin, make sure you have set up a Spring Boot project and have a basic understanding of Spring Boot configurations.
2. Implementing Asynchronous OTP Generation: In this step, we will create a service class responsible for OTP generation and database operations.
Creating the OTPService Interface:
public interface OTPService {
String generateOTP(String accountNumber);
CompletableFuture<Boolean> sendOTPByEmail(String email, String name, String accountNumber, String otp);
boolean validateOTP(String accountNumber, String otp);
}
Implementing the OTPServiceImpl Class:
@Service
public class OTPServiceImpl implements OTPService {
@Autowired
private EmailService emailService;
@Override
public String generateOTP(String accountNumber) {
Random random = new Random();
int otpValue = 100_000 + random.nextInt(900_000);
String otp = String.valueOf(otpValue);
// Save the new OTP information in the database
OtpInfo otpInfo = new OtpInfo();
otpInfo.setAccountNumber(accountNumber);
otpInfo.setOtp(otp);
otpInfo.setGeneratedAt(LocalDateTime.now());
otpInfoRepository.save(otpInfo);
return otp;
}
@Override
@Async
public CompletableFuture<Boolean> sendOTPByEmail(String email, String name, String accountNumber, String otp) {
// Compose the email content
String subject = "OTP Verification";
String emailText = emailService.getOtpLoginEmailTemplate(name, "xxx" + accountNumber.substring(3), otp);
CompletableFuture<Void> emailSendingFuture = emailService.sendEmail(email, subject, emailText);
return emailSendingFuture.thenApplyAsync(result -> true)
.exceptionally(ex -> {
ex.printStackTrace();
return false;
});
}
// ... Other Code
}
Configuring Asynchronous Execution: Ensure that the @EnableAsync
annotation is present in your main application class to enable asynchronous processing.
@ SpringBootApplication
@ EnableAsync
public class YourApplication {
// Application setup and configuration code // ...
}
3. Sending OTP via Email Asynchronously: Now, let’s focus on the implementation of the EmailService responsible for sending emails.
Creating the EmailService Interface:
public interface EmailService {
public CompletableFuture<Void> sendEmail(String to, String subject, String text);
public String getOtpLoginEmailTemplate(String name,String accountNumber, String otp) ;
}
Implementing the EmailServiceImpl Class:
@Service
public class EmailServiceImpl implements EmailService {
private final JavaMailSender mailSender;
@Autowired
public EmailServiceImpl(JavaMailSender mailSender) {
this.mailSender = mailSender;
}
@Override
@Async
public CompletableFuture<Void> sendEmail(String to, String subject, String text) {
CompletableFuture<Void> future = new CompletableFuture<>();
try {
MimeMessage message = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true);
helper.setTo(to);
// No need to set the "from" address; it is automatically set by Spring Boot based on your properties
helper.setSubject(subject);
helper.setText(text, true); // Set the second parameter to true to send HTML content
mailSender.send(message);
future.complete(null); // Indicate that the email sending is successful
} catch (MessagingException e) {
e.printStackTrace();
future.completeExceptionally(e); // Indicate that the email sending failed
}
return future;
}
@Override
public String getOtpLoginEmailTemplate(String name, String accountNumber, String otp) {
// Create the formatted email template with the provided values
String emailTemplate = "<div style=\"font-family: Helvetica,Arial,sans-serif;min-width:1000px;overflow:auto;line-height:2\">"
+ "<div style=\"margin:50px auto;width:70%;padding:20px 0\">"
+ "<div style=\"border-bottom:1px solid #eee\">"
+ "<a href=\"https://piggybank.netlify.app/\" style=\"font-size:1.4em;color: #00466a;text-decoration:none;font-weight:600\">piggybank</a>"
+ "</div>"
+ "<p style=\"font-size:1.1em\">Hi, " + name + "</p>"
+ "<p style=\"font-size:0.9em;\">Account Number: " + accountNumber + "</p>"
+ "<p>Thank you for choosing OneStopBank. Use the following OTP to complete your Log In procedures. OTP is valid for 5 minutes</p>"
+ "<h2 style=\"background: #00466a;margin: 0 auto;width: max-content;padding: 0 10px;color: #fff;border-radius: 4px;\">" + otp + "</h2>"
+ "<p style=\"font-size:0.9em;\">Regards,<br />OneStopBank</p>"
+ "<hr style=\"border:none;border-top:1px solid #eee\" />"
+ "<p>piggybank Inc</p>"
+ "<p>1600 Amphitheatre Parkway</p>"
+ "<p>California</p>"
+ "</div>"
+ "</div>";
return emailTemplate;
}
}
// application.properties
spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=example@gmail.com
spring.mail.password=xxxxxxxxxxxxxxxx
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
4. Creating the RESTful API: Now, let’s create a RESTful API to demonstrate the asynchronous OTP generation and email sending process.
// DTO For Receving OTP request
public class OtpRequest {
private String accountNumber;
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
}
@RestController
public class OTPController {
@Autowired
private OTPService otpService;
@PostMapping("/generate-otp")
public ResponseEntity<?> generateOtp(@RequestBody OtpRequest otpRequest) {
String accountNumber = otpRequest.getAccountNumber();
// Fetch the user by account number to get the associated email
User user = userService.getUserByAccountNumber(accountNumber);
if (user == null) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body("User not found for the given account number");
}
// Generate OTP and save it in the database
String otp = otpService.generateOTP(accountNumber);
// Send the OTP to the user's email address asynchronously
CompletableFuture<Boolean> emailSendingFuture = otpService.sendOTPByEmail(user.getEmail(), user.getName(), accountNumber, otp);
// Wait for the email sending process to complete and handle the response
try {
boolean otpSent = emailSendingFuture.get(); // This will block until the email sending is complete
if (otpSent) {
// Return JSON response with success message
return ResponseEntity.ok().body("{\"message\": \"OTP sent successfully\"}");
} else {
// Return JSON response with error message
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("{\"message\": \"Failed to send OTP\"}");
}
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
// Return JSON response with error message
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("{\"message\": \"Failed to send OTP\"}");
}
}
}
5. Testing the Asynchronous OTP Generation and Email Sending: Now that we have implemented the API, you can use tools like Postman to send a POST request to/generate-otp with the required data. The API will return a response indicating the status of the OTP generation and email sending.
For Any Reference Checkout this repo : https://github.com/abhi9720/BankingPortal-API/
Posted on July 30, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.