16

I have an ASP.NET Core 2.1 Web Application with Razor Pages which has AAD authentication information defined in the appsettings.json file (courtesy of the default application template - see below on how I got there). However, when trying to configure the authentication in Startup.cs the configuration does not have any of the config values from my appsettings.json. If I inspect the IConfiguration object in the debugger then it appears to only have the environment variable configurations:

enter image description here

Here's the Startup.ConfigureServices method where the issue lies:

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        // This lambda determines whether user consent for non-essential cookies is needed for a given request.
        options.CheckConsentNeeded = context => true;
        options.MinimumSameSitePolicy = SameSiteMode.None;
    });

    services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
        .AddAzureAD(options =>
        {
            // This is from the default template. It should work, but the relevant settings aren't there so options isn't populated.
            this.Configuration.Bind("AzureAd", options);

            // This of course works fine
            options.Instance = "MyInstance";
            options.Domain = "MyDomain";
            options.TenantId = "MyTenantId";
            options.ClientId = "MyClientId";
            options.CallbackPath = "MyCallbackPath";
        });

    services.AddMvc(options =>
    {
        var policy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .Build();
        options.Filters.Add(new AuthorizeFilter(policy));
    })
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

And the service configuration in case it's important (note that this is being built on top of a service fabric stateless service):

protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
    return new ServiceInstanceListener[]
    {
        new ServiceInstanceListener(serviceContext =>
            new KestrelCommunicationListener(serviceContext, "ServiceEndpoint", (url, listener) =>
            {
                ServiceEventSource.Current.ServiceMessage(serviceContext, $"Starting Kestrel on {url}");

                return new WebHostBuilder()
                            .UseKestrel(opt =>
                            {
                                int port = serviceContext.CodePackageActivationContext.GetEndpoint("ServiceEndpoint").Port;
                                opt.Listen(IPAddress.IPv6Any, port, listenOptions =>
                                {
                                    listenOptions.UseHttps(GetCertificateFromStore());
                                    listenOptions.NoDelay = true;
                                });
                            })
                            .ConfigureServices(
                                services => services
                                    .AddSingleton<StatelessServiceContext>(serviceContext))
                            .UseContentRoot(Directory.GetCurrentDirectory())
                            .UseStartup<Startup>()
                            .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
                            .UseUrls(url)
                            .Build();
            }))
    };
}

To create this service, I used the wizard in VS2017. I selected an existing service fabric project (.sfproj) and chose Services > Add > New Service Fabric Service and chose Stateless ASP.NET Core [for .NET Framework], then on the next page I chose Web Application (the one with Razor Pages, not MVC) and clicked Change Authentication where I chose Work or School Accounts and entered my AAD info. The only changes I have made to this template were adding the code inside the call to AddAzureAD in Startup.ConfigureServices and setting the appsettings.json files to always be copied to the output directory.

Why doesn't the appsettings.json file get loaded into the configuration? As I understand, this is supposed to happen by default, but something seems to be missing...

4
  • 3
    WebHostBuilder doesn't load appsettings.json by default, you need to manually call AddJsonFile. Commented Nov 13, 2018 at 23:36
  • Well I'll be damned. Sure enough, that solved my problem. Instead of doing that I actually replaced new WebHostBuilder() with WebHost.CreateDefaultBuilder(), but why the template doesn't work out of the box is beyond me. Thanks! Commented Nov 13, 2018 at 23:45
  • 1
    Yes, that will also load the config. I believe it was change in 2.0, perhaps your template is an older one? Commented Nov 13, 2018 at 23:46
  • It's possible. Who knows when the templates were last updated, but they're apparently not tested very thoroughly Commented Nov 13, 2018 at 23:50

6 Answers 6

27

WebHostBuilder doesn't load appsettings.json by default, you need to manually call AddJsonFile. For example:

return new WebHostBuilder()
            .UseKestrel(opt =>
            {
                //snip
            })
            .ConfigureAppConfiguration((builderContext, config) =>
            {
                config.AddJsonFile("appsettings.json", optional: false);
            })
            .ConfigureServices(
                services => services
                    .AddSingleton<StatelessServiceContext>(serviceContext))
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup<Startup>()
            .UseServiceFabricIntegration(listener, ServiceFabricIntegrationOptions.None)
            .UseUrls(url)
            .Build();

Alternatively you can use WebHost.CreateDefaultBuilder which will load more defaults.

Sign up to request clarification or add additional context in comments.

Comments

5

Another approach, would be to manually create the configuration via ConfigurationBuilder then use the UseConfiguration method.

var configuration = new ConfigurationBuilder()
     .SetBasePath(Directory.GetCurrentDirectory())
     .AddJsonFile("appsettings.json", false, true)
     .Build();

var host = new WebHostBuilder()
     .UseConfiguration(configuration)
     .UseKestrel()
     .UseStartup<Startup>();

The primary intent is core to provide a bit of flexibility when implementing, they often error on less is more. You have to explicitly say what you would like, that way the pipeline remains relatively small.

Comments

4

For others like me who find this issue:

It might be that you're not copying the appsettings.json file during build.

The OP does say he's doing this, but it's kind of a small print thing - and was what I was failing to do.

The more you know ...

Comments

2

Below mentioned steps worked for me

  1. Go to Appsettings.json
  2. Right click and goto properties
  3. select the build action from the drop down to none if its content
  4. Make the copy to Output directory as Copy Always

Comments

0

As mentioned before WebHostBuilder do not execute this default behavior CreateDefaultBuilder needs to be called instead.

I prefer following implementation:

public static void Main(string[] args)
{
     var host = CreateHostBuilder(args).Build();
     var logger = host.Services.GetRequiredService<ILogger<Program>>();

     try
     {
         logger.LogInformation("Starting up");
                
         host.Run();
      }
      catch (Exception ex)
      {
         logger.LogCritical(ex, "Application start-up failed");
      }
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseContentRoot(Directory.GetCurrentDirectory())
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

Comments

0

Make sure your file name is all lower case.

My mistake was to name the file appSettings.json instead of appsettings.json. When running within a Linux container, the camel-cased file was not loaded.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.