I would like to know whether I am
(a) engaging in good coding practices,
(b) repeating myself harmlessly, or
(c) adding inefficient redundancies
For example:
1) In Configure() I can add RewriteOptions().AddRedirectToHttps();
2) In ConfigureServices() I can add
services.Configure<MvcOptions>(options =>
{ options.Filters.Add(new RequireHttpsAttribute()); });
Another example:
1) In Configure() I can add app.UseAuthentication();
2) In ConfigureServices() I can add .RequireAuthenticatedUser() to my AddMvc() call.
It seems in both examples that I can get away with just one call. Am I free and clear to keep only one call? And if so, which one is the better to keep?
I've searched around a fair bit and I can see all of these approaches in use, but I haven't found a resource that compares the relative merits of these calls, let alone indicates whether it's good or bad practice to use them together.
To take your first example:
1) In Configure() I can add RewriteOptions().AddRedirectToHttps();
2) In ConfigureServices() I can add services.Configure<MvcOptions>(options =>
{ options.Filters.Add(new RequireHttpsAttribute()); });
Both of these achieve essentially the same thing - they will redirect HTTP requests to HTTPS. The difference is which requests they are applied to.
If you use the rewriter middleware, all requests that make it to the middleware will be redirected to HTTPS.
public void Configure(IApplicationBuilder app)
{
app.UseStaticFiles(); // Requests handled by this middleware won't be redirected to HTTPS
var options = new RewriteOptions()
.AddRedirectToHttps();
app.UseRewriter(options); // All requests that make it this far will be redirected from HTTP to HTTPS
app.UseMvc(); // Requests guaranteed to be HTTPS
}
In the second case, where you use a global filter to apply the RequireHttpsAttribute, only requests that make it to the MvcMiddleware will be redirected to HTTPS.
In terms of best practices, I recommend using the rewriter middleware - you can add it to the start of your middleware pipeline, and then all of your requests are required to HTTPS, instead of just the requests that make it to the MVC middleware.
In your second example, the two methods actually do different things:
app.UseAuthentication() - authenticates the request, and sets the User associated with the request by e.g. deserializing the user principal stored in the cookie
RequireAuthenticatedUser() - Requires that a User has logged in before action methods on your controllers are called. If the user hasn't logged in, they are redirected to the login page. In this case, you must call app.UseAuthentication() before app.UseMvc(), otherwise the User for the request will not be set even if you've logged in, and you will be redirected to the login page.
Related
In my current project I am using cookie authentication to secure my controllers.
However, for one particular controller (which will simply be used as API controller) I want the endpoints to be secured using Azure AD.
My use case is that the application serves as a website where users log in and the authentication is stored as a cookie. This already works. Now I want to extend a new controller that will be called via a Logic App.
But I only want the Logic App to be able to call this endpoint. So I created a system managed identity for the Logic App and now I want to secure this new API controller/endpoint.
I have read many articles explaining how to implement multiple schemes. But I don't understand how to implement cookie auth + this particular authentication method.
Perhaps a different method is required, thus I am asking it here. Preferably I don't want to edit the existing working code but rather have a [Authorize(Policy = "ManagedApp")] policy at the top of the new controller.
Any help is appreciated, I am pretty of stuck.
Current ConfigureServices method (irrelevant code removed)
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/signin";
options.LogoutPath = "/signout";
// Stuff to store the auth cookie
})
services.AddMvc();
}
From the information you provided, it seems to me that, in order to use policies such as [Authorize(Policy = "ManagedApp")], besides the AddAuthentication(... definition, you will still need to either include:
AddAuthorization
or AddPolicySchemes, depending on your preferences.
I would recommend following this tutorial, which explains both implementations: https://code-maze.com/dotnet-multiple-authentication-schemes/
Please mind that, in order to use exactly [Authorize(Policy = "ManagedApp")], you will need to register a policy named as "ManagedApp".
Concerning the development of front-end and API controllers alongside each other, I wrote this answer some time ago, which may be of interest for your scenario.
In order to authorize all your endpoints by default, you can, for example:
Have different (base) controllers for the front-end and API, and employ a specified Authorize attribute in each of them, then inheriting from those (Microsoft ref);
Or use RequireAuthorization on the endpoints definition, as Andrew Lock suggests here.
Hope this helps!
I have a client application that accesses my WebAPI (1) using Integrated Windows Authentication and the Authorization code [Authentication flow].
Now I need to have a second WebAPI (2) access the original WebAPI (1) as well using the Client Credentials Authorization code Authentication flow.
My question is whether you can configure WebAPI (1) to permit EITHER flow and where to do it or whether I would have to build a whole separate API to handle the Authorization code flow? I believe it would be in the ConfigureServices method of Startup.cs but I'm not certain how adding a a Client Credentials filter policy would be handled by the controller's filters.
services.AddControllers(options => {
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireClaim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
//Add Authorization code filter here as well??
});
In the StartUp of WebAPI (1), RequireAuthenticationUser adds DenyAnonymousAuthorizationRequirement to the current instance which enforces that the current user is authenticated. This is, obviously, not going to work for the service-to-service authorization I am looking to implement so needed to be removed to permit both the Authentication flow AND ACL-based, Client Credentials Authentication flow.
Also, to get WebAPI (1) to permit the ACL-based authenticated token of WebAPI (2), I needed to add the following to the configuration of WebAPI (1):
{
"AzureAD"
{
// other properties
"AllowWebApiToBeAuthorizedByACL" : true,
// other properties
}
}
The resulting configuration policy ended up looking like:
var policy = new AuthorizationPolicyBuilder()
.RequireClaim("appid")
.Build();
Hoping someone stumbling on this finds this and it saves them time.
I want to add a policy to my web-odataAPi-application to only allow communication from one address.
In my understanding something like this in the Startup:
services.AddCors(options =>
{
options.AddPolicy(
"AllowSpecificOrigins",
co => co
.WithOrigins("https://localhost:3000/")
.AllowAnyHeader()
.WithMethods()
.AllowCredentials());
options.DefaultPolicyName = "AllowSpecificOrigins";
});
In the configure:
app.UseCors("AllowSpecificOrigins");
Above a Get-Route I do the followng:
[EnableCors("AllowSpecificOrigins")]
When i run this request from the https://localhost:5000 via Postman, Firefox and Chrome it works fine, even if in my understanding, it only should work form localhost:3000, right?
Browser security prevents a web page from making requests to a different domain than the one that served the web page. This restriction is called the same-origin policy. The same-origin policy prevents a malicious site from reading sensitive data from another site. Sometimes, you might want to allow other sites to make cross-origin requests to your app. For more information, see the Mozilla CORS article.
CORS is not a security feature, CORS relaxes security. An API is not safer by allowing CORS. For more information, see How CORS works.
I've below code in Startup.Configure function
app.UseMiddleware<LogMiddleware>(_loggingLevelSwitch)
//other code
.ConfigureExceptionHandler(Logger, Configuration)
.UseIdentityServer()
.UseStaticFiles()
.UseMiddleware<ContextSetupMiddleware>()
.ConfigureMvcAndLocalization()
//other code
UseIdentityServer internally calls UseAuthentication
I've setup two schemes cookie and Bearer.
My expectation is after authentication ContextSetupMiddleware should be called but it is happening otherwise.
For cookie, I can understand since authentication will be done later and once signin is done it works as expected.
For Bearer, I think it should be part of pipeline since we add a scheme to authenticationbuilder.
Update:
Made defaultscheme as "Bearer" it started working as I wanted.
However the website has AccountLogin screen (cookie based Auth) and some RestAPI's (JWT based Auth).
Is there a way cookie based authentication is only applicable on AccountLogin screen. I can't add [Authorize] since it is login scree.
It is actually by design. The default scheme will override if at any middleware it is required then use context.AuthenticateAsync().
In my case, in ContextSetupMiddleware just call context.AuthenticateAsyc() and authentication done.
I have a standard VS2013 MVC5 project with a Web Api 2 in it. The way the standard project is designed, the [Authorize] attributes simply return a 401 status code if the request is not authenticated, while a totally separate module sniffs for any 401 codes, halts them, and instead sends a 302 redirect to the login page specified in the Startup.Auth.cs file. That's ok for Mvc controllers, but really stinks for Web Api controllers because for example browsers will automatically redirect ajax requests to the login url, so you ultimately end up with a 200OK status even though the response text is just the html of the login page.
That makes it hard to write good javascript that can distinguish between a case where you just need to tell the user to log back in versus other kinds of errors. Ideally we should be able to tell based on the status code, but javascript never ever sees the 401 status. What is the best way to handle this?
My first thought was to write an authorization attribute but use status code 403 instead of 401:
public class ApiAuthorizationAttribute : System.Web.Http.AuthorizeAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if (actionContext.RequestContext.Principal.Identity.IsAuthenticated)
{
base.OnAuthorization(actionContext);
}
else
{
actionContext.Response = actionContext.ControllerContext.Request.CreateErrorResponse(HttpStatusCode.Forbidden, "Not signed in.");
}
}
}
Of course, specifications explicitly state that 403 is incorrect:
Authorization will not help and the request SHOULD NOT be repeated
My other thought is that maybe I should disable asp.net's 401 redirect module altogether and handle redirects in custom authorization attributes, because even for Mvc views it is lousy because it doesn't allow you to redirect to different login pages depending on where in the site the user is trying to visit.
Are there other, better approaches to handling this?
Here's what I was able to find with a bit more research. The 401 is intercepted by the OWIN middleware. But, OWIN does support branching configurations using the Map method. So in the Startup.cs file I have this:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map(new PathString("/api"), site => ConfigureAuth2(site));
ConfigureAuth(app);
}
}
where ConfigureAuth is the default configuration method that comes in the Startup.Auth.cs file, while ConfigureAuth2 is a duplicate of that method but with the LoginPath option left unspecified in the UseCookieAuthentication method, which looks like this:
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
}
});
According to the documentation, when the LoginPath is unspecified, 401 responses won't be intercepted for this branch.
So with this approach I'm branching all requests into two different configurations--all /api requests get configured not to redirect on 401 statuses, while everything else gets configured to redirect to the login page.
This SO question talked a bit about branching the configuration.
I'm still not sure if this is the best approach though.