I am writing a REST API with C#/.NET 8 and Minimal API, which is called from a Web-GUI.
I get a JWTSecurityToken, and a list of group ids in the payload of the JWTSecurityToken.
I need the names of the groups.
Seems I have to query Graph to get these names.
In Entra admin center my application has the API permission "Group.Read.All" set, granted by an admin. And the JWTSecurityToken contains this in "scp" in the payload.
I try to create a GraphServiceClient this way (maybe there’s a better way, I tried various others – this one at least yields a GraphServiceClient):
var clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
// tenantId, clientId, clientSecret and scopes read from appsettings.json or secrets.json.
Then I try to get the group name:
string groupId = "<just for test hard coded one existing group id from the token>";
var singleGroup = await graphClient.Groups[groupId].GetAsync();
Console.WriteLine(singleGroup.DisplayName);
This results in different error messages, depending on the value of scopes I use in new GraphServiceClient(clientSecretCredential, scopes):
For the scopes
scopes = ["Group.Read.All"];
scopes = [$"api://{clientId}/Group.Read.All"];
I get
Azure.Identity.AuthenticationFailedException: ClientSecretCredential authentication failed:
AADSTS1002012: The provided value for scope is not valid.
Client credential flows must have a scope value with /.default suffixed to the resource identifier(application ID URI).
For the scopes (having variations of .default)
scopes = ["Group.Read.All", "https://graph.microsoft.com/.default"];
scopes = ["Group.Read.All", "graph.microsoft.com/.default"];
scopes = ["Group.Read.All", ".default"];
scopes = ["graph.microsoft.com/.default"];
I get
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 is not valid.
For the scopes
scopes = [".default"];
scopes = ["https://graph.microsoft.com/.default"];
I get
Microsoft.Graph.Models.ODataErrors.ODataError: Insufficient privileges to complete the operation.
Am I using the right Graph package? Examples use Microsoft.Graph or Microsoft.Identity.Web.MicrosoftGraph, which one is correct?
Do I use a correct startup routine?
I have:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddInMemoryTokenCaches();
Most examples have
.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
and later
.AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph"))
which leads to a compiler error when I try to add it to my code.
Please, can someone point me to a working example for this? All the examples I found seem to be for older versions of Graph and are not compatible with .NET 8.
2
Answers
You have granted
Delegated
api permission, and what you have now is an API project, so that we could Azure AD on-behalf-flow to do the authentication and then call Graph API. I test within the minimal API project(created a new minimal API but I choose the Authentication Type as Microsoft Identity Platform using VS template) and here’s my code.Here’s my nuget packages.
And we need to add configurations in appsettings.json
If you have concerns about how to get the custom API scope, please follow this tutorial to see how to protect our API via Azure AD.
Note that, client credentials flow won’t work with permissions of
Delegated
type. The correct scope to use with client credentials flow is https://graph.microsoft.com/.default.Initially, I too got same error when I tried to retrieve group name by granting
Delegated
permissions with client credentials flow:To resolve the error, add
Group.Read.All
permission of Application type and make sure to grant admin consent to it while working with client credentials flow like this:When I ran the code again after granting
Application
type permission, I got the response successfully with group name in response as below:Response: