SYL
SYL

Reputation: 155

ITokenAcquisition.GetAccessTokenForUserAsync throws NullReferenceException

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();

NullReferenceException: enter image description here

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:

enter image description here

enter image description here

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

Answers (1)

kavya Saraboju
kavya Saraboju

Reputation: 10839

I tried to reproduce the scenario in my environment using postman.

  • I excluded client secret/certificate while getting the token to the application for the user.
  • I received similar error :

enter image description here

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"
    }
}

enter image description here

And then received the group members successfully.

enter image description here

Reference: Build a web app that authenticates users and calls web APIs - Microsoft Entra | Microsoft Learn

Upvotes: 1

Related Questions