Problem calling Graph from Outlook addin

Paul Smith 60 Reputation points
2023-10-17T04:38:42.1266667+00:00

I am trying to call Graph from within an Outlook VSTO addin. The code is below. If I compile it into a simple Windows form application (not an addin) it works fine on the development computer and on the client's computer. If I use the identical code in a simple VSTO addin, it works fine on the development computer, but on the client's computer (the same one where the forms application works), the call to graphClient.Me.GetAsync runs into an exception with the message "InteractiveBrowserCredential authentication failed." Any suggestions on what the issue might be, or how to track it down? Thanks.

using System;
using Azure.Identity;
using Microsoft.Graph;
using System.Windows.Forms;

namespace Tasklist2
{
    internal class Graph
    {
        public static async void initializeGraph()
        {
            Microsoft.Graph.Models.User me = new Microsoft.Graph.Models.User();

            var scopes = new[] { "User.Read" };

            var interactiveBrowserCredentialOptions = new InteractiveBrowserCredentialOptions
            {
                TenantId = "common",
                ClientId = "[client Id here]",
                AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
                RedirectUri = new Uri("http://localhost"),
            };

            var tokenCredential = new InteractiveBrowserCredential(interactiveBrowserCredentialOptions);

            var graphClient = new GraphServiceClient(tokenCredential, scopes);

            try
            {
                me = await graphClient.Me.GetAsync();  //Exception here
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

            MessageBox.Show($"Hello {me?.DisplayName}!");
        }
    }
}
Microsoft Graph
Microsoft Graph
A Microsoft programmability model that exposes REST APIs and client libraries to access data on Microsoft 365 services.
10,819 questions
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Sourabh Gupta 795 Reputation points Microsoft Vendor
    2024-02-11T10:41:24.7433333+00:00

    Hi Paul Smith,

    Thanks for reaching out.

    You need to follow the following steps:

    • Go to corresponding app registration.
    • Go To Manage Blade - Authentication tab.
    • Click upon Add a platform and select Mobile and desktop application (Since VSTO com add-in is a desktop app)
    • In Redirect URIs Add a URI and set it to following and check the checkbox https://login.microsoftonline.com/common/oauth2/nativeclient
    • Save your changes.

    Now coming towards the code part.

    You can use the following code to get the User authenticated and create corresponding GraphServiceClient object in order to call Graph API from your VSTO add-in.

    private const string CLIENT_ID = "";
    private const string TENANT_ID = "";
    private const string AUTHORITY = "https://login.microsoftonline.com/" + TENANT_ID; 
    private const string REDIRECT_URI ="https://login.microsoftonline.com/common/oauth2/nativeclient";
    
    private static AuthenticationResult AuthResult;
    private static IPublicClientAppliction PublicClientApp;
    
     ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls| SecurityProtocolType.Tls11| SecurityProtocolType.Tls12| SecurityProtocolType.Ssl3;
    
    //Intilializing public client app
    
    PublicClientApp = PublicClientApplicationBuilder.Create(CLIENT_ID)WithAuthority(AUTHORITY)
        .WithRedirectUri(REDIRECT_URI).
        WithLogging((level, message, containsPii) =>
        { Debug.WriteLine($"MSAL: {level} {message} "); }
        , LogLevel.Warning, enablePiiLogging: false, enableDefaultPlatformLogging: true)
        .Build();
    
    //Authenticating the user and fetching the token. First attempt to aquire the token silently if it fails open the authentication pop up.            try            {
        try
        {
            IEnumerable<IAccount> accounts = await PublicClientApp.GetAccountsAsync().ConfigureAwait(true);
            IAccount firstAccount = accounts.FirstOrDefault();
    
            AuthResult = await PublicClientApp.AcquireTokenSilent(scopes, firstAccount)
            .ExecuteAsync(); 
        }
    catch (MsalUiRequiredException ex){
                // A MsalUiRequiredException happened on AcquireTokenSilentAsync. This indicates you need to call AcquireTokenAsync to acquire a token  
            try
            {
                AuthResult = await PublicClientApp.AcquireTokenInteractive(scopes)
                                                    .WithPrompt(Prompt.SelectAccount)
                                                    .ExecuteAsync()
                                                    .ConfigureAwait(false);
            }
            catch (Exception exc)
            {
                //log the exception;
            }
      
    }
    
    
    //Creating the Graph service client object
    
    GraphServiceClient graphServiceClient =
                    new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) =>
                    {
    
                        // It's good practice to not do work on the UI thread, so use ConfigureAwait(false) whenever possible.
                        IEnumerable<IAccount> accounts = await PublicClientApp.GetAccountsAsync().ConfigureAwait(true);
                        IAccount firstAccount = accounts.FirstOrDefault();
    
                        AuthResult = await PublicClientApp.AcquireTokenSilent(scopes, firstAccount)
                                                        .ExecuteAsync();
    
                        // Add the access token in the Authorization header of the API request.
                        requestMessage.Headers.Authorization =
                                new AuthenticationHeaderValue("Bearer", AuthResult.AccessToken);
    
                    })
                    );
    
    //Making the Graph Call
    User user = graphServiceClient.me.Request().GetAsync();
    
    
    
    
    

    In case you need any help regarding this code and also anything related to Token management fell free to comment.

    If the answer is helpful, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    0 comments No comments

  2. Sourabh Gupta 795 Reputation points Microsoft Vendor
    2024-02-11T10:49:06.2566667+00:00

    Hi Paul Smith,

    Thanks for reaching out.

    First you need to follow the steps below.

    • Go the Corresponding app registration.
    • Under the Manage Blade, click upon Authentication tab.
    • Click upon add a platform and select Mobile and desktop applications
    • In redirect URI add a URI and set it to value as following and select it before saving. https://login.microsoftonline.com/common/oauth2/nativeclient

    In Terms of code You can use the following code to authenticate the user and create the corresponding Graph service client object to make graph calls.

    private const string CLIENT_ID = "";
    private const string TENANT_ID = "";
    private const string AUTHORITY = "https://login.microsoftonline.com/" + TENANT_ID; 
    private const string REDIRECT_URI ="https://login.microsoftonline.com/common/oauth2/nativeclient";
    
    private static AuthenticationResult AuthResult;
    private static IPublicClientAppliction PublicClientApp;
    
     ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls| SecurityProtocolType.Tls11| SecurityProtocolType.Tls12| SecurityProtocolType.Ssl3;
    
    //Intilializing public client app
    
    PublicClientApp = PublicClientApplicationBuilder.Create(CLIENT_ID)WithAuthority(AUTHORITY)
        .WithRedirectUri(REDIRECT_URI).
        WithLogging((level, message, containsPii) =>
        { Debug.WriteLine($"MSAL: {level} {message} "); }
        , LogLevel.Warning, enablePiiLogging: false, enableDefaultPlatformLogging: true)
        .Build();
    
    //Authenticating the user and fetching the token. First attempt to aquire the token silently if it fails open the authentication pop up.            try            {
        try
        {
            IEnumerable<IAccount> accounts = await PublicClientApp.GetAccountsAsync().ConfigureAwait(true);
            IAccount firstAccount = accounts.FirstOrDefault();
    
            AuthResult = await PublicClientApp.AcquireTokenSilent(scopes, firstAccount)
            .ExecuteAsync(); 
        }
    catch (MsalUiRequiredException ex){
                // A MsalUiRequiredException happened on AcquireTokenSilentAsync. This indicates you need to call AcquireTokenAsync to acquire a token  
            try
            {
                AuthResult = await PublicClientApp.AcquireTokenInteractive(scopes)
                                                    .WithPrompt(Prompt.SelectAccount)
                                                    .ExecuteAsync()
                                                    .ConfigureAwait(false);
            }
            catch (Exception exc)
            {
                //log the exception;
            }
      
    }
    
    
    //Creating the Graph service client object
    
    GraphServiceClient graphServiceClient =
                    new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) =>
                    {
    
                        // It's good practice to not do work on the UI thread, so use ConfigureAwait(false) whenever possible.
                        IEnumerable<IAccount> accounts = await PublicClientApp.GetAccountsAsync().ConfigureAwait(true);
                        IAccount firstAccount = accounts.FirstOrDefault();
    
                        AuthResult = await PublicClientApp.AcquireTokenSilent(scopes, firstAccount)
                                                        .ExecuteAsync();
    
                        // Add the access token in the Authorization header of the API request.
                        requestMessage.Headers.Authorization =
                                new AuthenticationHeaderValue("Bearer", AuthResult.AccessToken);
    
                    })
                    );
    
    //Making the Graph Call- me endpoint only works with delegate flow
    User user = graphServiceClient.me.Request().GetAsync();
    
    
    
    
    

    In case you need any help regarding token management. Please reach out in comments

    If the answer is helpful, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

    0 comments No comments