How can I create a Microsoft Graph Client using an AccessToken in C#?

Andy Casey 40 Reputation points
2023-05-30T18:03:24.1133333+00:00

Hello,

We have a .net core web application that we have registered in Azure. The application registration has the Application Permission set for "Sites.Selected". It's also set as a multi-tenant application. The PowerShell commands have been run to assign the correct permissions to the correct site, using the site ID and the App ID. Those permissions have also been verified as well.

In my C# code I am trying to write a helper class, which does all of the GraphClient stuff we need to get done. I am using the ClientCredentials flow, but I keep getting an error "Either scp or roles claim need to be present in the token." I have searched for the best way to implement the ClientCredentials flow and use the Access Token. I finally found the code below that does include the access token, but I still get the same error.

And to clear some things up for people who have asked or commented about this: Yes, I have looked at the access token and no scp or roles claim exists in the token. This folks, is the crux of my problem. How do I get the roles to show up?

        public async Task<GraphServiceClient> InitializeGraphClientAsync()
        {
            // Values from app registration
            var clientId = "MyClientID";
            var clientSecret = "ShhItsASecret";

            
            var tenantId = "common";

            //// using Azure.Identity;
            var options = new TokenCredentialOptions
            {
                AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
            };


            //// https://learn.microsoft.com/dotnet/api/azure.identity.clientsecretcredential
            var clientSecretCredential = new ClientSecretCredential(
                tenantId, clientId, clientSecret, options);

            var accessToken = await clientSecretCredential.GetTokenAsync(new Azure.Core.TokenRequestContext(scopes) { });

            var authProvider = new DelegateAuthenticationProvider((requestMessage) =>
            {
                requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken.Token);
                return Task.CompletedTask;
            });

            return new GraphServiceClient(authProvider);
        }



This is the code I run to get a list of document libraries from the site.

ISiteDrivesCollectionPage docLibraries = await _graphServiceClient.Sites[siteID].Drives.Request().GetAsync();

Microsoft 365 and Office SharePoint For business Windows
Microsoft Security Microsoft Graph
Developer technologies C#
{count} votes

Accepted answer
  1. Anonymous
    2023-06-05T02:54:19.8066667+00:00

    Hi @Andy Casey ,

    I'm glad to hear you solve the problem ,if you have any issue about SharePoint, you are welcome to raise a ticket in this forum.

     

    By the way, since the Microsoft Q&A community has a policy that "The question author cannot accept their own answer. They can only accept answers by others." and according to the scenario introduced here: Answering your own questions on Microsoft Q&A, I would make a brief summary of this thread:

    [How can I create a Microsoft Graph Client using an AccessToken in C#?]

    Issue Symptom:

    In my C# code I am trying to write a helper class, which does all of the GraphClient stuff we need to get done. I am using the ClientCredentials flow, but I keep getting an error "Either scp or roles claim need to be present in the token."

    Solution:

    public GraphServiceClient InitializeGraphClientAsync()
            {
                var MyConfig = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
    
                // Values from app registration
                var clientId = MyConfig.GetValue<string>("AzureAd:ClientId");
                var clientSecret = MyConfig.GetValue<string>("AzureAd:ClientSecret");
    
                //var scopes = new[] { "https://graph.microsoft.com/.default" };
                var scopes = new[] { ".default" };
    
                //// Multi-tenant apps can use "common",
                //// single-tenant apps must use the tenant ID from the Azure portal
                var tenantId = MyConfig.GetValue<string>("AzureAd:TenantId");
    
                //// https://learn.microsoft.com/dotnet/api/azure.identity.clientsecretcredential
                var clientSecretCredential = new ClientSecretCredential(
                    tenantId, clientId, clientSecret);
    
                var graphClient = new GraphServiceClient(clientSecretCredential,scopes);
    
                return graphClient;
            }
    
    
    
    

    You could click the "Accept Answer" button for this summary to close this thread, and this can make it easier for other community member's to see the useful information when reading this thread.

    Thanks for your understanding!


4 additional answers

Sort by: Most helpful
  1. TH-4749-MSFT 3,315 Reputation points
    2023-05-30T19:30:37.0633333+00:00

    Hello Andy Casey,

    Thanks for reaching out. The Sites/SiteID/Drives endpoint you are trying to query requires the Files.Read.All, Files.ReadWrite.All, Sites.Read.All, Sites.ReadWrite.All permissions. Please see article https://learn.microsoft.com/en-us/graph/api/drive-list?view=graph-rest-1.0&tabs=http for more information. You can decode the token via https://jwt.ms to see if the token contains the above permissions.

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

    Thanks.


  2. Andy Casey 40 Reputation points
    2023-06-02T17:57:11.14+00:00

    Turns out I didn't actually need the access token, or I didn't need to generate it myself. This is the solution that worked for me.

            public GraphServiceClient InitializeGraphClientAsync()
            {
                var MyConfig = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
    
                // Values from app registration
                var clientId = MyConfig.GetValue<string>("AzureAd:ClientId");
                var clientSecret = MyConfig.GetValue<string>("AzureAd:ClientSecret");
    
                //var scopes = new[] { "https://graph.microsoft.com/.default" };
                var scopes = new[] { ".default" };
    
                //// Multi-tenant apps can use "common",
                //// single-tenant apps must use the tenant ID from the Azure portal
                var tenantId = MyConfig.GetValue<string>("AzureAd:TenantId");
    
                //// https://learn.microsoft.com/dotnet/api/azure.identity.clientsecretcredential
                var clientSecretCredential = new ClientSecretCredential(
                    tenantId, clientId, clientSecret);
    
                var graphClient = new GraphServiceClient(clientSecretCredential,scopes);
    
                return graphClient;
            }
    
    0 comments No comments

  3. Madhu Ambat 0 Reputation points
    2023-11-27T12:04:24.3633333+00:00

    DelegateAuthenticationProvider is no longer available for Graph version greater than 5.0 version. Is there any way do the same.

    0 comments No comments

  4. Anonymous
    2023-05-31T02:49:58.2833333+00:00

    Hi @Andy Casey ,

    Please add it in your code:

    var authProvider = new DelegateAuthenticationProvider((requestMessage) =>
    {
        requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken.Token);
        return Task.CompletedTask;
    });
    
    

    The purpose of this code is to provide valid authentication credentials for 'GraphServiceGraphServiceClient' when sending requests to the Microsoft Graph API. An Access Token is a common authentication credential that is often used to indicate that a user or application is authorized to access a protected resource.

    In this code, we create a 'DelegateAuthenticationrequestMessage parameter which represents the HTTP request message to send. Inside this method, we set the Authorization Header of the request message, specifying the access token as the authentication credential and specifying its type as Bearer Token.

    By using this custom authentication provider, we tell 'GraphServiceClient that GraphServiceClient will include the access token in the authentication header when sending the request, allowing our request to be properly authorized and executed in the Microsoft Graph API as required operation.

    Here is the complete code:

    using Azure.Identity;
    using Microsoft.Graph;
    
    var scopes = new[] { "https://graph.microsoft.com/.default" };
    
    // Multi-tenant apps can use "common",
    // single-tenant apps must use the tenant ID from the Azure portal
    var tenantId = "tenant id";
    
    // Values from app registration
    var clientId = "client id";
    var clientSecret = "client secret";
    
    // using Azure.Identity;
    var options = new TokenCredentialOptions
    {
        AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
    };
    
    // https://learn.microsoft.com/dotnet/api/azure.identity.clientsecretcredential
    var clientSecretCredential = new ClientSecretCredential(
        tenantId, clientId, clientSecret, options);
    
    var accessToken = await clientSecretCredential.GetTokenAsync(new Azure.Core.TokenRequestContext(scopes) { });
    
    var authProvider = new DelegateAuthenticationProvider((requestMessage) =>
    {
        requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken.Token);
        return Task.CompletedTask;
    });
    
    var graphClient = new GraphServiceClient(authProvider);
    
    // use graphClient to perform other operations
    
    

    Here are some links for your reference:

    https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS

    https://learn.microsoft.com/en-us/dotnet/api/microsoft.graph.delegateauthenticationprovider?view=graph-core-dotnet

    https://csharp.hotexamples.com/examples/-/DelegateAuthenticationProvider/-/php-delegateauthenticationprovider-class-examples.html


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

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    Best Regards

    Cheng Feng


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.