Spring Boot Asynchronous OTP Generation and Email Sending

abhi9720

Abhishek Tiwari

Posted on July 30, 2023

Spring Boot Asynchronous OTP Generation and Email Sending

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);
}
Enter fullscreen mode Exit fullscreen mode

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 
}
Enter fullscreen mode Exit fullscreen mode

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 // ... 
}
Enter fullscreen mode Exit fullscreen mode

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;
     }
}
Enter fullscreen mode Exit fullscreen mode
// 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
Enter fullscreen mode Exit fullscreen mode

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;
 }


}

Enter fullscreen mode Exit fullscreen mode
@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\"}");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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/

💖 💪 🙅 🚩
abhi9720
Abhishek Tiwari

Posted on July 30, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related