My requirement is to read email using MSAL and Microsoft Graph API. Once email is read I want to check if attachment is present or not if the attachment is present I want to download the attachment content/contentbytes and save on the local system. Using below code I am able to read email messages details and also attachment details/meta data but not what code to write to download/ the attachment.
package com.xxx.xxxxx.msaltest.client;
import static com.xxx.xxxxxx.msaltest.constants.Constants.AUTHORITY_COMMON_ENDPOINTS;
import static com.xxx.xxxxxx.msaltest.constants.Constants.CLIENT_ID;
import static com.xxx.xxxxxx.msaltest.constants.Constants.SCOPE;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import org.apache.commons.lang.StringUtils;
//import com.microsoft.graph.requests.GraphServiceClient;
import com.microsoft.aad.msal4j.DeviceCode;
import com.microsoft.aad.msal4j.DeviceCodeFlowParameters;
import com.microsoft.aad.msal4j.IAccount;
import com.microsoft.aad.msal4j.IAuthenticationResult;
import com.microsoft.aad.msal4j.PublicClientApplication;
import com.microsoft.graph.authentication.IAuthenticationProvider;
import com.microsoft.graph.models.Attachment;
import com.microsoft.graph.models.Message;
import com.microsoft.graph.models.User;
import com.microsoft.graph.requests.AttachmentCollectionPage;
import com.microsoft.graph.requests.GraphServiceClient;
import com.microsoft.graph.requests.MessageCollectionPage;
import okhttp3.Request;
public class DeviceCodeClientMsal2 implements Runnable {
public static String COMMON_VARIABLE = StringUtils.EMPTY;
public DeviceCodeClientMsal2() {
}
@Override
public void run() {
IAuthenticationResult result;
try {
result = acquireTokenDeviceCode();
System.out.println("** Access token: " + result.accessToken());
System.out.println("** Id token: " + result.idToken());
System.out.println("** Account username: " + result.account().username());
IAuthenticationProvider provider = new
MyAuthenticationProvider(result.accessToken());
GraphServiceClient<Request> graphServiceClient = GraphServiceClient.builder()
.authenticationProvider(provider).buildClient();
User user = graphServiceClient.me().buildRequest().get();
final MessageCollectionPage messagePage =
graphServiceClient.me().messages().buildRequest().top(3).get();
List<Message> messageList = messagePage.getCurrentPage();
for (Message msg : messageList) {
System.out.println("Subject -> " + msg.subject);
System.out.println("Body -> " + msg.body.toString());
System.out.println("Is attachment exist ? " + msg.hasAttachments);
if (msg.hasAttachments) {
System.out.println("Message id -> " + msg.id);
AttachmentCollectionPage attachments =
graphServiceClient.me().messages(msg.id).attachments()
.buildRequest().get();
List<Attachment> listOfAttachment = attachments.getCurrentPage();
System.out.println("Size -> " + listOfAttachment.size());
Attachment attachment = listOfAttachment.get(0);
System.out.println("Attachment id -> " + attachment.id);
//How to download the attachment
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("run method exits");
}
private static IAuthenticationResult acquireTokenDeviceCode() throws Exception {
// Load token cache from file and initialize token cache aspect. The token cache
// will have
// dummy data, so the acquireTokenSilently call will fail.
System.out.println("Before Public Client Application");
PublicClientApplication pca = PublicClientApplication.builder(CLIENT_ID).authority(AUTHORITY_COMMON_ENDPOINTS)
.build();
// Take first account in the cache. In a production application, you would
// filter
// accountsInCache to get the right account for the user authenticating.
IAuthenticationResult result;
Consumer<DeviceCode> deviceCodeConsumer = (DeviceCode deviceCode) -> {
COMMON_VARIABLE = deviceCode.message();
System.out.println(deviceCode.message());
};
DeviceCodeFlowParameters parameters = DeviceCodeFlowParameters.builder(SCOPE, deviceCodeConsumer).build();
result = pca.acquireToken(parameters).join();
return result;
}
}
In my case, I used Microsoft Graph Java SDK to download attachment content of email messages.
You can find below code files that worked in my environment and downloaded attachment files successfully as below:
DeviceCodeClientAuth.java:
package com.msaltest.client;
import com.azure.identity.DeviceCodeCredential;
import com.azure.identity.DeviceCodeCredentialBuilder;
import com.azure.core.credential.TokenRequestContext;
import com.microsoft.graph.authentication.IAuthenticationProvider;
import com.microsoft.graph.models.*;
import com.microsoft.graph.requests.*;
import okhttp3.Request;
import reactor.core.publisher.Mono;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
public class DeviceCodeClientAuth implements Runnable {
private static final String CLIENT_ID = "appId";
private static final String TENANT_ID = "tenantId";
private static final String[] SCOPES = new String[] {"Mail.Read"};
public DeviceCodeClientAuth() {
System.setProperty("com.microsoft.identity.client.enablePII", "true");
}
@Override
public void run() {
try {
// Acquire token using Device Code Flow
DeviceCodeCredential credential = new DeviceCodeCredentialBuilder()
.clientId(CLIENT_ID)
.tenantId(TENANT_ID)
.challengeConsumer(challenge -> {
// Display challenge to the user
System.out.println(challenge.getMessage());
})
.build();
if (credential == null) {
throw new Exception("Unexpected error: DeviceCodeCredential is null");
}
IAuthenticationProvider provider = requestUrl -> {
TokenRequestContext requestContext = new TokenRequestContext().addScopes(SCOPES);
// Get the access token asynchronously
Mono<String> futureToken = credential.getToken(requestContext)
.map(token -> token.getToken());
return futureToken.toFuture();
};
GraphServiceClient<Request> graphServiceClient = GraphServiceClient
.builder()
.authenticationProvider(provider)
.buildClient();
// Fetch user information
User user = graphServiceClient.me().buildRequest().get();
System.out.println("👤 User: " + user.displayName);
// Get messages
MessageCollectionPage messagePage = graphServiceClient
.me()
.messages()
.buildRequest()
.top(3)
.get();
List<Message> messageList = messagePage.getCurrentPage();
for (Message msg : messageList) {
System.out.println("📧 Subject: " + msg.subject);
System.out.println("📎 Has Attachments? " + msg.hasAttachments);
if (msg.hasAttachments) {
// Only fetch attachments if they exist
AttachmentCollectionPage attachments = graphServiceClient
.me()
.messages(msg.id)
.attachments()
.buildRequest()
.get();
List<Attachment> listOfAttachment = attachments.getCurrentPage();
for (Attachment attachment : listOfAttachment) {
System.out.println("🔗 Attachment ID: " + attachment.id);
if (attachment instanceof FileAttachment) {
FileAttachment fileAttachment = (FileAttachment) attachment;
System.out.println("⬇ Downloading File: " + fileAttachment.name);
// Get file content and save locally
saveToFile(fileAttachment.name, fileAttachment.contentBytes);
} else {
System.out.println("📄 Non-file attachment detected: " + attachment.name);
}
}
} else {
System.out.println("📧 No attachments for this message.");
}
}
} catch (Exception e) {
System.err.println("❌ Error: " + e.getMessage());
e.printStackTrace();
}
}
private static void saveToFile(String fileName, byte[] content) {
try (FileOutputStream fileOutputStream = new FileOutputStream("downloaded_" + fileName)) {
fileOutputStream.write(content);
System.out.println("✅ File saved: downloaded_" + fileName);
} catch (IOException e) {
System.err.println("❌ Error saving file: " + e.getMessage());
e.printStackTrace();
}
}
}
Main.java:
package com.msaltest.client;
public class Main {
public static void main(String[] args) {
System.out.println("Starting Microsoft Graph API Client...");
DeviceCodeClientAuth client = new DeviceCodeClientAuth();
client.run(); // Execute the authentication and email attachment retrieval
System.out.println("Process completed.");
}
}
Response:
Reference: