Sending Email via Gmail with 2-Step Authentication from AngularJS Frontend to Spring Boot Backend

Sending Email via Gmail with 2-Step Authentication from AngularJS Frontend to Spring Boot Backend

Sending Email via Gmail with 2-Step Authentication from AngularJS Frontend to Spring Boot Backend

In this tutorial, I’ll show you how to configure Spring Boot to set up a REST Controller to send emails via Gmail. Plus, if you have 2-Step Authentication set up, which you should if you don’t, then I’ll take you through a couple extra steps. Then, I’ll show you how you can tie everything together with a frontend written in AngularJS 1.6.

Firstly, make sure you have your Spring Boot application’s Maven POM configured correctly:

	<dependencies>
                ...
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-mail</artifactId>
		</dependency>
                ...
	</dependencies>

Now, you need to configure your application.properties. This is what needs to be added:

spring.mail.host=smtp.gmail.com
spring.mail.port=587
spring.mail.username=testing123@gmail.com
spring.mail.password=abcdef123456
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true

Of course, you will need to substitute your actual email address and your actual password to your Gmail account. Now, if you have 2-Step Authentication (ie, 2-Step Verification) configured on your Gmail account, you will need to create an application specific password. If you do not, you will see an error like the following:

{
"timestamp": 1521418235937,
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.mail.MailAuthenticationException",
"message": "Authentication failed; nested exception is javax.mail.AuthenticationFailedException: 535-5.7.8 Username and Password not accepted. Learn more at\n535 5.7.8 https://support.google.com/mail/?p=BadCredentials i79sm6827128qkh.11 - gsmtp\n",
"path": "/email/"
}

With that warning in place, lets just go ahead and mitigate ever seeing the aforementioned issue by creating an application specific password. Please go to the following page to create one:

Next, you are ready to create a REST controller to send emails. Here is an example:

/**
 * Copyright (c) 2017 CoySoft, All Rights Reserved
 * Contains proprietary and confidential information owned by CoySoft.
 */
package com.coysoft.email;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import javax.ws.rs.core.Response;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.coysoft.email.model.EmailModel;

/**
 * Service to send emails.
 *
 * @author kcoy - Kevin Coy
 * @version 1.0
 */
@RestController
@RequestMapping("/email")
public class EmailController {

    private static final Logger LOGGER = LoggerFactory.getLogger(EmailController.class);
    
    @Autowired
    private JavaMailSender javaMailSender;
    
    @RequestMapping(value = "/", method = RequestMethod.POST, consumes = "application/json")
    public Response sendEmail(@RequestBody EmailModel emailModel) {
        LOGGER.info("Sending email");
        
        MimeMessage mail = javaMailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(mail, true);
            helper.setTo("testing123@gmail.com");
            helper.setReplyTo(emailModel.getEmail());
            helper.setFrom(emailModel.getEmail());
            helper.setSubject(emailModel.getSubject());
            helper.setText("From: " + emailModel.getUsername() + "\n" + emailModel.getMessage());
        } catch (MessagingException e) {
            LOGGER.error("Failed to send email: " + emailModel.toString(), e);
        } finally {}
        javaMailSender.send(mail);
        
        return Response.accepted().build();
    }
}

This controller uses an object called EmailModel. Using Lombok, the class is very simple and compact:

@XmlRootElement
@Data
@AllArgsConstructor
@NoArgsConstructor
public class EmailModel {

    private String username;
    
    private String email;
    
    private String subject;
    
    private String message;
}

Now, you are ready to start your Spring Boot application and test the endpoint. Using Postman, we can easily test like so:

Now, we know the backend of the application is up and running. Lets move on to getting our AngularJS code to use this. We can create a service called email-service.js like this:

(function( angular ) {
    angular.module( 'contact' )
      .factory( 'email', function( $filter, $http) {
        var baseUrl = 'http://localhost:8181/email/',
        filter = $filter( 'filter' );
  
        return {
          submit  : submit
        };
  
  
        function submit( emailPayload ) {
          var p1 = $http.post( 'http://localhost:8181/email/', emailPayload);
          return p1;
        }

      } );

    })( angular );      

We can take this further by creating a contact page to call this service. Here is the controller contact-us-main.component.js:

(function( angular ) {
  angular.module( 'contact' )
    .component( 'contactUsMain', {
      templateUrl: 'app/contact-us/contact-us-main.component.html',
      controller : ContactUsMainController
    } );

  function ContactUsMainController( $log, $state, $stateParams, email ) {
    var ctrl = this;

    ctrl.reset = reset;
    ctrl.submit = submit;
    
    reset();
    
    function reset() {
      console.log("console log reset");
      $log.debug("in reset");
      ctrl.model = {};

      if(ctrl.form) { 
        ctrl.form.$setPristine();
      }
    }

    function submit(model) {
        email.submit(model).then(
            function(response) {
                alert('Thanks for contacting us!');
            },
            function(response) {
                alert('We are currently experience a technical issue. Please try again in a moment');
            }
        )
            
    }
  }
})( angular );

Next, here is the view contact-us-main.component.html:

<div class="row">
  <div class="col-md-12 container" ui-view>
    <h3>Contact Us</h3>

    <form class="form-horizontal" name="$ctrl.form" novalidate ng-show="!token">
      <div class="form-group">
          <div class="col-md-2">
              <label>Your Name</label>
          </div>
          <div class="col-md-5">
              <input type="text" name="username" class="form-control" ng-model="$ctrl.model.username" required>
              <div class="alert alert-danger" ng-show="($ctrl.form.username.$touched || $ctrl.form.$submitted) && $ctrl.form.username.$error.required">
                  Your name is required
              </div>
          </div>
      </div>
      <div class="form-group">
        <div class="col-md-2">
            <label>Your Email Address</label>
        </div>
        <div class="col-md-5">
            <input type="email" name="email" class="form-control" ng-model="$ctrl.model.email" ng-pattern="/^[_a-zA-Z0-9]+(\.[_a-zA-Z0-9]+)*@[a-z0-9-]+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,24})$/" required>
            <div class="alert alert-danger" ng-show="($ctrl.form.email.$touched || $ctrl.form.$submitted) && $ctrl.form.email.$error.required">
                Your email address is required
            </div>
            <div class="alert alert-danger" ng-show="($ctrl.form.email.$touched || $ctrl.form.$submitted) && !$ctrl.form.email.$valid">
              Your email is not valid
           </div>            
        </div>
      </div>   
      <div class="form-group">
          <div class="col-md-2">
              <label>Subject</label>
          </div>
          <div class="col-md-5">
              <input type="text" name="subject" class="form-control" ng-model="$ctrl.model.subject" required>
              <div class="alert alert-danger" ng-show="($ctrl.form.subject.$touched || $ctrl.form.$submitted) && $ctrl.form.subject.$error.required">
                  Subject is required
              </div>
          </div>
      </div>   
      <div class="form-group">
          <div class="col-md-2">
              <label>Your Message</label>
          </div>
          <div class="col-md-5">
              <textarea rows="5" name="message" class="form-control" ng-model="$ctrl.model.message" ng-minlength="25" required></textarea>
              <div class="alert alert-danger" ng-show="($ctrl.form.message.$touched || $ctrl.form.$submitted) && $ctrl.form.message.$error.required">
                Your message is required
              </div>
              <div class="alert alert-danger" ng-show="($ctrl.form.message.$touched || $ctrl.form.$submitted) && !$ctrl.form.message.$valid">
                Your message is not valid. Minimum of 25 characters.
              </div>           
          </div>
      </div>      
      <div class="form-group">
          <div class="col-md-offset-2 col-md-5">
              <input type="submit" value="Submit" class="btn btn-primary" ng-click="$ctrl.submit($ctrl.model)" ng-disabled="$ctrl.form.$invalid"/>
              <button class="btn btn-default" ng-click="$ctrl.reset()">Reset</button>
          </div>
      </div>                          
  </form>

  <div class="alert alert-success" ng-show="token">
      You have successfully registered with token: {{token}}!!!
  </div>

  </div>
</div>

Please note that the default pattern used by AngularJS to check email addresses is not correct in my opinion (and many others). Hence, a custom regular expression is used instead: /^[_a-zA-Z0-9]+(\.[_a-zA-Z0-9]+)*@[a-z0-9-]+(\.[a-zA-Z0-9-]+)*(\.[a-zA-Z]{2,24})$/.

Here is what it looks like in the browser:

Contact Us Page to send Email

Contact Us Page to send Email

I hope this gets you well on your way to being able to send emails from an AngularJS web application to a Spring Boot backend.

Leave a Reply

Your email address will not be published. Required fields are marked *