Building a Spring Boot Rest API to Send Email with Microsoft Graph APIs

/

In today’s digital world, email communication remains a fundamental part of our personal and professional lives. Developers often need to integrate email capabilities into their applications to streamline processes and enhance user experiences. If you’re working with Java and the Spring Boot framework, one powerful way to achieve this is by leveraging Microsoft Graph APIs to send emails. In this blog post, we will share slight insight on the process of building a Spring Boot REST API that sends emails using Microsoft Graph APIs.

Before we begin, ensure steps described in Part-1 & Part-2 are completed.

Prerequisites

  • Java Development Kit (JDK) installed
  • Integrated Development Environment (IDE) like IntelliJ IDEA or Eclipse
  • Basic knowledge of Spring Boot and REST APIs
  • Postman for testing application

OAuth 2.0 Client Credentials Flow

Lets start with understanding client credential flow which will be used in our email application.

OAuth 2.0 is an industry-standard protocol for authorization, and it’s widely used for securing web applications, mobile apps, and APIs. The Client Credentials Flow is one of the OAuth 2.0 grant types that allows applications to access resources on behalf of themselves, rather than a user. Here’s how it works:

  • Client Application: In the Client Credentials Flow, there are no end-users involved. It is typically used by server-side applications or scripts that need to access protected resources. The client application must be registered with an identity provider, such as the Microsoft Identity Platform.
  • Authentication: The client application sends a request to the identity provider, including its client credentials (client ID and client secret) in the request. These credentials are obtained during the registration of the client application.
  • Token Request: The identity provider validates the client credentials and, if they are correct, issues an access token to the client application. This access token represents the client’s authorization to access specific resources.
  • Resource Access: With the obtained access token, the client application can access the protected resources, typically APIs. The access token is included in the authorization header of the API requests.

Project Setup

The first step is to create a simple Spring boot Maven App using Spring Boot Initializr. Add the dependency Spring Web while generating the app. Download the project and unzip it. Then import it into your favorite IDE – Eclipse or IntelliJ IDEA.

Install dependencies

Open pom.xml and update the dependencies section to add below dependencies.

Maven
		<dependency>
			<groupId>com.microsoft.graph</groupId>
			<artifactId>microsoft-graph</artifactId>
			<version>5.0.0</version>
		</dependency>
		<dependency>
			<groupId>com.azure</groupId>
			<artifactId>azure-identity</artifactId>
			<version>1.3.7</version>
		</dependency>

Configure Application

Open application.properties under “src/main/resources” and add below properties. Refer previous posts to know details on how to get client-id, client-secret and tenant-id.

Application Configuration
# OAuth2 properties - O365
spring.security.oauth2.client.registration.azure.client-id=YOUR-CLIENT-ID
spring.security.oauth2.client.registration.azure.client-secret=YOUR-CLIENT-SECRET
microsoft.azure.tenant-id=YOUR-TENANT-ID
spring.mail.username=no-reply@eternalcompute.com

Create a Model

Create a package named “com.slightinsight.emailapi.model” and add the class “EmailRequest.java“.

EmailRequest.java
package com.slightinsight.emailapi.model;

public class EmailRequest {
	
	String subject;
	String message;
	String recipient;

	public String getSubject() {
		return subject;
	}

	public void setSubject(String subject) {
		this.subject = subject;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public String getRecipient() {
		return recipient;
	}

	public void setRecipient(String recipient) {
		this.recipient = recipient;
	}

}

Create a Spring Service

Create an Service class with name “EmailService.java” under package “com.slightinsight.emailapi.service” that encapsulates the logic for sending emails using Microsoft Graph APIs. This class should include methods for sending emails and be initialized with your Microsoft Graph API credentials.

EmailService.java
package com.slightinsight.emailapi.service;

import java.util.LinkedList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.azure.identity.ClientSecretCredential;
import com.azure.identity.ClientSecretCredentialBuilder;
import com.microsoft.graph.authentication.TokenCredentialAuthProvider;
import com.microsoft.graph.models.BodyType;
import com.microsoft.graph.models.EmailAddress;
import com.microsoft.graph.models.ItemBody;
import com.microsoft.graph.models.Message;
import com.microsoft.graph.models.Recipient;
import com.microsoft.graph.models.UserSendMailParameterSet;
import com.microsoft.graph.requests.GraphServiceClient;
import com.slightinsight.emailapi.model.EmailRequest;

import okhttp3.Request;

@Service
public class EmailService {

	private static ClientSecretCredential clientSecretCredential;
	private static GraphServiceClient<Request> appClient;

	@Value("${spring.security.oauth2.client.registration.azure.client-id}")
	private String clientId;

	@Value("${microsoft.azure.tenant-id}")
	private String tenantId;

	@Value("${spring.security.oauth2.client.registration.azure.client-secret}")
	private String clientSecret;
	
	@Value("${spring.mail.username}") 
	private String sender;

	Logger log = LoggerFactory.getLogger(getClass());

	public void sendEmail(EmailRequest emailRequest) {

		// establish the connection with M-Graph
		try {
			initializeGraphForAppOnlyAuth();
		} catch (Exception e) {
			log.error("Error initializing Graph for user auth");
			log.error(e.getMessage());
		}
		
		try {
	        sendMailGraphAPI(emailRequest);
	        log.info("Mail sent successfully.");
	    } catch (Exception e) {
	    	log.error("Error sending mail");
	    	log.error(e.getMessage());
	    }

	}

	private void initializeGraphForAppOnlyAuth() throws Exception {

		log.info("initializing Graph for user auth");
		
		clientSecretCredential = new ClientSecretCredentialBuilder().clientId(clientId).tenantId(tenantId)
				.clientSecret(clientSecret).build();

		if (appClient == null) {
			// Use the .default scope when using app-only auth
			final TokenCredentialAuthProvider authProvider = new TokenCredentialAuthProvider(
					List.of("https://graph.microsoft.com/.default"), clientSecretCredential);

			appClient = GraphServiceClient.builder().authenticationProvider(authProvider).buildClient();
		}

	}
	
	private void sendMailGraphAPI(EmailRequest emailRequest) throws Exception {
		
		log.info("Preparing email");
		
		// Ensure client isn't null
	    if (appClient  == null) {
	        throw new Exception("Graph has not been initialized for user auth");
	    }
	    
	    Message message = new Message();

		message.subject = emailRequest.getSubject();
		
		ItemBody body = new ItemBody();
		body.contentType = BodyType.HTML;
		body.content = emailRequest.getMessage();
		message.body = body;
		
		LinkedList<Recipient> toRecipientsList = new LinkedList<Recipient>();
		Recipient toRecipients = new Recipient();
		EmailAddress emailAddress = new EmailAddress();
		emailAddress.address = emailRequest.getRecipient();
		toRecipients.emailAddress = emailAddress;
		toRecipientsList.add(toRecipients);
		message.toRecipients = toRecipientsList;

	    // Send the message
		log.info("sending email");
		appClient.users(sender)
				.sendMail(UserSendMailParameterSet.newBuilder()
	            .withMessage(message)
	            .build())
	        .buildRequest()
	        .post();
	
	}

}

Create a Spring REST Controller

Create a REST controller with name “EmailController.java” under package “com.slightinsight.emailapi.web” in your Spring Boot application to expose an endpoint for sending emails.

EmailController.java
package com.slightinsight.emailapi.web;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.slightinsight.emailapi.model.EmailRequest;
import com.slightinsight.emailapi.service.EmailService;

@RestController
@RequestMapping("/api/email")
public class EmailController {
	
	@Autowired
	EmailService emailService;
	
	Logger log = LoggerFactory.getLogger(getClass());
	
	@PostMapping("/send")
    public ResponseEntity<String> sendEmail(@RequestBody EmailRequest emailRequest) {
		
		log.info("REST API invoked to send email");
        emailService.sendEmail(emailRequest);
        
        return ResponseEntity.ok("Email sent successfully");
    }


}

Email API : Test Run

We can run this app by issuing below command:

Command
mvn spring-boot:run

Once the app is running, try sending the email by using below API endpoint url:

URL
http://localhost:8080/api/email/send

As this is a post method, we have send request data in below JSON format:

Request
{
    "subject" : "Hello! Testing Graph API Email",
    "message" : "Dear User, <br/><br/> This is a test email, please ignore...! <br/><br/> Thanks, <br/> Slight Insight",
    "recipient" : "slightinsight.test@gmail.com"
}

If everything goes well, the recipient will receive the email in the inbox from no-reply mailbox and we should receive response as below in postman:

Response
Email sent successfully

Complete Code : Git Repository

Update to move Microsoft Graph setup to Configuration

Create MailConfig.java file under com.slightinsight.emailapi.config package.

MailConfig.java
package com.slightinsight.emailapi.config;

import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.azure.identity.ClientSecretCredentialBuilder;
import com.azure.identity.ClientSecretCredential;
import com.microsoft.graph.authentication.TokenCredentialAuthProvider;
import com.microsoft.graph.requests.GraphServiceClient;

import okhttp3.Request;

@Configuration
public class MailConfig {

    @Value("${spring.security.oauth2.client.registration.azure.client-id}")
    private String clientId;

    @Value("${microsoft.azure.tenant-id}")
    private String tenantId;

    @Value("${spring.security.oauth2.client.registration.azure.client-secret}")
    private String clientSecret;

    private GraphServiceClient<Request> initializeGraphForAppOnlyAuth() {

        ClientSecretCredential clientSecretCredential = new ClientSecretCredentialBuilder().clientId(clientId)
                .tenantId(tenantId)
                .clientSecret(clientSecret).build();

        // Use the .default scope when using app-only auth
        final TokenCredentialAuthProvider authProvider = new TokenCredentialAuthProvider(
                List.of("https://graph.microsoft.com/.default"), clientSecretCredential);

        return GraphServiceClient.builder().authenticationProvider(authProvider).buildClient();
    }

    @Bean
    public GraphServiceClient<Request> mailClient() {
        return initializeGraphForAppOnlyAuth();
    }

}

Now let’s update EmailService.java to delete the redundant code.

EmailService.java
package com.slightinsight.emailapi.service;

import java.util.LinkedList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.azure.identity.ClientSecretCredential;
import com.azure.identity.ClientSecretCredentialBuilder;
import com.microsoft.graph.authentication.TokenCredentialAuthProvider;
import com.microsoft.graph.models.BodyType;
import com.microsoft.graph.models.EmailAddress;
import com.microsoft.graph.models.ItemBody;
import com.microsoft.graph.models.Message;
import com.microsoft.graph.models.Recipient;
import com.microsoft.graph.models.UserSendMailParameterSet;
import com.microsoft.graph.requests.GraphServiceClient;
import com.slightinsight.emailapi.model.EmailRequest;

import okhttp3.Request;

@Service
public class EmailService {

	private static ClientSecretCredential clientSecretCredential;
	private static GraphServiceClient<Request> appClient;

	@Value("${spring.security.oauth2.client.registration.azure.client-id}")
	private String clientId;

	@Value("${microsoft.azure.tenant-id}")
	private String tenantId;

	@Value("${spring.security.oauth2.client.registration.azure.client-secret}")
	private String clientSecret;
	
	@Value("${spring.mail.username}") 
	private String sender;

	@Autowired
	private GraphServiceClient<Request> mailClient;

	Logger log = LoggerFactory.getLogger(getClass());

	public void sendEmail(EmailRequest emailRequest) {

		// establish the connection with M-Graph
		try {
			initializeGraphForAppOnlyAuth();
		} catch (Exception e) {
			log.error("Error initializing Graph for user auth");
			log.error(e.getMessage());
		}
		
		try {
	        sendMailGraphAPI(emailRequest);
	        log.info("Mail sent successfully.");
	    } catch (Exception e) {
	    	log.error("Error sending mail");
	    	log.error(e.getMessage());
	    }

	}

	private void initializeGraphForAppOnlyAuth() throws Exception {

		log.info("initializing Graph for user auth");
		
		clientSecretCredential = new ClientSecretCredentialBuilder().clientId(clientId).tenantId(tenantId)
				.clientSecret(clientSecret).build();

		if (appClient == null) {
			// Use the .default scope when using app-only auth
			final TokenCredentialAuthProvider authProvider = new TokenCredentialAuthProvider(
					List.of("https://graph.microsoft.com/.default"), clientSecretCredential);

			appClient = GraphServiceClient.builder().authenticationProvider(authProvider).buildClient();
		}

	}
	
	private void sendMailGraphAPI(EmailRequest emailRequest) throws Exception {
		
		log.info("Preparing email");
		
		// Ensure client isn't null
	    if (appClient  == null) {
	        throw new Exception("Graph has not been initialized for user auth");
	    }
	    
	    Message message = new Message();

		message.subject = emailRequest.getSubject();
		
		ItemBody body = new ItemBody();
		body.contentType = BodyType.HTML;
		body.content = emailRequest.getMessage();
		message.body = body;
		
		LinkedList<Recipient> toRecipientsList = new LinkedList<Recipient>();
		Recipient toRecipients = new Recipient();
		EmailAddress emailAddress = new EmailAddress();
		emailAddress.address = emailRequest.getRecipient();
		toRecipients.emailAddress = emailAddress;
		toRecipientsList.add(toRecipients);
		message.toRecipients = toRecipientsList;

	    // Send the message
		log.info("sending email");
		appClient.users(sender)
				.sendMail(UserSendMailParameterSet.newBuilder()
	            .withMessage(message)
	            .build())
	        .buildRequest()
	        .post();
	
	}

}

Conclusion

In this blog post, we have walked you through the process of building a Spring Boot REST API that sends emails using Microsoft Graph APIs. By following these steps, you can create a robust email integration for your Java applications, enhancing communication and user experience.

Integrating Microsoft Graph APIs with Spring Boot opens up a world of possibilities, allowing you to not only send emails but also perform a wide range of operations within the Microsoft 365 ecosystem. Remember to handle errors gracefully and consider additional features such as email templates, attachments, and tracking for a complete email solution. Good luck with your Spring Boot email project!


Frequently Asked Questions

What is Microsoft Graph API, and why use it for sending emails in Spring Boot?

Microsoft Graph API is a unified API endpoint for accessing various Microsoft 365 services. Using it in Spring Boot allows seamless integration with Microsoft services, simplifying email sending and other operations.

How do I authenticate my Spring Boot application to use Microsoft Graph API for email sending?

Authentication involves registering your app in the Azure portal, obtaining client credentials, and using them to generate access tokens. Implement OAuth 2.0 authentication in your Spring Boot app to securely connect with Microsoft Graph API.

What permissions does my app need to send emails through Microsoft Graph API?

Your app requires the Mail.Send permission. Ensure that the authenticated user or administrator has consented to this permission for the application.

Leave a Reply