Search code examples
.netmicrosoft-graph-api

How to call graphClient.Groups[groupId].GetAsync()?


I get a JwtSecurityToken with a list of groupIds and I need the names of the groups. I already learned that I have to use MS Graph for this. But by now all I get are different kinds of errors. I've searched and tried so many examples, but I read there were many changes in these APIs, so older examples are not working any more, and I didn't find a recent working one.

So here is my small test application:

 public static void Main(string[] args)
 {
     WebApplicationBuilder builder = WebApplication.CreateBuilder(args);

     // Add services to the container.
     builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
         .AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
     builder.Services.AddAuthorization();

     builder.Services.AddEndpointsApiExplorer();
     builder.Services.AddSwaggerGen();

     WebApplication app = builder.Build();

     // Configure the HTTP request pipeline.
     if (app.Environment.IsDevelopment())
     {
         app.UseSwagger();
         app.UseSwaggerUI();
     }

     app.UseHttpsRedirection();
     app.UseAuthentication();
     app.UseAuthorization();

     // This call is just to prove that the Authorization is okay
     app.MapGet("/Hi", () => "Hi, I'm secured :-)").WithOpenApi().RequireAuthorization();

     app.MapGet("/GraphCallAsync", (HttpContext context) => GetGroupInfo(context, app)).WithOpenApi().RequireAuthorization();

     app.Run();
 }

So I am trying to create a Graph Client, with many variations, but all of them lead to errors when I call graphClient.Groups, see the comments:

static GraphServiceClient CreateGraphClient(WebApplication app)
{
    string? tenantId = app.Configuration["AzureAd:TenantId"];
    string? clientId = app.Configuration["AzureAd:ClientId"];
    string? clientSecret = app.Configuration["AzureAd:ClientSecret"];
    var scopes = new string[];

    scopes = new string[] { "Group.Read.All" };
    //=> Azure.Identity.AuthenticationFailedException: ClientSecretCredential authentication failed:
    //        AADSTS1002012: The provided value for scope Group.Read.All is not valid.
    //        Client credential flows must have a scope value with /.default suffixed to the resource identifier(application ID URI).

    scopes = new string[] { "https://graph.microsoft.com/.default" }
    //=>  Microsoft.Graph.Models.ODataErrors.ODataError: Insufficient privileges to complete the operation.


    scopes = new string[] { $"https://{clientId}/.default" };
    //=>  Azure.Identity.AuthenticationFailedException: ClientSecretCredential authentication failed:
    //        AADSTS500011: The resource principal named https://<** ClientId **> was not found in the tenant named xxxxxx. 
    //            This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. 
    //You might have sent your authentication request to the wrong tenant.

    scopes = new string[] { "Group.Read.All", "https://graph.microsoft.com/.default" };
    //=>  Azure.Identity.AuthenticationFailedException: ClientSecretCredential authentication failed:
    //        AADSTS70011: The provided request must include a 'scope' input parameter. 
    //    The provided value for the input parameter 'scope' is not valid.
    //    The scope Group.Read.All https://graph.microsoft.com/.default is not valid.

    var clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);

    var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
    
    return graphClient;
}

And in this function I put the call:

public static async Task<IResult> GetGroupInfo(HttpContext context, WebApplication app)
        {
            Microsoft.Graph.GraphServiceClient graphClient = CreateGraphClient(app);

            string groupId = "<*** a valid groupId, just for test ***>";

            // this call yields the error
            var singleGroup = await graphClient.Groups[groupId].GetAsync();

            // more code to evaluate singleGroup.DisplayName in case the call would work
            // with the parameter HttpContext I can analyze the token
    }

So, is the syntax of my scopes-array wrong or is there some other code or setting to change?


Solution

  • Sridevi postet the solution, the Group.Read.All permission has to be of Application type, see How to create a working GraphServiceClient and query Graph for group names