Microsoft Graph to Manage Mailboxes in C#

The integration of Microsoft Graph API and the Aspose.Email for .NET library with an email application allows developers to easily access and manipulate mailbox data, perform operations like fetching messages, retrieving folder hierarchies, and saving emails in different formats. In this article, we’ll explore how to leverage this powerful combination in processing and managing mailboxes.

Microsoft Graph Presentation

Microsoft Graph is a comprehensive API platform provided by Microsoft that offers a unified endpoint to access a wide range of Microsoft services and data. It serves as a gateway to the vast data available in Microsoft 365, including Outlook mailboxes, calendars, contacts, OneDrive, Teams, and more.

With Microsoft Graph, developers can build applications that seamlessly interact with user data and insights across Microsoft cloud ecosystem. This is achieved through RESTful APIs and SDKs that provide the means to authenticate, authorize, and query the data with ease.

About the Aspose.Email for .NET Library

Aspose.Email for .NET is a feature-rich library that enables developers to work with email files and protocols in their .NET applications. It provides a robust set of APIs for creating, manipulating, and converting email messages in various formats, such as MSG, EML, and MBOX. Additionally, the library supports email protocols like SMTP, POP3, and IMAP, allowing for versatile email management.

In this guide, we’ll use Aspose.Email to interact with Microsoft Graph, processing mailbox data programmatically. With the GraphClient from Aspose.Email, we can efficiently perform mailbox operations by authenticating with Microsoft Graph.

To start utilizing the library, you need to integrate it into your project. The easiest way to obtain Aspose.Email for .NET is through the NuGet Package Manager:

  • Open your project in Visual Studio.
  • Navigate to Tools > NuGet Package Manager > Manage NuGet Packages for Solution.
  • Search for Aspose.Email.
  • Select the package and click Install.

Alternatively, you can use the Package Manager Console:

Install-Package Aspose.Email

You can also download the latest version of the API directly from the Aspose website.

Configure Your App on Azure Portal

Before we dive into the code, it’s crucial to configure your application within the Azure portal to enable Microsoft Graph access. Just follow these steps:

  1. Create an Azure Active Directory (AAD) Application:

    • Navigate to the Azure Portal.
    • Go to Azure Active Directory > App registrations.
    • Click New registration to create a new application.
    • Provide a name and set the redirect URI if needed.
    • Click Register to complete the process.
  2. Set API Permissions:

    • In your registered app, navigate to API permissions.
    • Click Add a permission > Microsoft Graph.
    • Select Application permissions for server-to-server calls.
    • Choose necessary permissions like Mail.Read, Mail.ReadWrite, User.Read, etc.
    • Click Add permissions to apply.
  3. Create a Client Secret:

    • Go to Certificates & secrets in your app.
    • Click New client secret and provide a description.
    • Set an expiration period, then click Add.
    • Note the generated client secret value, as you’ll need it later.
  4. Gather Configuration Values:

    • Obtain the Tenant ID, Client ID, and Client Secret from your app’s overview page and secret section.
    • You’ll use these values to authenticate and interact with Microsoft Graph.

Once your application is configured, you are ready to proceed with the development tasks.

Obtain Access Token and Initialize GraphClient

Before diving into mailbox operations, we need to unlock the door to Microsoft Graph’s vast ecosystem of data and services. This access is granted through an OAuth 2.0 access token - a digital key that authorizes your application to interact with Microsoft Graph on behalf of a user or service. Imagine it as a secure pass that grants you entry to an entire universe of possibilities, from fetching emails to managing contacts and beyond.

Step 1: Set Up a Configuration File

The first step in acquiring the token is to set up a configuration file that stores essential details like your application’s Tenant ID, Client ID, and Client Secret. These values are your application’s credentials and act as identifiers when communicating with Microsoft servers.

Here’s what your JSON configuration might look like:

{
    "Instance": "https://login.microsoftonline.com/{0}",
    "ApiUrl": "https://graph.microsoft.com/.default",
    "TenantId": "YOUR_TENANT_ID_HERE",
    "ClientId": "YOUR_APP_ID_HERE",
    "ClientSecret": "YOUR_CLIENT_SECRET_HERE",
    "UserId": "YOUR_ID_HERE"
}

Step 2: Map JSON Configuration to a C# Object

Now, let’s turn this configuration into a C# object that our application can use. We’ll read the JSON file and map its contents to an AuthenticationConfig class, ensuring that our application knows where to find the critical information it needs.

class AuthenticationConfig
{
    public string Instance { get; set; }
    public string ApiUrl { get; set; }
    public string TenantId { get; set; }
    public string ClientId { get; set; }
    public string UserId { get; set; }
    public string Authority => string.Format(CultureInfo.InvariantCulture, Instance, TenantId);
    public string ClientSecret { get; set; }
    
    public static AuthenticationConfig ReadFromJsonFile(string path)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile(path);

        var configuration = builder.Build();
        return configuration.Get<AuthenticationConfig>();
    }
}

Step 3: Acquire Access Token

With our configuration in place, it’s time to acquire the access token. We’ll implement a GraphTokenProvider class that handles the authentication process using Microsoft Authentication Library (MSAL). This class takes care of the heavy lifting - communicating with Microsoft Graph to get the token that empowers our application.

class GraphTokenProvider : ITokenProvider
{
    private readonly IConfidentialClientApplication _app;
    private readonly string[] _scopes;
    private string? _token;
    
    public GraphTokenProvider(AuthenticationConfig config)
    {
        _app = ConfidentialClientApplicationBuilder.Create(config.ClientId)
            .WithClientSecret(config.ClientSecret)
            .WithAuthority(config.Authority)
            .Build();

        // In memory token caches (App and User caches)
        _app.AddInMemoryTokenCache();
        
        _scopes = new[] { config.ApiUrl }; 
    }

    public void Dispose()
    {
        throw new NotImplementedException();
    }

    public OAuthToken GetAccessToken()
    {
        return GetAccessToken(false);
    }

    public OAuthToken GetAccessToken(bool ignoreExistingToken)
    {
        if (!ignoreExistingToken && _token != null)
        {
            return new OAuthToken(_token);
        }

        _token = GetAccessTokenAsync().GetAwaiter().GetResult();
        return new OAuthToken(_token);
    }
    
    private async Task<string?> GetAccessTokenAsync()
    {
        AuthenticationResult? result;
        
        try
        {
            result = await _app.AcquireTokenForClient(_scopes)
                .ExecuteAsync();
            
            Console.WriteLine($"The token acquired from {result.AuthenticationResultMetadata.TokenSource} {Environment.NewLine}");
        }
        catch (MsalServiceException ex)
        {
            Console.WriteLine($"Error Acquiring Token:{Environment.NewLine}{ex}{Environment.NewLine}");
            result = null;
        }

        if (result == null) return null;
        _token = result.AccessToken;
        return result.AccessToken;
    }
}

Step 4: Initialize GraphClient

Finally, we initialize the GraphClient using the token we’ve acquired. The GraphClient acts as our bridge to Microsoft Graph, allowing us to interact with user mailboxes seamlessly.

var config = AuthenticationConfig.ReadFromJsonFile("appsettings.json");
var tokenProvider = new GraphTokenProvider(config);

using var client = GraphClient.GetClient(tokenProvider, config.TenantId);
client.Resource = ResourceType.Users;
client.ResourceId = config.UserId;

Get a Folder Hierarchy and Retrieve Folders by Name

Once you’ve acquired access to Microsoft Graph, it’s time to embark on an exploration of the mailbox. In this section, we’ll delve into retrieving and navigating the folder hierarchy of a mailbox, enabling you to access specific folders by name.

Step 1: Understanding the Folder Hierarchy

Mailboxes are structured hierarchically, much like a tree. The root folder branches into several subfolders, each containing its own set of emails and potentially more subfolders. This nested structure allows for organized management and easy navigation of emails.

Let’s define and use the FolderNode class to represent each folder in the hierarchy:

// Represents a node in a folder hierarchy,
// extending the properties of FolderInfo and storing a collection of subfolders.
class FolderNode
{
    // Gets the FolderInfo object representing the folder information.
    public FolderInfo Folder { get; }
    
    // Gets the collection of subfolders contained within the current folder.
    public List<FolderNode?> SubFolders { get; }

    // Initializes a new instance of the FolderNode class with the specified FolderInfo object.
    public FolderNode(FolderInfo folder)
    {
        Folder = folder;
        SubFolders = new List<FolderNode?>();
    }
    
    // Prints all folders in a hierarchical manner starting from the current node.
    public void PrintHierarchy()
    {
        PrintFolderNode(this, 0);
    }

    private void PrintFolderNode(FolderNode node, int indentLevel)
    {
        // Print current folder node with indentation
        Console.WriteLine($"{new string(' ', indentLevel * 2)}{node}");

        // Recursively print subfolders
        foreach (var subFolder in node.SubFolders)
        {
            PrintFolderNode(subFolder, indentLevel + 1);
        }
    }

    // Get the folder Display Name.
    public override string ToString()
    {
        return $"{Folder.DisplayName} ({Folder.ContentCount})";
    }
}

To retrieve the complete folder hierarchy, we will create the FolderHierarchy class, which leverages the GraphClient to list all folders recursively. Here’s how it works:

static class FolderHierarchy
{
    // Retrieves all folders in the mailbox recursively and returns a hierarchical collection of FolderNode objects.
    public static List<FolderNode> Retrieve(IGraphClient client)
    {
        // Retrieve root folders
        var rootFolders = client.ListFolders();
        var allFolders = new List<FolderNode>();

        // Retrieve subfolders recursively
        foreach (var folder in rootFolders)
        {
            var folderNode = new FolderNode(folder);
            RetrieveSubFolders(client, folderNode);
            allFolders.Add(folderNode);
        }

        return allFolders;
    }

    // Retrieves subfolders recursively and adds them to the parent FolderNode's SubFolders property.
    private static void RetrieveSubFolders(IGraphClient client, FolderNode parentFolderNode)
    {
        if (parentFolderNode.Folder.HasSubFolders)
        {
            var subFolders = client.ListFolders(parentFolderNode.Folder.ItemId);
            
            foreach (var subFolder in subFolders)
            {
                var subFolderNode = new FolderNode(subFolder);
                RetrieveSubFolders(client, subFolderNode);
                parentFolderNode.SubFolders.Add(subFolderNode);
            }
        }
    }
}

Step 2: Retrieve the Folder Hierarchy

Using the FolderHierarchy.Retrieve method, you can effortlessly traverse the mailbox to uncover its folder structure. Here’s how you can achieve this:

// Retrieve the folder hierarchy from the mailbox
var folderNodes = FolderHierarchy.Retrieve(client);

// Print the folder hierarchy in a structured format
foreach (var folderNode in folderNodes)
{
    folderNode.PrintHierarchy();
}

Step 3: Retrieve Folders by Name

Once the folder hierarchy is retrieved, you can pinpoint specific folders by name. Whether you’re interested in accessing the Inbox or any custom folder, this method ensures you can locate them swiftly:

// Specify the folder name you're looking for
string targetFolderName = "Inbox";

// Locate the target folder by name
var targetFolder = folderNodes.FirstOrDefault(
    folderNode => folderNode.Folder.DisplayName.Equals(targetFolderName, StringComparison.OrdinalIgnoreCase))
    ?.Folder;

List Messages in Specified Folder

With the folder hierarchy successfully retrieved, the next step is to dive into the content of specific folders. Imagine you’ve navigated to your Inbox; now, you want to view all the messages it holds.

In this section, we will explore how to list messages within a specific folder using the GraphClient and Aspose.Email for .NET.

Once you have the folder, listing the messages within it is straightforward. The GraphClient provides a ListMessages method to retrieve all messages in a folder, which you can then process or display.

Here’s the code to list messages from the specified folder:

Console.WriteLine("Listing messages in the specified folder...");

// Call the client method to list messages in the selected folder
var messageInfoCollection = client.ListMessages(targetFolder.ItemId);

Console.WriteLine($"{targetFolderName}:");

// Print out the subject lines of the messages
foreach (var messageInfo in messageInfoCollection)
{
    Console.WriteLine($"     - {messageInfo.Subject}");
}

The messageInfoCollection contains essential details about each email. This information can be used to display summaries, generate reports, or even trigger alerts based on specific criteria.

Conclusion

In this article, we’ve explored how to harness the power of Microsoft Graph and the Aspose.Email for .NET library to effectively process mailboxes, navigate folder hierarchies, and list messages within specific folders. By following these steps, you can build robust applications that seamlessly interact with email data, providing enhanced functionality and user experiences.

Concepts in Action

If you’re eager to see these concepts in action, you can download a fully functional example of a working application. This application includes the source code described in this article and demonstrates how to implement these features step by step.

Visit our GitHub repository to access the sample application: Aspose.Email for .NET - GraphApp Example.

Processing mailboxes through Microsoft Graph offers unparalleled access to email data and management capabilities. With the right tools and techniques, you can build sophisticated applications that provide meaningful insights and automate complex tasks, ultimately enhancing productivity and user satisfaction.

Try it for Free

Additionally, Aspose.Email provides comprehensive documentation, an extensive API reference, and a variety of free online tools and apps to enhance your development process. Developers can also access a free support forum for community assistance and insights, and stay updated with the latest tips and tutorials through the Aspose blog.

See Also