Search code examples
c#azureazure-active-directoryazure-functionsmicrosoft-graph-api

How to use [FindMeetingTimes] in the Graph API (SDK 5.35) from Azure Function


If you run a common sample.

1.GraphClient.Me.FindMeetingTimes, the error [/me request is only valid with delegated authentication flow.]

2.GraphClient.Users["id"].FindMeetingTimes, an [Invalid user address] error occurs.

The following is the result.

FindMeetingTimes Sample

Graph Explorer

When I run POST in Graph Explorer (https://graph.microsoft.com/v1.0/users/{id}/microsoft.graph.findMeetingTimes), the response comes back correctly, but when I try with the source output in Code snippets However, an error occurs when trying with the source output in Code snippets.

[sample code (VSCode)]

Azure Function .Net 6 (LTS)Isolated

using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Mvc;
using Azure.Identity;
using Microsoft.Graph;
using Microsoft.Graph.Models;
using Microsoft.Kiota.Abstractions.Authentication;
using Microsoft.Identity.Client;

namespace Company.Function
{
    public class HttpTrigger0
    {
        private readonly ILogger _logger;

        public HttpTrigger0(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<HttpTrigger5>();
        }

        [Function("HttpTrigger0")]
        public async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequestData req)
        {
            _logger.LogInformation("C# HTTP trigger function processed a request.");

            // Dummy Id
            var userId = "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX";

            // https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=csharp#client-credentials-provider
            var scopes = new[] { "https://graph.microsoft.com/.default" };
            var tenantId = Environment.GetEnvironmentVariable("TENANT_ID");
            var clientId = Environment.GetEnvironmentVariable("CLIENT_ID");
            var clientSecret = Environment.GetEnvironmentVariable("CLIENT_SECRET");
            var options = new ClientSecretCredentialOptions  
            {  
                AuthorityHost = AzureAuthorityHosts.AzurePublicCloud  
            }; 
            var clientSecretCredential = new ClientSecretCredential(
                tenantId, clientId, clientSecret, options);
            var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
            var requestBody = new Microsoft.Graph.Me.FindMeetingTimes.FindMeetingTimesPostRequestBody
            //var requestBody = new Microsoft.Graph.Users.Item.FindMeetingTimes.FindMeetingTimesPostRequestBody
            {
                Attendees = new List<AttendeeBase>
                {
                    new AttendeeBase
                    {
                        EmailAddress = new EmailAddress
                        {
                            Name = "User1",
                            Address = "user1@test.onmicrosoft.com",
                        },
                    },
                    new AttendeeBase
                    {
                        EmailAddress = new EmailAddress
                        {
                            Name = "User2",
                            Address = "user2@test.onmicrosoft.com",
                        },
                    },
                    new AttendeeBase
                    {
                        EmailAddress = new EmailAddress
                        {
                            Name = "User3",
                            Address = "user3@test.onmicrosoft.com",
                        },
                    },
                },
            };
            try {
                var result = await graphClient.Me.FindMeetingTimes.PostAsync(requestBody);
                //var result = await graphClient.Users[userId].FindMeetingTimes.PostAsync(requestBody);
                return new OkObjectResult(result);
            } catch (Exception e) {
                Console.WriteLine("TRC:" + e.StackTrace);
                Console.WriteLine("ERR:" + e.Message);
                return new OkObjectResult(null);
           }
        }
    }
}

Solution

  • The error occurred as you are using client credentials flow to retrieve findMeetingTimes which is not supported.

    To resolve the error, you need to switch to delegated flows like interactive flow or authorization code flow.

    In your app registration, grant Calendars.Read.Shared permission of Delegated type while using delegated flows.

    enter image description here

    If you prefer interactive flow, you need to add redirect URI in Mobile and Desktop applications platform like this:

    enter image description here

    Make sure to enable public client flows in your app registration while using interactive flow:

    enter image description here

    In my case, I ran below sample c# code in Console app to retrieve findMeetingTimes using interactive flow:

    using Azure.Identity;
    using Microsoft.Graph;
    using Microsoft.Graph.Models;
    using Microsoft.Graph.Models.ODataErrors;
    
    // Dummy Id
    var userId = "userId";
    
    var scopes = new[] { "https://graph.microsoft.com/.default" };
    var tenantId = "tenantID";
    var clientId = "appID";
    var clientSecret = "secret";
    var options = new InteractiveBrowserCredentialOptions
    {
        TenantId = tenantId,
        ClientId = clientId,
        AuthorityHost = AzureAuthorityHosts.AzurePublicCloud,
        RedirectUri = new Uri("http://localhost"),
    };
    
    var interactiveCredential = new InteractiveBrowserCredential(options);
    
    var graphClient = new GraphServiceClient(interactiveCredential, scopes);
    var requestBody = new Microsoft.Graph.Me.FindMeetingTimes.FindMeetingTimesPostRequestBody
    //var requestBody = new Microsoft.Graph.Users.Item.FindMeetingTimes.FindMeetingTimesPostRequestBody
    {
        Attendees = new List<AttendeeBase>
                    {
                        new AttendeeBase
                        {
                            EmailAddress = new EmailAddress
                            {
                                Name = "User1",
                                Address = "user1@test.onmicrosoft.com",
                            },
                        },
                        new AttendeeBase
                        {
                            EmailAddress = new EmailAddress
                            {
                                Name = "User2",
                                Address = "user2@test.onmicrosoft.com",
                            },
                        },
                        new AttendeeBase
                        {
                            EmailAddress = new EmailAddress
                            {
                                Name = "User3",
                                Address = "user3@test.onmicrosoft.com",
                            },
                        },
                    },
    };
    
    try
    {
        var result = await graphClient.Me.FindMeetingTimes.PostAsync(requestBody);
        Console.WriteLine(result);
    }
    
    catch (ODataError odataError)
    {
        Console.WriteLine(odataError.Error.Code);
        Console.WriteLine(odataError.Error.Message);
    }
    

    Reference: Choose a Microsoft Graph authentication provider - Microsoft Graph