How are middlewares executed in ASP.NET Core - c#

I'm adding Auth0 to simple project and trying to understand how middlewares work.
In my Startup.cs I have this code
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IOptions<AuthSettings> auth0Settings)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
// Add the cookie middleware
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
// Add the OIDC middleware
var options = new OpenIdConnectOptions("Auth0")
{
// here there are some configurations
// .....................
};
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("name");
options.Scope.Add("email");
options.Scope.Add("picture");
app.UseOpenIdConnectAuthentication(options);
app.UseMvc(routeBuilder =>
{
routeBuilder.MapRoute("Default", "{controller=Home}/{action=Index}");
});
}
If I understand correctly the idea of middleware in ASP.NET Core in our example, if there is a cookie present and authentication can be done by it
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true
});
OpenId middleware will not be executed.
app.UseOpenIdConnectAuthentication(options);
Could somebody explain me how does OpenId middleware knows that it should not be executed?
At the bottom we have
app.UseMvc(routeBuilder =>
{
routeBuilder.MapRoute("Default", "{controller=Home}/{action=Index}");
});
How does it knows that it should always be executed but in case where we request some static file we do not use mvc.

Every single middleware in the pipeline can choose to call the next middleware. The reason you get static files instead of it hitting an MVC controller is because the static file middleware finds the file requested, and chooses not to call the next middleware in the chain. It simply returns the file as a response.
AutomaticAuthenticate in authentication middleware always means "Inspect the incoming request. If you find something that interests you, create a ClaimsPrincipal from it." In this case cookie authentication automatically creates a principal for the signed-in user when their sign-in cookie is in the request, before passing the request to the next middleware.
The OpenId Connect middleware executes actually, but it doesn't do anything because it won't find anything interesting in the request even if it had AutomaticAuthenticate = true. It is looking for requests to its callback path, which by default is set as CallbackPath = new PathString("/signin-oidc"); in the constructor.
The two authentication middleware are setup like this so that the cookie middleware runs always, but OpenId Connect only redirects to the identity provider when requested (e.g. by returning a ChallengeResult from your MVC controller).

Related

Setting HttpOnly Cookies in .NET 3.1 Back-End

I'm working on a project whose back-end is built in .NET 3.1 and Angular 11 for the front-end. I'm trying to set the HttpOnly attribute on true for cookies via the Startup method, which is extended by various APIs and Managers through the entire application: so far the current configuration is as follows.
public void ConfigureServices(IServiceCollection services)
{
...
services.Configure<CookiePolicyOptions>(options =>{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
options.HttpOnly = HttpOnlyPolicy.Always;
options.Secure = CookieSecurePolicy.Always;
});
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
...
app.UseCookiePolicy();
...
}
Anyways, when checking via the Network tab in Chrome/Firefox the HttpOnly flag is not set, what am I missing?
Thanks for any help!
It seems like you need to pass in the cookie policy options you created.
Using this line
services.Configure<CookiePolicyOptions>(options ...
makes the cookie policy available later on but doesn't configure it in your startup.
According to https://learn.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-3.1#cookie-policy-middleware-1 you'll need to create a cookie policy and then pass that in to
app.UseCookiePolicy();
Otherwise, it uses the default cookie policy which doesn't have them set to what you would prefer.

How to restrict multi-tenant login to specific tenants via code?

I have built a very simple Razor Pages app that is hosted using the Azure free plan and which uses multi-tenant authentication.
It's a simple application, not an Enterprise Application.
It seems like tenant restrictions are what I'd need, but those seem to require a premium plan.
Is there some way to specify in code that only certain tenants are allowed? Basically, if a user signed in using a different domain account than what I've allowed, I want to send a 403 error (doesn't have to look pretty, as usually, only users from one or two specific tenants would sign in anyway, it's not a public website).
Like this, for all pages (pseudocode):
if(!User.Identity.Name.EndsWith("#alloweddomain.com"))
{
throw new NotAuthorizedException(); // Or HTTP 403
}
It would be even better if I could configure that somehow in the authentication configuration.
This is what I have so far (autogenerated by Visual Studio):
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.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"));
;
services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy
options.FallbackPolicy = options.DefaultPolicy;
});
services.AddRazorPages()
.AddMvcOptions(options => { })
.AddMicrosoftIdentityUI();
}
// 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();
endpoints.MapControllers();
});
}
}
I tried to create an environment with multi-tenant login and restrict user .
Here are the steps i followed :
Created a Azure active directory tenant
In visual studio select Authentication account and provide my domain as below;
Here i have my client id which was created (App registration)
or we can create manually by going to Azure portal and we can select following to which account we want provide authentication
After mentioning client id and callbackurl as above now it allow users to login
Now, to restrict user to not allow to the webapp ,in your startup.cs change the default setting as below instead of validateissuer = false make it as true and .
provide the client id which mention in app settings.json
Here also we can provide multiple usser whom we want to allow by providing the client id
ValidateIssuer = true,
valiadIssuer = new List<string>()
{"https://sts.windows.net/clientid***/" }
Please refer this Microsoft documentation for more information : Build a multi-tenant daemon with the Microsoft identity platform endpoint & to Restrict who can sign in to your application .

ASP.NET Core: InvalidOperationException: No authenticationScheme was specified, and there was no DefaultSignInScheme found

We have a brand new ASP.NET Core site, for the purpose of upgrading an old ASP.NET Framework site. We used DotNetOpenAuth for OpenID logins back then, and now we're trying to replicate in ASP.NET Core.
We have gotten as far as our site redirecting to the OpenID provider, being able to log in, but it throws an exception upon calling back to our site:
InvalidOperationException: No authenticationScheme was specified, and there was no DefaultSignInScheme found.
I am unable to find a complete example, which would really help me understand the whole process. I have pulled together the following from a variety of sources...
In Startup's ConfigureServices:
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always; //require https
// Make the session cookie essential
options.Cookie.IsEssential = true;
});
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false; //true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSimpleInjector(_simpleInjectorContainer, options =>
{
// AddAspNetCore() wraps web requests in a Simple Injector scope.
options.AddAspNetCore()
// Ensure activation of a specific framework type to be created by
// Simple Injector instead of the built-in configuration system.
.AddControllerActivation()
.AddViewComponentActivation();
//.AddPageModelActivation()
//.AddTagHelperActivation();
});
services.AddAuthentication(options =>
{ /* Authentication options */
//options.DefaultAuthenticateScheme = "Steam";
})
.AddSteam(options =>
{
});
With no prior experience in ASP.NET Core, I have blindly tried assigning "Steam" to DefaultAuthenticateScheme inside of AddAuthentication, but that throws an error saying it cannot call itself.
We've been using the default Home controller as a testing ground:
[HttpGet("~/signin")]
public IActionResult SignIn()
{
// Instruct the OIDC client middleware to redirect the user agent to the identity provider.
// Note: the authenticationType parameter must match the value configured in Startup.cs
return Challenge(new AuthenticationProperties
{
RedirectUri = Url.Action("HandleSteamLogin", "Home"),
}, "Steam");
}
public async Task<IActionResult> HandleSteamLogin()
{
//Everything in this method is marked as obsolete, so it's a poor example. I guess it's from an older version of ASP.NET Core?
var claimsPrincipal = await HttpContext.Authentication.AuthenticateAsync("ExternalCookie");
//do something the the claimsPrincipal, possibly create a new one with additional information
//create a local user, etc
await HttpContext.Authentication.SignInAsync("MainCookie", claimsPrincipal);
await HttpContext.Authentication.SignOutAsync("ExternalCookie");
return Redirect("~/");
}
I can even see in the exception data, we are getting the necessary login information back, but I really need a complete example for me to be able to understand.

Authorization by a Claim of a Role on Web API using JWT Token- Asp.net Core Identity

I have been learning Asp.Net Identity on the past few days, I am familiar with authorizing the controller with [Authorize(Roles = "Admin")] or [Authorize(Policy = "OnlyAdminAndModerators")] for example.
I am using JWT token, when authorizing via "[Authorize(Roles = "Admin")]" all I have to do is set a role type on my token, like this:
{
"nameid": "a173e923-1808-4d7d-2b64-08d684882677",
"unique_name": "yuri",
"role": [
"Admin",
"Moderator"
],
"nbf": 1549522727,
"exp": 1549609127,
"iat": 1549522727
}
With this, my controller is able to authenticate via the "role" name on the json and the value of "Admin".
What I have heard is that it is possible to create a role on the Identity AspNetRole Table, associate a claim to the role via the AspNetRoleClaims table, so for example Admin would have "CanAdd" claim, then on the Startup class, I could create a Policy saying something like options.AddPolicy("Add Role", policy => policy.RequireClaim("CanAdd", "AddClaim"));
And then finally I could go on my controller, set a method with [Authorize(Policy = "Add Role")] and the controller would authorize any user with the Role of Admin because he would have the CanAdd claim.
Sorry I know it's a big question but I really want to make this work.
Thanks in advance.
One way to get additional claims retrieved based on the contents of your token can be done in an message handler that runs after the reading of the token and before the authorization step. For .NET Full framework I used OWin to do this. This block injects additional claims into the claimsPrinciple that can be used then in the policies you define.
This is my startup file:
ConfigureAuthorization -> my extension method to wrap tge BearerTokenAuthentication owin block
IncludeAzureActiveDirectoryUserClaims -> get claims from Azure APi and add them...
using Owin;
[assembly: OwinStartup(typeof(Token.API.Startup))]
namespace Token.API
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.ConfigureAuthorization(ClaimsProviders
.InitializeAuthorizationProviders()
.IncludeAzureActiveDirectoryUserClaims()
);
}
}
}
If I would do it for .NET Core , it would look something like this:
Bearer Authentication: link
In startup.cs:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseAuthentication();
app.Use(async (context, next) =>
{
//Retrieve claims from database based on roles in token.
// Add to loaded identity (= context.User)
await next.Invoke();
});

Location of UseRequestLocalization() call in Startup

I have a custom implementation of IRequestCultureProvider. This provider checks if the user is not logged in, then it sets the current culture, otherwise it does not set the culture. Other culture providers would set the current culture, if user is logged in.
In Configure method I use UseRequestLocalization() after authentication middleware so that HttpContext.User property is populated. However, the property httpContext.User.Identity.IsAuthenticated is never true, and it always sets the culture.
The logs show that Culture Provider sets the culture prior to bearer token authentication middleware call, which should populate the HttpContext.User property.
I do not understand why.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
{
var localizationOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
var defaultRequestCultureProvider = app.ApplicationServices.GetService<DefaultRequestCultureProvider>();
localizationOptions.RequestCultureProviders.Insert(1, defaultRequestCultureProvider);
app.Map("/api", webApiApp => ConfigureWebApiBranch(webApiApp,localizationOptions.Value));
// Removed for brevity
}
private void ConfigureWebApiBranch(IApplicationBuilder webApi, RequestLocalizationOptions localizationOptions)
{
webApi.UseCors(Constants.CorsPolicyName);
webApi.UseAuthentication();
webApi.UseRequestLocalization(localizationOptions);
webApi.UseMvc();
}
I am using IdentityServer4 bearer token authentication middleware.
There was no cookie attached with the request, so authentication was not successful.

Categories