'Obtaining access to Graph API without user interaction

I'm new to graph API. I'm creating an app that can access user email using Graph API. I've been using the device token method which is used here.

The above code is working with my application. But I want to automate this process. I found some help from Microsoft documents here, from the sample codes here, and from this SO post.

But I can't get my code to obtain the token automatically. I thought it was an API permissions issue so I set them up like below.

API permissions.

I was able to get the AcquireTokenInteractive but AcquireTokenSilent is not working.

UPDATE: I've managed to get an exception the message is this "No account or login hint was passed to the AcquireTokenSilent call.". Also found that variable FirstAccount seems to be empty.

Below is my code for obtaining the token.

using Azure.Core;
using Azure.Identity;
using Microsoft.Graph;
using Microsoft.Identity.Client;
using System.Net.Http.Headers;

namespace DEA
{
    public class GraphHelper
    {
        private static GraphServiceClient? graphClient;
        private static AuthenticationResult token;
        
        private static IPublicClientApplication? application;        

        public static async void InitializeAuto(string ClientID, string InstanceID, string TenantID, string GraphUrl, string[] scopes)
        {
            string auth = string.Concat(InstanceID, TenantID);
            application = PublicClientApplicationBuilder.Create(ClientID)
                                        .WithAuthority(auth)
                                        .WithDefaultRedirectUri()
                                        .Build();

            try
            {
                var accounts = await application.GetAccountsAsync();

                graphClient = new GraphServiceClient(GraphUrl,
                    new DelegateAuthenticationProvider(async (requestMessage) =>
                    {
                        token = await application.AcquireTokenInteractive(scopes, accounts.FirstOrDefault()).ExecuteAsync();
                        requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", token.AccessToken);
                    }
                    ));
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception thrown: {0}", ex.Message);
            }  
        }
}

The above function is called from my "Program.cs".

using DEA;
using Microsoft.Extensions.Configuration;

var appConfig = LoadAppSettings();

if (appConfig == null)
{
    Console.WriteLine("Set the graph API pemissions. Using dotnet user-secrets set .... They don't exsits in this computer.");
    return;
}

var appId = appConfig["appId"];
var TenantId = appConfig["TenantId"];
var Instance = appConfig["Instance"];
var GraphApiUrl = appConfig["GraphApiUrl"];
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };

GraphHelper.InitializeAuto(appId, Instance, TenantId, GraphApiUrl, scopes);

static IConfigurationRoot? LoadAppSettings()
{
    var appConfig = new ConfigurationBuilder()
        .AddUserSecrets<Program>()
        .Build();

    // Check for required settings
    if (string.IsNullOrEmpty(appConfig["appId"]) ||
        string.IsNullOrEmpty(appConfig["scopes"]))
    {
        return null;
    }

    return appConfig;
}

I don't know what I'm doing wrong. I used a try ... catch but still nothing. The only error I get is an exception thrown by the graph client call when I press option 2 of my app.

Exception

Can someone please help me to solve this, please?



Solution 1:[1]

After reading @Marc and @Jeremy Lakeman I rewrote the code using IConfidentialClientApplication. And used this as a guide from Microsoft guides.

And came up with the below code and it works now.

public static async void InitializeAuto(string ClientID, string InstanceID, string TenantID, string GraphUrl, string ClientSecret, string[] scopes)
        {
            string auth = string.Concat(InstanceID, TenantID);

            application = ConfidentialClientApplicationBuilder.Create(ClientID)
                          .WithClientSecret(ClientSecret)
                          .WithAuthority(new Uri(auth))
                          .Build();

            Console.WriteLine("Auth: {0}", auth);
            Console.WriteLine("Client Secrets: {0}", ClientSecret);

            try
            {
                graphClient = new GraphServiceClient(GraphUrl,
                    new DelegateAuthenticationProvider(async (requestMessage) =>
                    {
                        AuthToken = await application.AcquireTokenForClient(scopes).ExecuteAsync();
                        requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", AuthToken.AccessToken);
                    }
                    ));
                /*result = await application.AcquireTokenForClient(scopes)
                         .ExecuteAsync();

                Console.WriteLine("Token: {0}", result.AccessToken);*/
            }
            catch (MsalUiRequiredException ex)
            {
                // The application doesn't have sufficient permissions.
                // - Did you declare enough app permissions during app creation?
                // - Did the tenant admin grant permissions to the application?
                Console.WriteLine("Exception: {0}", ex.Message);
            }
            catch (MsalServiceException ex) when (ex.Message.Contains("AADSTS70011"))
            {
                // Invalid scope. The scope has to be in the form "https://resourceurl/.default"
                // Mitigation: Change the scope to be as expected.
                Console.WriteLine("Scope provided is not supported");
            }  
        }

Solution 2:[2]

Could you please do a fresh start and try again with AcquireTokenSilent(), lets see if you got any error .

{
         var accounts = await application.GetAccountsAsync();
         result = await application.AcquireTokenSilent(scopes, 
         accounts.FirstOrDefault())
         .ExecuteAsync();
   }
   catch (MsalUiRequiredException ex)
   {
         result = await application.AcquireTokenInteractive(scopes)
         .WithClaims(ex.Claims)
         .ExecuteAsync();
   }

doc - https://github.com/Azure-Samples/ms-identity-dotnet-desktop-tutorial/tree/master/1-Calling-MSGraph/1-1-AzureAD

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 S4NDM4N
Solution 2 vicky kumar