How to configure ASP.NET Core application to use windows authentication? - asp.net-core

I want to configure ASP.NET application to use different authentication depends on the environment. So for development environment I want to use Windows authentication and for all other environment I want to use Facebook authentication.
I have already configured Facebook authentication for non development environment. How do I configure windows authentication for development environment? Windows authentication will only be used during development so developers does not have to login every time they run application in VS. We have multiple developers that means the windows identity will be different depend on who is executing it. Once the windows identity is created I will add claims to windows identity.
public class Startup
{
public Startup(IHostingEnvironment env)
{
// some stuff here for building configuration
}
public IConfigurationRoot Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization();
services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime)
{
if(env.IsDevelopment())
{
// How do i use windows authentication here
}
else
{
// this is my custom extension method
app.UseFacebookAuthentication();
}
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}

Windows Auth is configured during the web host configuration in program.cs
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
Specifically it's the UseIISIntegration() line.
Now that on it's own does nothing, it also needs configuring in web.config in the aspNetCore node;
<aspNetCore
processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%"
stdoutLogEnabled="false"
stdoutLogFile=".\logs\stdout"
forwardWindowsAuthToken="true" />
The forwardWindowsAuthToken value needs to be set.
So no, you can't do it within an env.IsDevelopment() check.

If you use IIS Express for development environment, there is a quick way of setting up Windows authentication. There is a launchSettings.json in the Properties folder, and you can easily modify it to use Windows authentication for development without modifying the class Startup.
In this file, you can change "windowsAuthentication" to true, and "anonymousAuthentication" to false.
Here is the sample launchSettings.json:
{
"iisSettings": {
"windowsAuthentication": true,
"anonymousAuthentication": false,
"iisExpress": {
"applicationUrl": "http://localhost:6366/",
"sslPort": 0
}
},
profiles": {
"IIS Express": {...
}
After this modification, you can run the application by selecting IIS Express as the debug target.

Related

How to resolve Request to Long error in Asp.Net Core Azure B2C Configuraiton?

I am new to Asp.Net Core identity.
I have configured the startup as per below. When I run the code in a normal and incognito browser I get the below error.
I have cleared cookies as previous questions have suggested. What is interesting is a high number of cookies get created when loading the sign screen.
My issue is similar to those described in the below old articles. Both solutions seem outdated.
https://www.javaer101.com/en/article/18781756.html
https://blog.bitscry.com/2018/09/19/azure-ad-request-too-long/
using d365fl.DocumentGenerator.blazor_frontend.Data;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Identity.Web;
using Microsoft.IdentityModel.Logging;
namespace d365fl.DocumentGenerator.blazor_frontend
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
ConfigureIdentiy(services);
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
}
private void ConfigureIdentiy(IServiceCollection services)
{
services.AddMicrosoftIdentityWebAppAuthentication(Configuration, "AzureAdB2C");
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
services.Configure<OpenIdConnectOptions>(Configuration.GetSection("AzureAdB2C"));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
IdentityModelEventSource.ShowPII = true;
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
}
}
EDIT 1 - HTTP Request from Developer Toolbar
EDIT 2 - Screen Shot of Cookie data from Developer Toolbar / Network Tab
As we discussed in the comment, the issue is cause by too many cookies.
Please clear your cookies and modify your code to avoid endless loops and back and forth requests.
See this answer for more details.

InstrumentationKey not picked up when stored in key vault

I am using a key-vault to store all the keys used in my bot v4 solution. I suspect the InstrumentationKey is not picked up correctly. What should be the name of the ApplicationInsights- InstrumentationKey in the key-vault.
In appsetting.json the key is added like this :
"ApplicationInsights": {
"InstrumentationKey": "xxxx-xxxx-xxx-xxxx-xxx" }.
I had the same issue. These were the steps I followed to resolve it in an ASP.NET Core MVC 5 application:
In Program.cs file, the code for adding configuration values from Key Vault in Production environment (based on https://learn.microsoft.com/en-us/aspnet/core/security/key-vault-configuration?view=aspnetcore-5.0#use-managed-identities-for-azure-resources):
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
if (context.HostingEnvironment.IsProduction())
{
var builtConfig = config.Build();
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(
azureServiceTokenProvider.KeyVaultTokenCallback));
config.AddAzureKeyVault(
$"https://{builtConfig["KeyVaultName"]}.vault.azure.net/",
keyVaultClient,
new DefaultKeyVaultSecretManager());
}
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
In Azure Key Vault create a new secret with the name "ApplicationInsights--InstrumentationKey" and give it the value of the production application insights instrumentation key. In order for your app service to be able to communicate with the key vault and read the secrets, you need to firstly enable the system assigned service identity for your azure app service and then create a key vault Access Policy for Get, List operations for secrets and assign the policy to the previously created system assigned service identity.
In appSettings.Production.json file:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"KeyVaultName": "{your-keyvault-name}",
"ApplicationInsights": {
"CloudRoleName": "TinyCrm.Web.Prod",
"DisableTelemetry": false,
"EnableAdaptiveSampling": true
}
}
To register the application insights specific stuff for the aspnet core DI framework I created the following extension method. I am using Microsoft.ApplicationInsights.AspnetCore version 2.17.0 nuget package. (taking into consideration what is written here https://learn.microsoft.com/en-us/azure/azure-monitor/app/asp-net-core#user-secrets-and-other-configuration-providers):
public static IServiceCollection AddApplicationInsights(this IServiceCollection services, IConfiguration configuration)
{
// Register the settings for "ApplicationInsights" section as a service for injection from DI container
var applicationInsightsSettings = new ApplicationInsightsSettings();
configuration.Bind(ApplicationInsightsSettings.ApplicationInsightsSectionKey, applicationInsightsSettings);
services.AddSingleton(applicationInsightsSettings);
// Use telemetry initializers when you want to enrich telemetry with additional information
services.AddSingleton<ITelemetryInitializer, CloudRoleTelemetryInitializer>();
// Remove a specific built-in telemetry initializer
var telemetryInitializerToRemove = services.FirstOrDefault<ServiceDescriptor>
(t => t.ImplementationType == typeof(AspNetCoreEnvironmentTelemetryInitializer));
if (telemetryInitializerToRemove != null)
{
services.Remove(telemetryInitializerToRemove);
}
// You can add custom telemetry processors to TelemetryConfiguration by using the extension method AddApplicationInsightsTelemetryProcessor on IServiceCollection.
// You use telemetry processors in advanced filtering scenarios
services.AddApplicationInsightsTelemetryProcessor<StaticWebAssetsTelemetryProcessor>();
// The following line enables Application Insights telemetry collection.
services.AddApplicationInsightsTelemetry();
return services;
}
In Startup.cs file:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
// Call the extension method we created above
services.AddApplicationInsights(Configuration);
}
Important note: Make sure when you create the new Azure App Service to not enable the Application Insights Telemetry integration automatically from the Azure app service creation wizard (see below image). Instead create the application insights resource yourself and setup your app to use the instrumentation key in production environment through following the above steps.
Azure App Service Creation Wizard

Deployed WebAssembly Blazor application doesn't route authentication properly, but locally working

I created a 'normal' WebAssembly Blazor client and server application.
I decided later on to add authentication, so I followed the steps at this address:
https://learn.microsoft.com/en-us/aspnet/core/blazor/security/webassembly/hosted-with-identity-server?view=aspnetcore-3.1&tabs=visual-studio
ending up with this startup code in the server part of the Blazor WebAssembly application:
public class Startup
{
private readonly IWebHostEnvironment _environment;
private readonly IConfiguration _configuration;
public Startup(IWebHostEnvironment environment, IConfiguration configuration)
{
_environment = environment;
_configuration = configuration;
}
public void ConfigureServices(IServiceCollection services)
{
if (_environment.IsDevelopment())
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
_configuration.GetConnectionString("LocalEnvironment")));
}
else
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
_configuration.GetConnectionString("CloudEnvironment")));
}
services.AddDefaultIdentity<ApplicationUser>()
.AddRoles<ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.IdentityResources["openid"].UserClaims.Add("name");
options.ApiResources.Single().UserClaims.Add("name");
options.IdentityResources["openid"].UserClaims.Add("role");
options.ApiResources.Single().UserClaims.Add("role");
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages();
services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier;
options.User.RequireUniqueEmail = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireLowercase = false;
options.Password.RequireUppercase = true;
options.Password.RequireDigit = true;
});
services.AddTransient<IPasswordValidator<ApplicationUser>, CustomPasswordPolicy>();
services.AddTransient<IUserValidator<ApplicationUser>, CustomUsernameEmailPolicy>();
services.AddTransient<IProfileService, ProfileService>();
services.AddHttpContextAccessor();
services.AddHsts(options =>
{
options.Preload = true;
options.IncludeSubDomains = true;
options.MaxAge = TimeSpan.FromDays(60);
});
}
public void Configure(IApplicationBuilder app, ApplicationDbContext db,
UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager)
{
if (_environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseWebAssemblyDebugging();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
db.Database.EnsureCreated();
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html");
});
IdentityDataInitializer.SeedTestData(userManager, roleManager);
}
}
The resulting application works perfectly when in the Development environment (both in Kestrel and IIS Express, but when I deploy it to an Azure Service App, the authentication part, and only that one, doesn't work properly.
For example: if I click the Login button in the home page, when I'm local there's a jump to the page:
https://localhost:5001/Identity/Account/Login?ReturnUrl=...
That's the correct path, because, moreover, after logging in, I'm redirected correctly to the home page.
But when I click the same button on the deployed application, I see the address becoming first
'.../authentication/login'
and after a few moments, going to
'.../connect/authorize?client_id=Test1.Client&redirect_uri=...'
that's a not existing page.
Personally, I don't even understand, at the moment, if it's a server or client problem, or just the configuration of the service app on Azure...
Please, feel free to ask for other code, or anything that can help.
Thank you in advance.
/connect/authorize is one of the endpoints that IdentityServer listens for and it is the first URL that the application/client should redirect to when the user is about to authenticate.
One way to tell if IdentityServer is up and running is to go to this URL https://yourdomain.com/.well-known/openid-configuration
This URL Should always succeed.
When you deploy to the cloud and Azure Service App, one thing is to make sure you understand where HTTPS is terminated, is it in your application or in Azure Service? If it is not terminated in the application it-self, then it might be that the public URL is HTTPS but what your application sees is HTTP.
Some links to follow:
https://securecloud.blog/2020/07/23/unobvious-stuff-about-azure-services-app-service-tls-termination/
https://www.hanselman.com/blog/SecuringAnAzureAppServiceWebsiteUnderSSLInMinutesWithLetsEncrypt.aspx

Enable / Disable SSL on ASP.NET Core projects in Development

On an ASP.NET Core project, I am using SSL in Production so I have in Startup:
public void ConfigureServices(IServiceCollection services) {
services.AddMvc(x => {
x.Filters.Add(new RequireHttpsAttribute());
});
// Remaining code ...
}
public void Configure(IApplicationBuilder builder, IHostingEnvironment environment, ILoggerFactory logger, IApplicationLifetime lifetime) {
RewriteOptions rewriteOptions = new RewriteOptions();
rewriteOptions.AddRedirectToHttps();
builder.UseRewriter(rewriteOptions);
// Remaining code ...
}
It works fine in Production but not in Development. I would like to either:
Disable SSL in Development;
Make SSL work in Development because with current configuration it is not.
Do I need to set any PFX files on my local machine?
I am working on multiple projects so that might create problems?
You can configure a service using the IConfigureOptions<T> interface.
internal class ConfigureMvcOptions : IConfigureOptions<MvcOptions>
{
private readonly IHostingEnvironment _env;
public ConfigureMvcOptions(IHostingEnvironment env)
{
_env = env;
}
public void Configure(MvcOptions options)
{
if (_env.IsDevelopment())
{
options.SslPort = 44523;
}
else
{
options.Filters.Add(new RequireHttpsAttribute());
}
}
}
Then, add this class as a singleton:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
var builder = services.AddMvc();
services.AddSingleton<IConfigureOptions<MvcOptions>, ConfigureMvcOptions>();
}
Concerning the SSL point, you can easily use SSL using IIS Express (source)
If you don't want to use IIS Express then delete the https-address in Project Properties -> Debug section -> Under "Web Server Settings" -> Uncheck "Enable SSL".
just comment this line:
rewriteOptions.AddRedirectToHttps();
or in new versions of .Net core on Startup.cs comment:
app.UseHttpsRedirection();
Using #if !DEBUG, like below:
public void ConfigureServices(IServiceCollection services) {
services.AddMvc(x => {
#if !DEBUG
x.Filters.Add(new RequireHttpsAttribute());
#endif
});
// Remaining code ...
}

ASP.NET 5 beta8 app with virtual directories/applications

Since ASP.NET 5 beta8 we are experiencing problems using virtual directories and/or sub applications.
We want (for the time beeing) to serve images from a virtual directory or a "sub application". However we only get 404 errors when trying to use a virtual directory and 502.3 errors when using a "sub application".
The server is running IIS 8.0. The Application Pools for the site and the "sub application" is set to "No Managed Code".
Using the same configuration of virtual dirs/apps on another site running the "old" ASP.NET 4 version of our site works like expected.
The problem came after upgrading to beta8, so we assume it has something to do with the HttpPlatformHandler.
Are we missing something or is this a bug?
EDIT:
To clarify, the ASP.NET5 application works just fine. It is only the content from the virtual dirs/apps that cannot be accessed.
The HttpPlatformHandler is installed on the server.
Here is our current Startup.cs
public class Startup
{
public Startup(IHostingEnvironment env, IApplicationEnvironment appEnv)
{
var builder = new ConfigurationBuilder()
.SetBasePath(appEnv.ApplicationBasePath)
.AddJsonFile("appsettings.json")
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
builder.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; set; }
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.ConfigureXXXXXXIdentityServices(); // Custom identity implementation
services.AddMvc(options =>
{
options.OutputFormatters
.Add(new JsonOutputFormatter(new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
}));
});
services.AddSqlServerCache(options =>
{
options.ConnectionString = "XXXXXX";
options.SchemaName = "dbo";
options.TableName = "AspNet5Sessions";
});
services.AddSession();
var builder = new ContainerBuilder();
builder.RegisterModule(new AutofacModule());
builder.Populate(services);
var container = builder.Build();
return container.Resolve<IServiceProvider>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.MinimumLevel = LogLevel.Debug;
loggerFactory.AddConsole();
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseFileServer(new FileServerOptions
{
RequestPath = new PathString("/gfx"),
FileProvider = new PhysicalFileProvider(#"\\webdata2.XXXXXX.se\webdata\gfx"),
EnableDirectoryBrowsing = false
});
app.UseFileServer(new FileServerOptions
{
RequestPath = new PathString("/files"),
FileProvider = new PhysicalFileProvider(#"\\webdata2.XXXXXX.se\webdata"),
EnableDirectoryBrowsing = false
});
}
else
{
app.UseExceptionHandler("/Error/Index");
}
app.UseIISPlatformHandler();
app.UseStaticFiles();
app.UseIdentity();
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
The app.UseFileServer() statements works on our dev machines, but cannot be used on the server, unless there is a way to specify credentials. (haven't found a way to do that... (yet...))
Got it "working".
Dropped all virtual directories and/or applications.
Changed the Application Pool user to a user that had rights to read the file shares on the other machine.
Added app.UseFileServer() to all environments for the required paths.
Feels like there should be an option to pass Network Credentials to the UseFileServer method...
The Hosting model changed in beta 8, meaning that you need to install the new HttpPlatformHandler module as an administrator.
See Change to IIS hosting model