I am very new to OWIN and trying to understand how OWIN mapping extension will work. I created an Empty ASP.Net project and referenced Owin, Microsoft.Owin, Microsoft.Owin.Host.SystemWeb packages.
I created a middleware class something like bello.
public class TempMiddleware : OwinMiddleware
{
public TempMiddleware(OwinMiddleware next)
: base(next)
{
}
public override Task Invoke(IOwinContext context)
{
return context.Response.WriteAsync("Response from Temp Middleware");
}
}
Here is my OWIN startup class.
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/temp", config => config.Use<TempMiddleware>());
}
}
I configured the portal project something like this.
When I run the project from VS2017, it returns HTTP Error 403.14 - Forbidden page.
Actually my expectation is, it should print "Response from Temp Middleware" message on the browser.
Any issues in my code ?
Thanks
Map() is used for branching the pipeline. In the delegate, the second parameter of the Map() method, you rather need to Run a middleware.
This is how your configuration code should be:
public void Configuration(IAppBuilder app)
{
app.Map("/temp", config => config.Run(async context =>
{
await context.Response.WriteAsync("Response from Temp Middleware");
}));
}
Related
background
I hope to add additional middleware by registering the plugin without changing the original project code, but how to get the IApplicationBuilder required to register the middleware in the plugin is the biggest problem I currently face.
According to the Hosting Startup Document, the plugin can be registered by inheriting IHostingStartup and loaded automatically when the project starts, E.g:
// plugin
public class MyStartup: IHostingStartup
{
// Implement the IHostingStartup interface
public void Configure(IWebHostBuilder builder)
{
// TODO: I want to get an IApplicationBuilder object to register middleware
}
}
question
How to get IApplicationBuilder object by IWebHostBuilder?
In the official docs, Extend Startup with startup filters explains that IStartupFilter might be useful here:
Use IStartupFilter to configure middleware at the beginning or end of an app's Configure middleware pipeline without an explicit call to Use{Middleware}.
Here's a sample implementation:
public class MyStartupFilter : IStartupFilter
{
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
{
return app =>
{
app.UseMiddleware<MyMiddleware>();
next(applicationBuilder);
};
}
}
In this example, we're adding MyMiddleware to the beginning of the pipeline, which means that it runs before the rest of the pipeline. To run MyMiddleware at the end of the pipeline, switch the order of app.UseMiddleware and next.
You must also register this implementation with the DI container, like this:
// plugin
public class MyStartup : IHostingStartup
{
// Implement the IHostingStartup interface
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
services.AddTransient<IStartupFilter, MyStartupFilter>();
});
}
}
Although this works, it's not as flexible as you might need it to be. For example, it doesn't allow you to inject middleware between middleware added by the app.
I'm trying to get Policy based Authorization working in .net core 2.1 based on this article: https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-2.1
However I cannot seem to get it to fire.
In the below example, I've commented out the context.suceeed line, so I would have thought my api call to my usercontroller would fail.
However my API call is being accepted.
What am I doing wrong?
This is my startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IAuthorizationHandler, VerifyAuthCookieHandler>();
services.AddAuthorization(options =>
{
options.AddPolicy("VerifyAuthCookie", policy =>
policy.Requirements.Add(new VerifyAuthCookieRequirement()));
});
services.AddMvcCore().AddJsonFormatters();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc();
}
}
Here is my handler
public class VerifyAuthCookieHandler : AuthorizationHandler<VerifyAuthCookieRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
VerifyAuthCookieRequirement requirement)
{
//context.Succeed(requirement);
return Task.CompletedTask;
}
}
And here is my requirement:
public class VerifyAuthCookieRequirement : IAuthorizationRequirement
{
public VerifyAuthCookieRequirement()
{
}
}
And finally, my controller:
[Route("api/[controller]")]
[Authorize(Policy = "VerifyAuthCookie")]
public class UserController : Controller
{
}
If I add code in HandleRequirementAsync and set a breakpoint then it's not being hit when I debug, so my Handler doesn't appear to be called at all.
You should call app.UseAuthentication(); before the app.UseMvc(); in the Configure method of the Startup class. This will add the ASP.NET Core authentication middleware to the request pipeline.
Since you are using services.AddMvcCore() we'll need to configure the authorization services manually, something services.AddMvc() does for you automatically. We should add .AddAuthorization() to the IMvcCoreBuilder.
This will add the default Authentication- and Authorization services and a PolicyEvaluator.
If you're interested in the exact services that will be registered in your DI container you should follow this link.
I had similar issue, I fix it by :
services.AddMvcCore().AddAuthorization().AddJsonFormatters();
Long story short, Is it possible to place an environment based authorization attribute on my API so that the authorization restriction would be turned off in development and turned back on in Production?
I have a separate Angular 2 project that I wish to call a .NET Core API with. We created a separate project so we could open the Angular 2 project in vscode and debug the typescript. When we are finished, we will build the project and place it inside the .NET Core project for security reasons.
Our problem is that during the debugging stages, we are unable to connect to the API because they are two separate projects and our Angular 2 project does not have Active Directory. The .NET Core project currently has Authentication Attributes and wont allow access (401) to the API. It would be nice if we could turn that off during development and back on during production.
I'm also open to any other suggestions on how we can best solve this problem.
[Authorize: (Only in Production)] <-- // something like this???
[Route("api/[controller]")]
public class TestController : Controller
{
...
ASP.NET Core authorization is based on policies. As you may have seen, the AuthorizeAttribute can take a policy name so it knows which criteria need to be satisfied for the request to be authorized. I suggest that you have a read of the great documentation on that subject.
Back to your problem, it looks like you don't use a specific policy, so it uses the default one, which requires the user to be authenticated by default.
You can change that behaviour in Startup.cs. If you're in development mode, you can redefine the default policy so that it doesn't have any requirements:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(x =>
{
// _env is of type IHostingEnvironment, which you can inject in
// the ctor of Startup
if (_env.IsDevelopment())
{
x.DefaultPolicy = new AuthorizationPolicyBuilder().Build();
}
});
}
Update
im1dermike mentioned in a comment that an AuthorizationPolicy needs at least one requirement, as we can see here. That code wasn't introduced recently, so it means the solution above was broken the whole time.
To work around this, we can still leverage the RequireAssertion method of AuthorizationPolicyBuilder and add a dummy requirement. This would look like:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(x =>
{
// _env is of type IHostingEnvironment, which you can inject in
// the ctor of Startup
if (_env.IsDevelopment())
{
x.DefaultPolicy = new AuthorizationPolicyBuilder()
.RequireAssertion(_ => true)
.Build();
}
});
}
This ensures we have at least one requirement in the authorization policy, and we know that it will always pass.
I end up with this, might help :
public class OnlyDebugModeAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext context)
{
base.OnActionExecuted(context);
}
public override void OnActionExecuting(ActionExecutingContext context)
{
#if DEBUG
//Ok
#else
context.Result = new ForbidResult();
return;
#endif
}
}
and then apply it on controler
[OnlyDebugMode]
[Route("api/[controller]")]
[ApiController]
public class DebugController : ControllerBase
{
Here's my solution:
New attribute for your controllers:
[AzureADAuthorize]
AzureADAuthorize.cs:
public class AzureADAuthorize : AuthorizeAttribute
{
public AzureADAuthorize() : base(AzureADPolicies.Name)
{
}
}
AzureADPolicies.cs:
public static class AzureADPolicies
{
public static string Name => "AzureADAuthorizationRequired";
public static void Build(AuthorizationPolicyBuilder builder)
{
if (StaticRepo.Configuration.GetValue<bool>("EnableAuthorization") == true)
{
var section = StaticRepo.Configuration.GetSection($"AzureAd:AuthorizedAdGroups");
var groups = section.Get<string[]>();
builder.RequireClaim("groups", groups);
}
else if (StaticRepo.Configuration.GetValue<bool>("EnableAuthentication") == true)
{
builder.RequireAuthenticatedUser();
}else
{
builder
.RequireAssertion(_ => true)
.Build();
}
}
}
Startup.cs:
//Authentication & Authorization
#region AUTHENTICATION / AUTHORICATION
StaticRepo.Configuration = Configuration;
services.AddAuthorization(options =>
{
options.AddPolicy(
AzureADPolicies.Name, AzureADPolicies.Build);
});
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
#endregion
Appsettings.json:
"EnableAuditLogging": false,
"EnableAuthentication": true,
"EnableAuthorization": false,
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "https://MyDomain.onmicrosoft.com/",
"TenantId": "b6909603-e5a8-497d-8fdb-7f10240fdd10",
"ClientId": "6d09a1bf-4678-4aee-b67c-2d6df68d5324",
"CallbackPath": "/signin-oidc",
//Your Azure AD Security Group Object IDs that users needs to be member of to gain access
"AuthorizedAdGroups": [
"568bd325-283f-4909-9fcc-a493d19f98e8",
"eee6d366-0f4d-4fca-9965-b2bc0770506d"
]
}
(These are random guids)
Now you can conditional control if you want to have anonymous access, azure ad authentication, authentication + group authorization. There are still some stuff you need to setup in your azure ad app manifest file to get it to work, but I think it's outside the scope here.
Following the docs here I tried to implement a policy-based auth scheme. http://docs.asp.net/en/latest/security/authorization/policies.html#security-authorization-handler-example
I ran into the issue that my Handle method was not being called on my custom AuthorizationHandler. (It does not throw here). It also does inject the dependency currently in the constructor.
Here it the AuthorizationHandler Code.
using WebAPIApplication.Services;
using Microsoft.AspNet.Authorization;
namespace WebAPIApplication.Auth
{
public class TokenAuthHandler : AuthorizationHandler<TokenRequirement>, IAuthorizationRequirement
{
private IAuthService _authService;
public TokenAuthHandler(IAuthService authService)
{
_authService = authService;
}
protected override void Handle(AuthorizationContext context, TokenRequirement requirement)
{
throw new Exception("Handle Reached");
}
}
public class TokenRequirement : IAuthorizationRequirement
{
public TokenRequirement()
{
}
}
}
In Start Up I have
// Authorization
services.AddSingleton<IAuthorizationHandler, TokenAuthHandler>()
.AddAuthorization(options =>
{
options.AddPolicy("ValidToken",
policy => policy.Requirements.Add(new TokenRequirement()));
});
The controller method is
// GET: api/values
[HttpGet, Authorize(Policy="ValidToken")]
public string Get()
{
return "test";
}
Hitting this endpoint returns nothing and there is a warning in the console of
warn: Microsoft.AspNet.Mvc.Controllers.ControllerActionInvoker[0]
Authorization failed for the request at filter 'Microsoft.AspNet.Mvc.Filters.AuthorizeFilter'.
I am able to hit other endpoints that don't have the attribute successfully.
SOS,
Jack
I'm putting this here for reference because I spent way too long figuring this out...
I had implemented a custom requirement and handler (empty for testing's sake):
using Microsoft.AspNetCore.Authorization;
using System.Threading.Tasks;
public class TestHandler : AuthorizationHandler<TestRequirement>, IAuthorizationRequirement
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, TestRequirement requirement)
{
context.Succeed(requirement);
return Task.CompletedTask;
}
}
public class TestRequirement : IAuthorizationRequirement
{
}
Registered it in my Startup.cs ConfigureServices() section:
services.AddAuthorization(options =>
{
options.AddPolicy("Test", policy => policy.Requirements.Add(new TestRequirement()));
// Other policies here
}
Added it to my controller method:
[HttpGet]
[Authorize(Policy = "Test")]
public IActionResult Index()
{
Return View();
}
But was getting a 403 error (not 401) with every request to the controller method!
Turns out, I was not registering TestHandler with the ConfigureServices() (Dependency Injection) section of Startup.cs.
services.AddSingleton<IAuthorizationHandler, TestHandler>();
Hope this saves someone from banging their head on their desk. :|
The answer to this question is alluded to in a comment to adem caglin, so props to him.
The issue is that the AuthorizeFilter is rejecting the request before the AuthorizationHandler is being called. This is because for every use of the Authorize tag MVC adds AuthorizeFilter ahead of the AuthorizationHandler in the pipeline. This AuthorizeFilter checks to see if any of the current users identities are authorized. In my case there were no authorized identities associated with any user so this would always fail.
A solution (which IMO is somewhat hackish) is to insert a peice of middleware that will get executed before any MVC code. This middleware will add a generic authenticated identity to a User (if the user does not already have one).
Consequently the AuthorizeFilter check will pass and the Handle method on the AuthenticationHandler method will be executed and our problem will be solved. The middleware code (which needs to be added to Configure before app.UseMvc(); is called) is as follows
app.Use(async (context, next) =>
{
if (!context.User.Identities.Any(i => i.IsAuthenticated))
{
context.User = new ClaimsPrincipal(new GenericIdentity("Unknown"));
}
await next.Invoke();
});
An alternative way to override the AuthorizeFilter is outline here (Override global authorize filter in ASP.NET Core MVC 1.0)
Citing the response from here (Asp.Net Core policy based authorization ends with 401 Unauthorized)
Take a look at Asp.net Core Authorize Redirection Not Happening i think adding options.AutomaticChallenge = true; solves your problem.
I've created a basic webAPI project (blank web project with webAPI checked) and added the owin nuget packages to the project.
Microsoft.AspNet.WebApi.Owin
Microsoft.Owin.Host.SystemWeb
Owin
I've then created a Logging class, and hooked it up via startup
using AppFunc = System.Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>;
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
Debug.WriteLine("Startup Called");
var config = new HttpConfiguration();
WebApiConfig.Register(config);
appBuilder.UseWebApi(config);
appBuilder.Use(typeof(LoggingMiddleware));
}
}
public class LoggingMiddleware
{
private AppFunc Next { get; set; }
public LoggingMiddleware(AppFunc next)
{
Next = next;
}
public async Task Invoke(IDictionary<string, object> environment)
{
Debug.WriteLine("Begin Request");
await Next.Invoke(environment);
Debug.WriteLine("End Request");
}
}
When I run the project, and the default page opens, I see the Begin/End requests called (twice, as it happens, not sure why that is).
However, if I try to call an /api route (such as `/api/ping/'), the request completes successfully, but I do not see the Begin/End request states in the log.
What am I missing with this?
Owin executes the middleware items in the order that they are registered, ending at the call to the controller (appBuilder.UseWebApi(config)) which does not appear to call next.Invoke(). Given that the code in the question has the Logging Middleware class registered after the UseWebApi call, this causes it to never be called for API requests.
Changing the code to:
public class Startup
{
public void Configuration(IAppBuilder appBuilder)
{
//.....
//This must be registered first
appBuilder.Use(typeof(LoggingMiddleware));
//Register this last
appBuilder.UseWebApi(config);
}
}
resolves the issue.