Navigating the Cloud: Harnessing the Power of Microsoft Graph APIs in .NET Core Applications
“If everything was perfect you would never learn and you would never grow.”
Greetings, Developers!
Embark on a journey with us as we explore the seamless integration of Microsoft Graph API in .NET Core. From Azure portal registration to implementing Client Certificate Authentication, we’ll unravel the power of collaboration in real-world applications. Let’s dive into a world where code transforms workflows, enhances collaboration, and makes time work for you. Welcome to a tutorial where innovation meets practicality.
Key Takeaways from this article
- Master Microsoft Graph API integration in .NET Core applications.
- Follow a step-by-step guide for application registration in the Azure portal.
- Implement Client Certificate Authentication for enhanced security.
- Explore a real-world use case: Building an efficient employee management system.
- Leverage Microsoft Graph API for streamlined workflows and enhanced collaboration.
- Prioritize security with best practices in user authentication and client certificates.
- Learn practical tips for secure configuration using appsettings.json and environment variables.
- Implement effective logging and error-handling strategies with the NLog library.
- Gain a holistic understanding of Microsoft Graph API for building robust and secure .NET Core applications.
Prerequisites
Before we start, let’s make sure we have the following in place:
- Microsoft 365 Account: We’ll need a Microsoft 365 account to register our application in the Azure portal.
- Microsoft Entra ID: Access to the Azure portal is necessary to register our application and get the required authentication credentials.
- .NET Core SDK: Make sure we have the latest version of the .NET Core SDK installed on our development machine.
- NLog Library: This tutorial uses the NLog library for logging. We can add it to our project using the following command in the NuGet Package Manager Console:
dotnet add package NLog
Real-world Use Case: Enhancing Employee Management with Microsoft Graph API
In our development journey, we’re crafting an employee management system for a company using a .NET Core application. Our goal is to make communication, collaboration, and access to information more efficient across the organization. Microsoft Graph API proves invaluable in achieving these objectives.
Scenario:
Problem: Employees struggle to quickly access contact information, calendar availability, and shared documents. The current methods involve using different systems and manual effort.
Solution with Microsoft Graph:
- Access Employee Information:
We utilize Microsoft Graph to retrieve employees’ profiles, including contact details, department, and job title, in a single API call. - Calendar Integration:
We incorporate calendar data to display availability, schedule meetings, or check colleagues’ schedules seamlessly. - Document Collaboration:
Leveraging Microsoft Graph, we access shared documents, enabling efficient collaboration on projects.
Tangible Benefits:
- Streamlined Workflows:
Employees can access comprehensive information about their colleagues directly within the application, reducing the need to switch between different tools. - Enhanced Collaboration:
Integration with Microsoft Graph enables real-time collaboration on calendars and documents, fostering a more connected and collaborative work environment. - Time Savings:
Automation through Microsoft Graph reduces manual efforts, saving time for employees and allowing them to focus on more meaningful tasks.
Security Best Practices:
When working with Microsoft Graph, ensuring the security of our application and user data is crucial. Here are beginner-friendly security best practices:
Importance of Security:
- Data Protection:
Microsoft Graph often deals with sensitive information. It’s crucial to protect this data from unauthorized access or manipulation. - User Authentication:
We always authenticate users before making requests to Microsoft Graph. This ensures that only authorized individuals can access the data.
Using Client Certificates:
In our provided code, client certificates are used for authentication. Let’s break down why this is a secure practice:
- Authentication Assurance:
Client certificates provide an additional layer of authentication beyond just a username and password. They verify the identity of the application making the request. - Secure Communication:
Client certificates enable secure communication between our application and Microsoft Graph, encrypting data in transit. - Token Security:
The client certificate is used to obtain a secure token, which is then used for authentication with Microsoft Graph.
Implementation in the Code:
// Get the certificate used for authentication
var certificate = GetCertificate();
// Create a client credential using the certificate
var clientCredential = new ClientCertificateCredential(tenantId, clientId, certificate);
// Create a GraphServiceClient using the client credential
var graphServiceClient = new GraphServiceClient(clientCredential);
These lines demonstrate the use of a client certificate for authentication, where the certificate is obtained from the system’s certificate store.
By following these security best practices, we ensure that our application is resilient against unauthorized access and data breaches, creating a safer environment for both our application and its users.
Step 1: Register Your Application in Azure Portal
- Sign in to Azure Portal: Let’s go to the Azure Portal and sign in with our Microsoft 365 account.
- Navigate to App Registrations: In Azure Active Directory > App registrations > New registration, let’s fill in the details for our application. Note down the Application (client) ID and Directory (tenant) ID for later use.
- Fill in Application Details: Provide details like Name and Redirect URI for our application. We need to create a new client secret under Certificates & Secrets and note down its Value. This secret acts as our application’s password.
- Set API Permissions: In the API permissions section, let’s grant the necessary permissions for Microsoft Graph based on our application’s requirements.
We have very detailed article on creating / registering Application. Kindly please have a look – Microsoft Entra – registering new application and assigning permissions to access Microsoft Graph APIs – https://knowledge-junction.in/category/technology-articles/m365/microsoft-graph/microsoft-graph-powershell/
Step 2: Install Microsoft.Graph NuGet Package
In our .NET Core application, we need to install the Microsoft.Graph NuGet package. Let’s open our terminal or command prompt and run the following command:
dotnet add package Microsoft.Graph
This command adds the Microsoft.Graph package to our project, enabling us to use it for interacting with Microsoft Graph APIs.
Step 3: Tips for Configuring Our .NET Core Application
Configuring our .NET Core application correctly is crucial for making it flexible, secure, and easy to maintain. Let’s go through some tips on managing configuration settings, using the appsettings.json file, and environment variables, while focusing on the best practices for securing sensitive information.
1. Use appsettings.json for Configuration:
The appsettings.json file is like a handy notebook where our application keeps important information. It helps us organize settings logically and makes it easy to update them without changing our code.
// appsettings.json
{
"AzureAd": {
"ClientId": "your-client-id",
"TenantId": "your-tenant-id",
"Thumbprint": "your-thumbprint"
}
}
In our code, we can read these settings using the Configuration object:
// Inside our service or startup class
var clientId = Configuration["AzureAd:ClientId"];
var tenantId = Configuration["AzureAd:TenantId"];
var thumbprint = Configuration["AzureAd:Thumbprint"];
2. Environment Variables:
Think of environment variables as secret codes that our application can use, especially for sensitive information. .NET Core automatically translates these secret codes into configuration settings.
# Set environment variables
export AzureAd__ClientId=your-client-id
export AzureAd__TenantId=your-tenant-id
export AzureAd__Thumbprint=your-thumbprint
In our .NET Core code, we can access these variables:
// Inside our service or startup class
var clientId = Environment.GetEnvironmentVariable("AzureAd__ClientId");
var tenantId = Environment.GetEnvironmentVariable("AzureAd__TenantId");
var thumbprint = Environment.GetEnvironmentVariable("AzureAd__Thumbprint");
3. Securing Sensitive Information:
When dealing with secret information like passwords, follow these best practices:
- Use Environment Variables for Secrets: Keep secret information in environment variables instead of the appsettings.json file. This adds an extra layer of security, and these secrets won’t be accidentally shared with others.
- Restrict Access: Imagine our secrets are like treasure maps. Only share these maps with people we trust. Limit access to our configuration files and variables, ensuring that only the right people can find our treasure.
- Encryption: Sometimes, it’s like hiding our treasure in a secret cave. If needed, consider encrypting sensitive configuration values. Special tools, like the .NET Core Secret Manager (
dotnet user-secrets), can help us manage these secrets during development. - Avoid Hardcoding: Hardcoding secrets directly into our code is like leaving our keys in the car. Avoid it to reduce the risk of accidental exposure.
By following these tips, we can navigate the world of configuration settings in our .NET Core application with confidence. Beginners are encouraged to use these practices for a smoother and more secure development journey.
Step 4: Implement GraphService Class
Now, let’s create a GraphService class to encapsulate the functionality of authenticating and getting a GraphServiceClient instance. Below is the detailed explanation for each line in the GraphService class:
using Microsoft.Graph;
using Microsoft.Identity.Client;
using Azure.Identity;
using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Configuration;
using System.Threading.Tasks;
using NLog;
namespace MySite.Services
{
public class GraphService
{
// Configuration settings
private readonly string clientId = ConfigurationManager.AppSettings["ClientId"];
private readonly string tenantId = ConfigurationManager.AppSettings["TenantId"];
private readonly string thumbprint = ConfigurationManager.AppSettings["Thumbprint"];
internal static readonly NLog.Logger Logger = LogManager.GetCurrentClassLogger();
/// <summary>
/// Gets an authenticated instance of GraphServiceClient.
/// </summary>
/// <returns>GraphServiceClient instance.</returns>
public async Task<GraphServiceClient> GetAuthenticatedGraphClient()
{
try
{
// Get the certificate used for authentication
var certificate = GetCertificate();
// Create a client credential using the certificate
var clientCredential = new ClientCertificateCredential(tenantId, clientId, certificate);
// Create a GraphServiceClient using the client credential
var graphServiceClient = new GraphServiceClient(clientCredential);
return graphServiceClient;
}
catch (Exception ex)
{
// Log the exception using NLog
Logger.Error(ex, "An exception occurred while getting GraphServiceClient.");
// Log exception to file
LogExceptionToFile(ex);
// Rethrow the exception after logging
throw;
}
}
/// <summary>
/// Gets the X.509 certificate used for authentication.
/// </summary>
/// <returns>X509Certificate2 instance.</returns>
private X509Certificate2 GetCertificate()
{
try
{
// Open the X.509 certificate store
X509Store store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
// Find the certificate by thumbprint
var certificates = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
// Check if the certificate is found
if (certificates.Count == 0)
throw new InvalidOperationException($"Certificate with thumbprint '{thumbprint}' not found.");
return certificates[0];
}
catch (Exception ex)
{
// Log the exception using NLog
Logger.Error(ex, "An exception occurred while getting certificate.");
// Log exception to file
LogExceptionToFile(ex);
// Rethrow the exception after logging
throw;
}
}
/// <summary>
/// Logs the exception details to a file.
/// </summary>
/// <param name="ex">Exception to log.</param>
private void LogExceptionToFile(Exception ex)
{
try
{
// Specify your log file path
var logFilePath = "error.log";
// Log the error message and stack trace to the file
File.AppendAllText(logFilePath, $"{DateTime.UtcNow} - Error: {ex.Message}\nStackTrace: {ex.StackTrace}\n");
}
catch (Exception logException)
{
// Log any exception that occurs while logging to file
Logger.Error(logException, "An exception occurred while logging to file.");
}
}
}
}
Explaining Code: Microsoft Graph API Integration in .NET Core
using Microsoft.Graph;
using Microsoft.Identity.Client;
using Azure.Identity;
using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Configuration;
using System.Threading.Tasks;
using NLog;
Using Directives:
We use these directives to bring in necessary namespaces for the code to use specific classes and functionalities.
- Microsoft.Graph: Provides classes for working with Microsoft Graph API.
- Microsoft.Identity.Client: Contains classes for Microsoft Identity Client Library, which is used for authentication.
- Azure.Identity: Includes classes for Azure Identity library, used for secure access to Azure resources.
- System: Standard .NET namespace for fundamental types and basic coding constructs.
- System.IO: Provides functionality for input/output operations, including file operations.
- System.Security.Cryptography.X509Certificates: Contains classes for working with X.509 certificates.
- System.Configuration: Used for accessing application configuration settings.
- System.Threading.Tasks: Enables working with asynchronous programming constructs.
- NLog: Imports classes from the NLog library for logging.
namespace MySite.Services
{
public class GraphService
{
// Class members…
}
}
Namespace and Class Declaration:
- We place our code within the MySite.Services namespace, and we declare a class named GraphService.
- This class encapsulates functionality related to interacting with Microsoft Graph.
private readonly string clientId = ConfigurationManager.AppSettings["ClientId"];
private readonly string tenantId = ConfigurationManager.AppSettings["TenantId"];
private readonly string thumbprint = ConfigurationManager.AppSettings["Thumbprint"];
internal static readonly NLog.Logger Logger = LogManager.GetCurrentClassLogger();
Class Member Declarations:
- We declare private, read-only string variables (clientId, tenantId, thumbprint) initialized with values from the application settings.
- We declare a static logger (Logger) using NLog, making it accessible within the assembly.
public async Task GetAuthenticatedGraphClient()
{
try
{
// Method body…
}
catch (Exception ex)
{
// Exception handling…
}
}
Async Method Declaration (GetAuthenticatedGraphClient):
- We declare an asynchronous method that returns a GraphServiceClient.
- This method is responsible for obtaining an authenticated instance of GraphServiceClient.
private X509Certificate2 GetCertificate()
{
try
{
// Method body…
}
catch (Exception ex)
{
// Exception handling…
}
}
Private Method Declaration (GetCertificate):
- We declare a private method that returns an X509Certificate2 instance.
- This method is responsible for retrieving the X.509 certificate used for authentication.
private void LogExceptionToFile(Exception ex)
{
try
{
// Method body…
}
catch (Exception logException)
{
// Exception handling…
}
}
Private Method Declaration (LogExceptionToFile):
- We declare a private method that logs exception details to a file.
- This method handles logging exceptions and any errors that may occur during the logging process.
var certificate = GetCertificate();
Method Invocation (GetCertificate):
- We call the GetCertificate method to obtain the X.509 certificate for authentication.
var clientCredential = new ClientCertificateCredential(tenantId, clientId, certificate);
Object Instantiation (ClientCertificateCredential):
- We instantiate a ClientCertificateCredential object using the obtained certificate, tenant ID, and client ID.
- This object is used for client credential authentication.
var graphServiceClient = new GraphServiceClient(clientCredential);
Object Instantiation (GraphServiceClient):
- We instantiate a GraphServiceClient object using the client credential.
- This is the authenticated client used to make requests to Microsoft Graph API.
throw;
Throw Statement:
- We rethrow the caught exception after logging.
- This ensures that the exception continues to propagate up the call stack.
return graphServiceClient;
Method Return Statement:
- We return the authenticated GraphServiceClient instance to the calling code.
File.AppendAllText(logFilePath, $"{DateTime.UtcNow} - Error: {ex.Message}\nStackTrace: {ex.StackTrace}\n");
File Logging:
- We append error details, including timestamp, message, and stack trace, to a log file.
catch (Exception logException)
{
Logger.Error(logException, "An exception occurred while logging to file.");
}
Nested Exception Logging:
- We catch and log any exception that occurs during the file logging process.
- This ensures that logging issues are also captured.
Step 4: Use GraphService in Our Application
Now, we can use the GraphService class in our .NET Core application to interact with Microsoft Graph. Here’s an example:
using Microsoft.Graph;
namespace MySite
{
class Program
{
static async Task Main(string[] args)
{
// Create an instance of GraphService
var graphService = new GraphService();
try
{
// Get an authenticated GraphServiceClient
var graphClient = await graphService.GetAuthenticatedGraphClient();
// Use the graphClient to make Graph API requests
// Example: Get the current user's profile
var me = await graphClient.Me.GetAsync();
Console.WriteLine($"User: {me.DisplayName}");
// Add our desired Graph API requests here...
}
catch (Exception ex)
{
Console.WriteLine($"An error occurred: {ex.Message}");
}
}
}
}
- Line 7-15: Example usage of the GraphService class in a console application.
- Line 9: Create an instance of GraphService.
- Line 11-14: Inside a try-catch block, obtain an authenticated GraphServiceClient and make a request to get the current user’s profile from Microsoft Graph. Any exceptions are caught and logged.
This example demonstrates how to integrate the GraphService class into your application and utilize the authenticated GraphServiceClient to interact with Microsoft Graph API.
We have very good / detailed articles on Microsoft Graph. Kindly please have a look. – https://knowledge-junction.in/category/technology-articles/m365/microsoft-graph/
Conclusion
In conclusion, our journey through Microsoft Graph API integration in a .NET Core application has paved the way for efficient and secure development. The GraphService class, exemplifying streamlined authentication and client instantiation, empowers developers, especially beginners. Prioritizing configuration best practices and security measures ensures robust application functionality. As you incorporate this knowledge, envision a future where applications seamlessly enhance communication and collaboration, contributing to a more secure digital landscape. Happy coding!
Also get my article updates on my social media handles.
Twitter – https://twitter.com/PrajyotYawalkar?t=oovP0r9FnDtz5nNSJGKO0Q&s=09
LinkedIn – https://www.linkedin.com/in/prajyot-yawalkar-093716224/
Have a wonderful day.
Thanks for reading.

1 Response
[…] In this article we are describing integration of Microsoft Graph with .NET Core – Navigating the Cloud: Harnessing the Power of Microsoft Graph APIs in .NET Core Applications – https://microsoft365hub.in/2024/01/27/navigating-the-cloud-harnessing-the-power-of-microsoft-graph-a… […]