System.ArgumentNullException: IDW10106: The 'ClientId' option must be provided.

Chornologic 1 Reputation point
2022-02-24T04:28:06.957+00:00

I am creating a .NET Core Web API that speaks to Microsoft Graph and in turn returns that API. Everytime I try to make a call however my program throws the error in the Question Title. I have the Client Id in the appsettings.json. With my Startup.cs looking like

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddHttpClient();
        services.AddScoped<GraphHelper>();

        services.AddMicrosoftIdentityWebApiAuthentication(Configuration, "AzureAd")
            .EnableTokenAcquisitionToCallDownstreamApi()
            .AddInMemoryTokenCaches();

        services.AddControllers(options =>
        {
            var policy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
            options.Filters.Add(new AuthorizeFilter(policy));
        });

        var appConfig = new ConfigurationBuilder()
            .AddUserSecrets<Program>()
            .Build();

        if (string.IsNullOrEmpty(appConfig["appId"]) || string.IsNullOrEmpty(appConfig["scopes"])) 
        {
            Console.WriteLine("Missing or Invalid appsettings.json");
            return;
        }

        var appId = appConfig["appId"];
        var scopesString = appConfig["scopes"];
        var scopes = scopesString.Split(';');
        var accessToken = login(appId, scopes);

        services.AddEndpointsApiExplorer();
        services.AddSwaggerGen(options =>
        {
            options.CustomSchemaIds(type => type.FullName);
        });
    }

Is there somewhere else I need to add the Client ID

ASP.NET Core
ASP.NET Core
A set of technologies in the .NET Framework for building web applications and XML web services.
4,560 questions
Microsoft Graph
Microsoft Graph
A Microsoft programmability model that exposes REST APIs and client libraries to access data on Microsoft 365 services.
12,045 questions
{count} votes

2 answers

Sort by: Most helpful
  1. Tiny Wang-MSFT 2,646 Reputation points Microsoft Vendor
    2022-02-25T02:50:56.6+00:00

    Hi @Chornologic , in my humble opinion, there're 2 scenarios for calling ms graph api, one is calling api stand for a user, that means you need your users to sign in first and then call the api on behalf of the user, another one is calling api on behalf of the application, that means your api application worked like a deamon application which doesn't need any users to sign in. The first scenario has to make user sign in so that it can use delegate api permission to generate access token and call api, the second scenario doesn't require a login module as it uses application api permission and use client credential flow to generate access token. You may see these 2 kind of permissions in each ms graph api.

    177669-image.png

    The second seconario is relatively simple. We can use graph client to call ms graph api directly without writing code to send http request.

    using Azure.Identity;  
    using Microsoft.Graph;  
      
    var scopes = new[] { "https://graph.microsoft.com/.default" };  
    var tenantId = "your_tenant_name.onmicrosoft.com";  
    var clientId = "azure_ad_app_client_id";  
    var clientSecret = "client_secret_for_the_azuread_app";  
    var clientSecretCredential = new ClientSecretCredential(  
        tenantId, clientId, clientSecret);  
    var graphClient = new GraphServiceClient(clientSecretCredential, scopes);  
    await graphClient.Users["user_id"].Request().GetAsync();  
    

    The first scenario requires your app to integrate MSAL so that it can make users sign in first, I also have a test before, you may see my answer here for more details. I followed this official document to integrate azure ad into my project.

    Controller:

    using Microsoft.AspNetCore.Mvc;  
    using Microsoft.Extensions.Logging;  
    using System;  
    using System.Collections.Generic;  
    using System.Diagnostics;  
    using System.Linq;  
    using System.Threading.Tasks;  
    using WebMvcApp.Models;  
    using Microsoft.AspNetCore.Authorization;  
    using Microsoft.Identity.Web;  
      
    namespace WebMvcApp.Controllers  
    {  
        [Authorize]  
        public class HomeController : Controller  
        {  
            private readonly ITokenAcquisition _tokenAcquisition;  
            private readonly ILogger<HomeController> _logger;  
      
            public HomeController(ILogger<HomeController> logger, ITokenAcquisition tokenAcquisition)  
            {  
                _logger = logger;  
                _tokenAcquisition = tokenAcquisition;  
            }  
      
            public async Task<IActionResult> IndexAsync()  
            {  
                string[] scopes = new string[] { "user.read" };  
                string accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(scopes);  
      
                return View();  
            }  
        }  
    }  
    

    appsettings.json

    "AzureAd": {  
        "Instance": "https://login.microsoftonline.com/",  
        "Domain": "your_tenant_name.onmicrosoft.com",  
        "TenantId": "tenant_id",  
        "ClientId": "azure_ad_app_clientid",  
        "ClientSecret": "client_secret",  
        "CallbackPath": "/signin-oidc",  
        "SignedOutCallbackPath ": "/signout-callback-oidc"  
      }  
    

    StartUp.cs

    using Microsoft.AspNetCore.Authorization;  
    using Microsoft.AspNetCore.Builder;  
    using Microsoft.AspNetCore.Hosting;  
    using Microsoft.AspNetCore.Mvc.Authorization;  
    using Microsoft.Extensions.Configuration;  
    using Microsoft.Extensions.DependencyInjection;  
    using Microsoft.Extensions.Hosting;  
    using Microsoft.Identity.Web;  
    using Microsoft.Identity.Web.UI;  
      
    public void ConfigureServices(IServiceCollection services)  
    {  
        services.AddMicrosoftIdentityWebAppAuthentication(Configuration)  
            .EnableTokenAcquisitionToCallDownstreamApi(new string[] { "user.read" })  
            .AddInMemoryTokenCaches();  
        services.AddControllersWithViews(options =>  
        {  
            var policy = new AuthorizationPolicyBuilder()  
                .RequireAuthenticatedUser()  
                .Build();  
            options.Filters.Add(new AuthorizeFilter(policy));  
        }).AddMicrosoftIdentityUI();  
    }  
    

    and pls don't forget app.UseAuthentication(); in Configure method.

    Then add login page _LoginPartial.cshtml and add it to layout:

    @using System.Security.Principal  
      
    <ul class="navbar-nav">  
    @if (User.Identity.IsAuthenticated)  
    {  
            <li class="nav-item">  
                <span class="navbar-text text-dark">Hello @User.Identity.Name!</span>  
            </li>  
            <li class="nav-item">  
                <a class="nav-link text-dark" asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="SignOut">Sign out</a>  
            </li>  
    }  
    else  
    {  
            <li class="nav-item">  
                <a class="nav-link text-dark" asp-area="MicrosoftIdentity" asp-controller="Account" asp-action="SignIn">Sign in</a>  
            </li>  
    }  
    </ul>  
    

    177670-image.png


    If the answer is helpful, please click "Accept Answer" and upvote it.
    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,
    TinyWang

    1 person found this answer helpful.

  2. Bruce (SqlWork.com) 65,316 Reputation points
    2022-02-24T16:36:16.397+00:00

    show your app settings (mask the ids)

    0 comments No comments

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.