Reputation: 155
In the Startup.cs of web api app, I have the following code. When this runs it throws NullReferenceException at GetAccessTokenForUserAsync. Any idea what I am missing?
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Events = new JwtBearerEvents()
{
OnTokenValidated = async ctx =>
{
var tokenAcquisition = ctx.HttpContext.RequestServices
.GetRequiredService<ITokenAcquisition>();
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(async (request) =>
{
var token = await tokenAcquisition
.GetAccessTokenForUserAsync(new[] { "User.Read", "User.ReadBasic.All", "GroupMember.Read.All" }, user: ctx.Principal);
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token);
}));
// Get user information from Graph
var groups = await graphClient.Me.CheckMemberGroups(new[] { Constants.GroupId })
.Request()
.PostAsync();
}
};
});
services
.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddMicrosoftIdentityWebApi(this.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph()
.AddInMemoryTokenCaches();
Update: I Just figured out that the issue is because I am missing the authenticationScheme: JwtBearerDefaults.AuthenticationScheme parameter value for the GetAccessTokenForUserAsync method call but now I am getting this exception message ""IDW10104: Both client secret and client certificate cannot be null or whitespace, and only ONE must be included in the configuration of the web app when calling a web API. For instance, in the appsettings.json file. "
Am I not using a token to call the endpoint? Why do I need a client secret or certificate? Can I do without? The token that the web API get from the caller is from an authenticated user and the web API has delegated permission of the user?
I am new to this so please bear with me. Thanks!
Update2: After adding the client secret, I am getting the following exception:
Update3:
public void ConfigureServices(IServiceCollection services)
{
var principalProvider = new ClaimsPrincipalProvider();
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.Events = new JwtBearerEvents()
{
OnTokenValidated = async ctx =>
{
try
{
var tokenAcquisition = ctx.HttpContext.RequestServices
.GetRequiredService<ITokenAcquisition>();
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(async (request) =>
{
var token = await tokenAcquisition
.GetAccessTokenForUserAsync(new[] { "User.Read", "User.ReadBasic.All", "GroupMember.Read.All" }, user: ctx.Principal, authenticationScheme: JwtBearerDefaults.AuthenticationScheme);
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token);
}));
var groups = await graphClient.Me.CheckMemberGroups(
new[]
{
Constants.GroupId
})
.Request()
.PostAsync();
}
catch (System.Exception ex)
{
}
}
};
});
services
.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddMicrosoftIdentityWebApi(this.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi()
.AddMicrosoftGraph()
.AddInMemoryTokenCaches();
services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme);
defaultAuthorizationPolicyBuilder = defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
}
Upvotes: 2
Views: 1597
Reputation: 10839
I tried to reproduce the scenario in my environment using postman.
Basicallly, when Web apps call web APIs we need some valid secret token to give access to secure app.In general they are confidential client applications. So the secret that is registered in the azure ad for the app must be passed during the call to Azure AD endpoint to get a token.
Then I provided a valid value of secret to the endpoint and received to token successfully to call my Api. In your app it must be included in
appsettings.json:
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "[xxxxx]",
"TenantId": "<tenantId here>"
// To call an API
"ClientSecret": "[Copy the client secret added to the app from the Azure portal]",
},
"MyApi": {
"BaseUrl": "https://graph.microsoft.com/beta",
"Scopes": "https://graph.microsoft.com/.default"
}
}
And then received the group members successfully.
Reference: Build a web app that authenticates users and calls web APIs - Microsoft Entra | Microsoft Learn
Upvotes: 1