Introduction
This is a continuation of the previous article, creating ‘
Azure AD Applications’. This console app calls the graph API service to get the users' information in your tenant.
Please note that .NET 5 provides capabilities of .NET Core and .NET framework. This is still platform-independent, which means the code will run on Windows machines and as well as Linux, MAC OS machines.
Note
You need to have the latest version of Visual Studio Code installed to implement this project by the following steps. This console app is developed using VS Code.
In a nutshell, this is overall what your application does.
- Console app requests for token
- MS Identity platform sends access token on successful authentication.
- Console app sends Access Token in the authorization header
- Console app gets HTTP response back from the Graph API service
Steps
Step1
To perform the steps you need to make sure .NET 5 SDKs are installed. To check the version of SDKs installed, open the command prompt and enter the following command
dotnet --list-sdks
Please check the references section, on how to install .NET 5 SDK.
Step 2
Create the console app by running the following command.
dotnet new console -o consoleapp1.
Make sure to create a console app under a specific directory. In this case, I have it installed under C:\Users\Documents\VSCode and created a folder called ‘MSGraph’. This can be created anywhere on your machine.
This is just similar to creating a new console app in visual studio IDE.
Step 3
Install the following dependencies for the console app to consume the graph API successfully. first, change the director to consoleapp1 that just got created.
cd consoleapp1
Then run the following commands in sequence
Microsoft.Identity.Client
dotnet add package Microsoft.Identity.Client
This command installs the MSAL client into your program, which takes care of generating tokens (Access Token and Refresh Token) for you. ‘dotnet add package’ provides a convenient way to add the NuGet packages to your project.
Microsoft.Graph
dotnet add package Microsoft.Graph
This command installs the models and requests builders to access the v1.0 endpoint with fluent API.
Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration
This command installs Configuration class, which is used to read the configuration data from settings such as appsettings.json, Environment variables, Azure Key Vault, Azure App Configuration, command-line arguments, Directory files, In-memory .NET objects, custom providers (installed or created)
Microsoft.Extensions.Configuration.FileExtensions
dotnet add package Microsoft.Extensions.Configuration.FileExtensions
This command installs the extension methods for Microsoft.Extensions.Configuration class for file-based configuration providers.
Microsoft.Extensions.Configuration.Json
dotnet add package Microsoft.Extensions.Configurations.Json
this command installs the required methods to access the settings from JSON file.
Step 4
Open the Visual studio code by typing the command "code .". This directly opens VS code with the correct folder.
Note
You might be getting a window message saying ‘Required assets to build and debug”. Just click on yes.
Step 5
Create a file called ‘appsettings.json’ in the consoleapp1 project.
Step 6
Update the properties. you should be having following values ready from the previous article ‘Creating Azure AD Application’
- Tenant ID:
- Application ID:
- Application Secret:
Update these values in appsettings.json file and save it. note that the values could be different for your specific tenant.
Step 7
Create new folder called ‘Helpers’. Add the helper classes ‘AuthHandler.cs’. This is used to handle the authentication in your application to access the v1.0 endpoint Graph API service.
Add the following code in AuthHandler.cs code
- using System.Net.Http;
- using System.Threading.Tasks;
- using Microsoft.Graph;
- using System.Threading;
-
- namespace Helpers
- {
- public class AuthHandler : DelegatingHandler
- {
- private IAuthenticationProvider _authenticationProvider;
-
- public AuthHandler(IAuthenticationProvider authenticationProvider, HttpMessageHandler innerHandler)
- {
- InnerHandler = innerHandler;
- _authenticationProvider = authenticationProvider;
- }
-
- protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
- {
- await _authenticationProvider.AuthenticateRequestAsync(request);
- return await base.SendAsync(request, cancellationToken);
- }
- }
- }
Step 8
In a similar way create MsalAutheticationProvider.cs in the Helpers folder. This takes care of token generation and token renewals which is required to access the Graph api.
This also adds access token to the authentication header and sends requests to Graph API.
add the following code in 'MsalAuthenticationProvider.cs' class
- using System.Net.Http;
- using System.Net.Http.Headers;
- using System.Threading.Tasks;
- using Microsoft.Identity.Client;
- using Microsoft.Graph;
-
- namespace Helpers
- {
- public class MsalAuthenticationProvider : IAuthenticationProvider
- {
- private IConfidentialClientApplication _clientApplication;
- private string[] _scopes;
-
- public MsalAuthenticationProvider(IConfidentialClientApplication clientApplication, string[] scopes)
- {
- _clientApplication = clientApplication;
- _scopes = scopes;
- }
-
- public async Task AuthenticateRequestAsync(HttpRequestMessage request)
- {
- var token = await GetTokenAsync();
- request.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
- }
-
- public async Task<string> GetTokenAsync()
- {
- AuthenticationResult authResult = null;
- authResult = await _clientApplication.AcquireTokenForClient(_scopes).ExecuteAsync();
- return authResult.AccessToken;
- }
- }
- }
Step 9
Consume the Graph API responses in the console app. This is a bit tedious part. So please follow carefully.
In the main program.cs add the following references.
- using System.Collections.Generic;
- using Microsoft.Graph;
- using Microsoft.Identity.Client;
- using Microsoft.Extensions.Configuration;
- using Helpers;
Add the static member of type ‘GraphServiceClient’ to the program.cs file which is used to instantiate the client used to call MS Graph.
- private static GraphServiceClient _graphClient;
for simplicity I have tried to add the lines of code after the main method. Just don’t disturb main method.
Step 10
It is required to get the configuration settings which is updated in file ‘appsettings.json’. add the following lines of code.
- private static IConfigurationRoot LoadAppSettings()
- {
- try
- {
- var config = new ConfigurationBuilder()
- .SetBasePath(System.IO.Directory.GetCurrentDirectory())
- .AddJsonFile("appsettings.json", false, true)
- .Build();
-
- if (string.IsNullOrEmpty(config["applicationId"]) ||
- string.IsNullOrEmpty(config["applicationSecret"]) ||
- string.IsNullOrEmpty(config["redirectUri"]) ||
- string.IsNullOrEmpty(config["tenantId"]))
- {
- return null;
- }
-
- return config;
- }
- catch (System.IO.FileNotFoundException)
- {
- return null;
- }
- }
observe that method is just after the static member we just created.
Step 11
Create method called ‘AuthorizationProvider’ which will take the setting values from appsettings.json and builds the service to Graph API.
- private static IAuthenticationProvider CreateAuthorizationProvider(IConfigurationRoot config)
- {
- var clientId = config["applicationId"];
- var clientSecret = config["applicationSecret"];
- var redirectUri = config["redirectUri"];
- var authority = $"https://login.microsoftonline.com/{config["tenantId"]}/v2.0";
-
- List<string> scopes = new List<string>();
- scopes.Add("https://graph.microsoft.com/.default");
-
- var cca = ConfidentialClientApplicationBuilder.Create(clientId)
- .WithAuthority(authority)
- .WithRedirectUri(redirectUri)
- .WithClientSecret(clientSecret)
- .Build();
- return new MsalAuthenticationProvider(cca, scopes.ToArray());
- }
Notice that this method ‘AuthorizationProvider’ is just after the method LoadAppSettings()
Step 12
Now consume the response from the Graph API service. For this you need to have the following method ‘GetAuthenticatedGraphClient’ to the program class. this method creates an instance of the graph service client object, which is used to capture the http response.
- private static GraphServiceClient GetAuthenticatedGraphClient(IConfigurationRoot config)
- {
- var authenticationProvider = CreateAuthorizationProvider(config);
- _graphClient = new GraphServiceClient(authenticationProvider);
- return _graphClient;
- }
Notice that this method ‘GetAuthenticatedGraphClient’ is just after the ‘CreateAuthorizationProvider’ method.
Step 13
Now locate the main method and now add the following code to load the values from appsettings.json file.
- var config = LoadAppSettings();
- if (config == null)
- {
- Console.WriteLine("Invalid appsettings.json file.");
- return;
- }
Step 14
Now add the following code just after the LoadAppSettings() code that is written before. This code get the authenticated instance of the graph service client, and then writes back the request results got from Graph API service to the console.
- var client = GetAuthenticatedGraphClient(config);
-
- var graphRequest = client.Users.Request();
-
- var results = graphRequest.GetAsync().Result;
- foreach(var user in results)
- {
- Console.WriteLine(user.Id + ": " + user.DisplayName + " <" + user.Mail + ">");
- }
-
- Console.WriteLine("\nGraph Request:");
- Console.WriteLine(graphRequest.GetHttpRequestMessage().RequestUri);
- }
Below is the complete code for program.cs
- using System;
- using System.Collections.Generic;
- using Microsoft.Graph;
- using Microsoft.Identity.Client;
- using Microsoft.Extensions.Configuration;
- using Helpers;
-
- namespace consoleapp1
- {
- class Program
- {
- static void Main(string[] args)
- {
- Console.WriteLine("Hello World!");
- var config = LoadAppSettings();
- if (config == null)
- {
- Console.WriteLine("Invalid appsettings.json file.");
- return;
- }
- var client = GetAuthenticatedGraphClient(config);
-
- var graphRequest = client.Users.Request();
-
- var results = graphRequest.GetAsync().Result;
- foreach(var user in results)
- {
- Console.WriteLine(user.Id + ": " + user.DisplayName + " <" + user.Mail + ">");
- }
-
- Console.WriteLine("\nGraph Request:");
- Console.WriteLine(graphRequest.GetHttpRequestMessage().RequestUri);
- }
- private static GraphServiceClient _graphClient;
- private static IConfigurationRoot LoadAppSettings()
- {
- try
- {
- var config = new ConfigurationBuilder()
- .SetBasePath(System.IO.Directory.GetCurrentDirectory())
- .AddJsonFile("appsettings.json", false, true)
- .Build();
-
- if (string.IsNullOrEmpty(config["applicationId"]) ||
- string.IsNullOrEmpty(config["applicationSecret"]) ||
- string.IsNullOrEmpty(config["redirectUri"]) ||
- string.IsNullOrEmpty(config["tenantId"]))
- {
- return null;
- }
-
- return config;
- }
- catch (System.IO.FileNotFoundException)
- {
- return null;
- }
- }
- private static IAuthenticationProvider CreateAuthorizationProvider(IConfigurationRoot config)
- {
- var clientId = config["applicationId"];
- var clientSecret = config["applicationSecret"];
- var redirectUri = config["redirectUri"];
- var authority = $"https://login.microsoftonline.com/{config["tenantId"]}/v2.0";
-
- List<string> scopes = new List<string>();
- scopes.Add("https://graph.microsoft.com/.default");
-
- var cca = ConfidentialClientApplicationBuilder.Create(clientId)
- .WithAuthority(authority)
- .WithRedirectUri(redirectUri)
- .WithClientSecret(clientSecret)
- .Build();
- return new MsalAuthenticationProvider(cca, scopes.ToArray());
- }
- private static GraphServiceClient GetAuthenticatedGraphClient(IConfigurationRoot config)
- {
- var authenticationProvider = CreateAuthorizationProvider(config);
- _graphClient = new GraphServiceClient(authenticationProvider);
- return _graphClient;
- }
- }
- }
Build and Test the application
Step 1
Run the following command in command prompt to ensure the developer certificate has been trusted.
dotnet dev-certs https –trust
In this case, I might have already trusted the developer certificates in my previous projects. Usually you will be prompted with a message box, to trust the developer certificates, and select yes.
Step 2
build the application by simply running the following command. This is similar to building application making sure it is compiled successfully in visual studio IDE.
dotnet build
Step 3
Run the application by running the following command
dotnet run
Step 4
Observe that now on successful compilation and run, you should have all the users displayed in your tenant. Since this is developer tenant, I have only few users in this case.
Also notice that the URL that is generated by SDK, which is used to send to Graph API service.
Conclusion
The complete source code is mentioned in the references section. Thus in this article, we have created a console app using .Net 5 which requests token from the Microsoft IDentity platform, with that access token it calls the Graph API service to get the information about the users in the Office 365 Tenant.
References
- Source Code: available here
- https://dotnet.microsoft.com/download/dotnet/5.0
- https://docs.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-windows-desktop
- https://docs.microsoft.com/en-us/graph/sdks/sdk-installation
- https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-5.0
- https://docs.microsoft.com/en-us/archive/msdn-magazine/2016/february/essential-net-configuration-in-net-core
- https://stackoverflow.com/questions/52156484/how-exactly-does-microsoft-extensions-configuration-dependent-on-asp-net-core
- https://www.nuget.org/packages/Microsoft.Extensions.Configuration.FileExtensions/
- https://docs.microsoft.com/en-us/learn/modules/optimize-data-usage/3-exercise-retrieve-control-information-returned-from-microsoft-graph