August 6, 2025

SpringBoot – Email Verification (Code With Me)

In this tutorial, you will learn how to enable email verification on Spring boot application.

Email Verification Workflow in SpringBoot
Email Verification Workflow in SpringBoot

The Flow

  1. User fills and submits registration form
  2. UserService receives and saves the user information with accountVerified set to false
  3. UserService creates and saves a new SecureToken
  4. UserService sends a verification email to user along with a link having the secure token
  5. The user receives the email and clicks on the verification link
  6. UserServices receives the request
  7. UserService extracts the token
  8. Check if the token is valid (exists)
  9. Verifies user by setting the accountVerified field to True
  10. Removes the secure token

 

Step1 – Setup Your Dependencies

  1. Add the springboot-starter-mail dependency
  2. Add the commons-lang3 dependency (we need the BooleanUtils module from here)
  3. We would also need com.sun.xml.messaging.saaj
  4. Also make sure you have the MySQL dependency

 

Step 2 – Create the SecureToken model

Create the SecureToken model in the security/models package.

These SecureToken model will have the following fields

    private Long id;
    private String token;
    private LocalDateTime expiredAt;
    private User user;

 

Step 3 – Update the User model to include these fields

    • email
    • accountVerified (boolean)
    • loginDisabled (boolean)
    • tokens (set of SecureToken)
    • setter for loginDisabled
    • make the User model extend Auditable

 

Step 4 – Create EmailContext

Create the mailing package and inside it:

  • create the AbstractEmailContext ( an abstract class whose constructor returns a hashmap of email properties)

It would have the following fields:

    private String from;
    private String to;
    private String subject;
    private String email;
    private String templateLocation;
    private Map<String, Object> context;

In addition to the getters and the setters, this class would have :

  • a put() method the puts item into the context
  • an init() method to with an empty body (for performing some initial configuration)
  • a constructor that initializes an empty context

these are give below:

public AbstractEmailContext() {
    this.context = new HashMap<>();
}

public <T> void init(T context){
    //we can do any common configuration setup here
    // like setting up some base URL and context
}

public Object put(String key, Object value) {
    return key ==null ? null : this.context.put(key.intern(),value);
}

 

Step 5 – Create the AccountVerificationEmailContext

  • create the AccountVerificationEmailContext (extends the AbstractEmailContext)
private String token;

@Override
public <T> void init(T context){
    User user = (User) context;
    put("firstName", user.getFirstname());
    setTemplateLocation("mailing/email-verification");
    setSubject("Complete your registration");
    setFrom("[email protected]");
    setTo(user.getEmail());
}

public void setToken(String token) {
    this.token = token;
    put("token", token);
}

public void buildVerificationUrl(final String baseURL, final String token){
    final String url= UriComponentsBuilder.fromHttpUrl(baseURL)
            .path("/register/verify").queryParam("token", token).toUriString();
    put("verificationURL", url);
}

 

Step 7 – Create and implement the EmailService

Also inside the mailing package:

  • EmailService interface (declares sendMail() method)
void sendMail(final AbstractEmailContext email) throws javax.mail.MessagingException;

 

  • DefaultEmailService (implements the EmailService and  the sendMail() method). The body of the sendMail() function is given below:
MimeMessage message = emailSender.createMimeMessage();
MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(message,
        MimeMessageHelper.MULTIPART_MODE_MIXED_RELATED,
        StandardCharsets.UTF_8.name());
Context context = new Context();
context.setVariables(email.getContext());
String emailContent = templateEngine.process(email.getTemplateLocation(), context);

mimeMessageHelper.setTo(email.getTo());
mimeMessageHelper.setSubject(email.getSubject());
mimeMessageHelper.setFrom(email.getFrom());
mimeMessageHelper.setText(emailContent, true);
emailSender.send(message);

You will also need to  need to:

  • wire in the JavaMailSender and SpringTemplateEngine
  • add the thymeleaf-extras-springsecurity5 and the spring-boot-starter-thymeleaf dependencies

The context used here is coming from org.thymeleaf.context

 

Step 8 – Create the SecureTokenRepository and Service

  1. Setup the SecureTokenRepository which should contain findByToken() and removeToken() methods
  2. Create the SecureTokenService interface which would expose the CRUD SecureToken operations
  3. Create the DefaultSecureTokenService (implements the SecureTokenService and implements the methods)

 

Step 9 – Add the mailing properties to the application.properties file

  • Create the app using a Google account and obtain the mailing properties. Follow these steps:
    • Open Gmail
    • Click your profile avatar at the upper right
    • Select “Manager Your Google Account”
    • Click on Security
    • 2-Step verification
    • Scroll down and select App Passwords (if you don’t see App Passwords, scroll down and under Looking for Something Else, select Search Google Account. Type App Passwords and then select it)
    • Create an new app and then get the credentials
  • Add the mailing values to the properties file
  • Remember to add the site.base.url.https property with the correct port.

 

Step 10 – Implement the sendRegistrationConfirmationEmail Function

  1. We would need to write a few custom exceptions. We put them an an exception package.
    • InvalidTokenException
    • UnkownIdentifierException
    • UserAlreadyExistsException
  2. Update the UserRepository to include the findByEmail() method
  3. Update the UserService:
    • Write the checkIfUserExists() function
    • Rename the save() method to register()
    • Write the sendRegistrationConfirmationEmail() method
    • The register method should first check if user exists, save the user and then call the sendRegistrationConfirmationEmail() method
    • Update the UserController: modify the addNew() method to call the service’s register() method and send a success message

 

Step 11 – Create Html templates and Update the RegistrationPage

  1. Create the mailing directory in the templates folder and create the email-verification.html template. Get it here.
  2. Create the registrationSuccessful template inside the security folder. Get it here (this is only for FleetMS and does not apply to InventoryMS).
  3. Modify the Registration template form to include both username and email
  4. Modify the password/confirmPassword fields so that we can check that the two does not match using script
  5. Add the script for checking password.

For InventoryMS: we would create the success pages in the React UI app

 

Step 12 – Update the ApplicationSecurityConfig

  1. Update the ApplicationSecurityConfig to allow access to the /usersAddNew route
  2. Add the /register/verify url to the ApplicationSecurityConfig file
  3. Test(5) – Test the Verification Email functionality

 

Step 13 – Setup the /verify URL

  1. Write the verifyUser() method which takes a token and validates that token exists and it is the user’s token. It
    • tests for empty token
    • checks for empty user
  2. Create a registrationController in the controllers package and provide the method for /verify url. This method does the following:
    • checks for empty token
    • calls the userService’ verifyUser() method and catches any exception
    • redirects user to login page with a success message

 

Step 14 – Update UserPrincipal to use accountVerified and Create Messages

  1. Update the isEnabled method of the UserPrincipal to return user.isAccountVerified()
  2. Create the message bundle
  3. Test(6) – Test the /verify function

 

Happy coding!!!

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments