ASP.NET Core 3.1 with Razor pages, api controllers and IdentityServer4 - asp.net-core

I'm creating a boilerplate for ASP.NET Core 3.1 projects with Razor page, api controller and IdentityServer4. The full source code is in my Github.
The configuration in the Startup.cs is
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options => {
options.Authority = "https://localhost:44301";
options.RequireHttpsMetadata = false;
});
services.AddIdentityServer()
.AddDeveloperSigningCredential()
//not something we want to use in a production environment
.AddInMemoryIdentityResources(InMemoryConfig.GetIdentityResources())
.AddTestUsers(InMemoryConfig.GetUsers())
.AddInMemoryClients(InMemoryConfig.GetClients());
}
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseIdentityServer();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
});
app.UseOpenApi();
app.UseSwaggerUi3();
}
I configured the IdentityServer4 with InMemoryConfig. If I use Postman, I have a valid token as result.
The problem occurs when I call the webapi from the url https://localhost:44301/api/Account. When I decorate the apicontroller with [Authorize], I'm using Postman to call it and I pass as authorization the bearer token I've just create and I already receive 404 Not Found. If I try to use Swagger for calling this function and I pass the token, there is no result.
The Swagger page is recognizing correctly all apis. Without the [Authorize] decoration I can test my apis.
When I add the authorization and I add the token to my request, Swagger doesn't show anything, oh well, 404.
I don't know if this is a configuration problem or something different.
Update
I googled a lot and I found a post on the ASP.NET core repo and I tried to update ConfigureService like
// this order seems important for .NET:
// first AddIdentityServer then AddAuthentication
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(InMemoryConfig.GetIdentityResources())
.AddTestUsers(InMemoryConfig.GetUsers())
.AddInMemoryClients(InMemoryConfig.GetClients());
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
// necessary, even if you don't use BearerAuth.
.AddJwtBearer();
then I changed
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
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.UseIdentityServer();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
// this order seems important for .NET
// first UseAuthorization and then UseAuthentication
app.UseAuthorization();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapControllers();
});
app.UseOpenApi();
app.UseSwaggerUi3();
}
Now I receive 401 Unauthorized instead of 404 Non found. Apparently, ASP.NET Core redirects automatically to a login page.

Related

How to set the redirect URI when using Microsoft sign-in in a .NET 5 application?

I have created a .NET 5 application with Microsoft sign-in based on this explanation.
It is working fine when running locally. However, something is going wrong when running the application in Amazon EKS. This became clear to me after reading error message I saw in the browser and after reading the network traffic.
This is how this looks like.
What becomes clear is that there is something wrong with "redirect_uri" (containing http instead of https). This is really frustrating as my application is using https. I use https when opening the application in my browser. It is important to mention that this does not occur when running the application locally on my laptop. What I hope for is that there is a simple way to set the "redirect_uri" property that is used in my code. In this way, I can guarantee that the right redirect uri is used.
Here is the source code I would like to change:
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
var configSettings = new ConfigSettings();
Configuration.Bind("ConfigSettings", configSettings);
services.AddSingleton(configSettings);
services.AddSingleton<IAuthResponseFactory, AuthResponseFactory>();
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
services.AddControllersWithViews(options =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
});
services.AddRazorPages()
.AddMicrosoftIdentityUI();
services.AddHealthChecks();
services.Configure<HealthCheckPublisherOptions>(options =>
{
options.Delay = TimeSpan.FromSeconds(2);
options.Predicate = (check) => check.Tags.Contains("ready");
});
}
// 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();
}
else
{
app.UseExceptionHandler("/Home/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.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health/ready", new HealthCheckOptions()
{
Predicate = (check) => check.Tags.Contains("ready")
});
endpoints.MapHealthChecks("/health/live", new HealthCheckOptions());
});
}
So how do I change my source in a way that I can set the redirect uri correctly?
Looks like you need to enable header forwarding.
Step 1: configure the ForwardedHeadersOptions
services.Configure<ForwardedHeadersOptions>(options =>
{
options.RequireHeaderSymmetry = false;
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// TODO : it's a bit unsafe to allow all Networks and Proxies...
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
Step 2: UseForwardedHeaders in the public void Configure(IApplicationBuilder app, IHostingEnvironment env) method
app.UseForwardedHeaders();
Step 3: Only use UseHttpsRedirection for production
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
// Forward http to https (only needed for local development because the Azure Linux App Service already enforces https)
app.UseHttpsRedirection();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
See How to set redirect_uri protocol to HTTPS in Azure Web Apps and .net Core X Forwarded Proto not working

The [Authorize] attribute can not work in asp.net core web?

I am using cookie authentication in the url:https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-5.0
I use HttpContext.SignInAsync for user successful and then I open other razor pages which has the
[Authorize] attribute it redirected to the login path and tell me I have not loggin.
why?
the startup page:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
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.UseAuthorization();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
and the services:
services.AddRazorPages();
services.AddDbContext<Models.DBContext>(options => options.UseSqlServer(Configuration.GetConnectionString("dbContext")));
services.AddScoped<Models.DBContext>();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Manage/Login";
options.LogoutPath = "/Manage/Login";
options.ExpireTimeSpan = TimeSpan.FromMinutes(3600);
});
Change your code to:
app.UseAuthentication();
app.UseAuthorization();

Blazor Server Side app won't redirect to login page after IIS WebSite is restarted

After I restart IIS Website, I get following error, instead of being redirected to login page:
blazor.server.js:1 POST https://localhost/_blazor/negotiate?negotiateVersion=1 net::ERR_ABORTED 401
when visiting https://localhost/someblazorroute.
I need to manually go to https://localhost/ and hard reload the page (in chrome Ctrl+Shift+R) in order to be redirected to login
I have following startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<IISServerOptions>(options => options.AutomaticAuthentication = false);
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.LoginPath = "/Auth/Login";
options.LogoutPath = "/Auth/Logout";
})
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler >("Basic", null);
services.AddAuthorization(options =>
{
options.AddPolicy("AllowBasicAuthPolicy", policy =>
{
policy.AddAuthenticationSchemes(CookieAuthenticationDefaults.AuthenticationScheme);
policy.AddAuthenticationSchemes("Basic");
policy.RequireAuthenticatedUser();
});
});
}
// 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();
}
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.MapRazorPages().RequireAuthorization();
endpoints.MapBlazorHub().RequireAuthorization();
endpoints.MapFallbackToPage("/_Host");
});
}
I was facing the exact same issue and the solution to this was rather trivial.
you just need to ensure that you call for setting up Authentication, Authorization and then set default route in the right order in startup.cs as shown in the code below:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
.
.
.
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
//this MUST come after the lines before.
app.UseMvcWithDefaultRoute();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}

User.Identity with empty properties when running app on Kestrel

I have two situations during windows authentication using Active Directory domain identities:
When I running my app with IIS I'm getting object WindowsPrincipal filled with information
When I running my app with Kestrel I'm getting object ClaimsPrincipal without any informatiion about the user information
What could be the problem?
My ConfigureService:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<IdentityUser, IdentityRole>().AddRoleManager<RoleManager<IdentityRole>>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationContext>();
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
services.AddAuthorization();
services.AddScoped<IUserManagementManager, UserManagementManager>();
services.AddScoped<IRolesManagementManager, RolesManagementManager>();
}
Configure:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/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.UseResponseCaching();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Authentication}/{action=Index}");
});
}
How did you configure Windows Authentication in the Kestrel? I found that the attribute of IsAuthenticated in your image is false. This may cause you can not get any information about the ClaimsPrincipal.
You can use invoke AddAuthentication and AddNegotiate in Startup.ConfigureServices to add authentication services in Kestrel.
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme)
.AddNegotiate();
More information about how to config windows Authentication in the Kestrel you can refer to this link: https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth?view=aspnetcore-5.0&tabs=visual-studio

.MapWhen executing for all calls

I have a large already running application built on .net core 1.0.4. I have a need to add a micro api to this application, and am attempting to integrate a fairly simple http basic auth middleware into the pipeline only for calls to the api controller.
In my startup.csconfigure method, I have the following.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//run basic auth only on API calls
app.MapWhen(context => context.Request.Path.StartsWithSegments("/api", StringComparison.OrdinalIgnoreCase), appBuilder =>
{
app.UseMiddleware<BasicAuthenticationMiddleware>();
app.UseMvc();
});
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/site/error");
}
app.UseStaticFiles();
app.UseIdentity();
app.UseDefaultFiles();
app.UseStatusCodePages();
app.UseSession();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Site}/{action=Login}/{id?}");
});
}
It's my understanding that the pipeline would branch for all calls that start with /api into the Basic middle ware. My issue is that with this code, ALL calls regardless of the path are hitting the .MapWhen path, and essentially breaking the entire site.