I would like to construct a custom authorization attribute that does not invoke Identity or OWIN. Essentially, the only thing that it should have access to is a request context and the ability to either tell the MVC framework to process to continue to process the request or deny it.
Question Is there a simple way of achieving this in ASP.NET Core 2?
Some ideas
My understanding of ASP.NET Core is that it provides a way to customize the request pipeline using different middleware. I have seen that there are specific ones that are used for authentication, but they all seem to be very specific to Identity.
Is it better to to use a different type of filter?
A little bit late answer, but still.. the "old" way of overriding attributes comes back with the .Net Core 2.0, where in addition to the base class, you have to implement the IAuthorizationFilter interface:
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class CustomAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
private readonly string _someFilterParameter;
public CustomAuthorizeAttribute(string someFilterParameter)
{
_someFilterParameter = someFilterParameter;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
// you can play with the context here
}
}
More discussion here
Related
I have a SignalR app. hosted in a Windows service (used OWIN & Katana as self hosting) and it's listening on mydomain.com:8080
On the same server, I also have an MVC application which is basically a website that connects to my SignalR hub which I mentioned above.
I want to restrict access to my SignalR app only to my MVC application. I've searched the internet but didn't come along an example of this.
Is it possible to achieve this? How can I get the information about if the connection is coming from my MVC app or from another app? Do I need to implement an authorization for my own MVC application to be able to connect to my SignalR application?
Right now, everyone on the internet can access to mydomain.com:8080/signalr endpoint which basically means a competitor can code a client that connects to my SignalR hub and use it. What are the options to prevent this scenario?
p.s: Please ask for more information -if you need- instead of just marking the post as "non constructive" because I don't know how this question can be asked more "constructive"
I believe I have a working example, it's quick and dirty, but it should do the job, and you should be able to expand it so it'll fit your needs better:
I created a class that inherits from Microsoft.AspNet.SignalR.AuthorizeAttribute and overrode the AuthorizeHubConnection method:
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
public class CustomAuthorize : AuthorizeAttribute
{
public override bool AuthorizeHubConnection(Microsoft.AspNet.SignalR.Hubs.HubDescriptor hubDescriptor, IRequest request)
{
string referer = request.Headers["Referer"];
string authority = new Uri(referer).Authority;
if (authority == "mydomain.com:8080")
{
return true;
}
return false;
}
}
all it does is check the Referer header's host/authority against a hard coded one, and returns true if they match.
You can then use it like this:
[CustomAuthorize]
public class ChatHub : Hub
{
//Hub code here...
}
If CustomAuthorize returns false, the request will stop there. The hub's OnConnected() will not be triggered.
Just Use cors option instead of writing code.in cors allow your domain only
I'm creating an ASP.NET Web API 2.1 site and as I want to inject dependencies directly into the controllers, I've created my own implementation of IDependencyResolver so that StructureMap will handle that for me.
public class StructureMapDependencyResolver : IDependencyResolver
{
public IDependencyScope BeginScope()
{
return this;
}
public object GetService(Type serviceType)
{
return ObjectFactory.GetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return ObjectFactory.GetAllInstances(serviceType).Cast<object>();
}
public void Dispose()
{
}
}
I've then told Web API to use this class by adding this line to the Application_Start method in Global.asax
GlobalConfiguration.Configuration.DependencyResolver = new StructureMapDependencyResolver();
That compiled but when I tried to access any of the API methods in a browser I got an error like this
No Default Instance defined for PluginFamily System.Web.Http.Hosting.IHostBufferPolicySelector, System.Web.Http
That one was relatively easy to solve as I added a line to my StructureMap configuration
this.For<IHostBufferPolicySelector>().Use<WebHostBufferPolicySelector>();
However then I got other similar errors for other System.Web.Http classes and while I could resolve some of them I am stuck on how to deal with 3 of them, namely ITraceManager, IExceptionHandler and IContentNegotiator.
The issue is that TraceManager which seems to be the default implementation of ITraceManager is an internal class and so I can't reference it in my StructureMap configuration.
So am I going about this completely the wrong way or is there some other way to inject these internal classes?
I'd like to give you a suggestion and explanation why not to go this way, and how to do it differently (I'd even say better and properly).
The full and complete explanation of the inappropriate IDependencyResolver design could be found here: Dependency Injection and Lifetime Management with ASP.NET Web API by Mark Seemann
Let me cite these essential parts:
The problem with IDependencyResolver
The main problem with IDependencyResolver is that it's essentially a Service Locator. There are many problems with the Service Locator anti-pattern, but most of them I've already described elsewhere on this blog (and in my book). One disadvantage of Service Locator that I haven't yet written so much about is that within each call to GetService there's no context at all. This is a general problem with the Service Locator anti-pattern, not just with IDependencyResolver.
And also:
...dependency graph need to know something about the context. What was the request URL? What was the base address (host name etc.) requested? How can you share dependency instances within a single request? To answer such questions, you must know about the context, and IDependencyResolver doesn't provide this information.
In short, IDependencyResolver isn't the right hook to compose dependency graphs. **Fortunately, the ASP.NET Web API has a better extensibility point for this purpose. **
ServiceActivator
So, the answer in this scenario would be the ServiceActivator. Please take a look at this answer:
WebAPI + APIController with structureMap
An example of the ServiceActivator:
public class ServiceActivator : IHttpControllerActivator
{
public ServiceActivator(HttpConfiguration configuration) {}
public IHttpController Create(HttpRequestMessage request
, HttpControllerDescriptor controllerDescriptor, Type controllerType)
{
var controller = ObjectFactory.GetInstance(controllerType) as IHttpController;
return controller;
}
}
All we can do with StructureMap, is in place. The key features of the Web API framework are still in place... we do not have to hack them. And we are also rather using DI/IoC then Service locator
Just try using UnityHierarchicalDependencyResolver instead of the other one. It worked for me. This is for future reference if somebody would like to use Unity
I have several .asmx web services that I want to upgrade to WebAPI. These web services look somewhat like this:
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.Web.Script.Services.ScriptService]
public class TheWebService : System.Web.Services.WebService {
[WebMethod(EnableSession = true)]
public string SomeMethod(string SomeInput)
{
MySessionModel TheSession = HttpContext.Current.Session["UserSession"] as MySessionModel;
return SomeClass.SomeMethod(SomeInput, TheSession);
}
}
Basically, I have a single-page application. I'm using Forms Auth to login and redirect users to their "profile" and then, from this page, the app uses web services to communicate with the server. The web services only return raw strings so I don't need serialization at teh web service level. For the moment, the app is hosted in IIS and soon I'll be deploying it into azure.
I've looked around on the web, and several posts suggest that using session state and HttpContext is bad design. Why is using HttpCurrent and session state a bad idea in this case?
There is nothing innately wrong with using ASP.NET Session, as long as you don't use it as a catch-all basket for any old data. Shopping carts, for example, do not belong in Session: they belong in a Shopping Cart persistence component.
Also, and I suspect the reason for the Azure tag on this question, if you are running in a load-balanced environment such as an Azure Cloud Service, you need to use an external session provider such as a SQL Database or a shared cache. Using the in-process session provider (the default) will cause very odd, often unreproducable bugs as users are switched between different servers with different copies of the session.
As for HttpContext.Current, well, for Web API, things like Inversion of Control, Dependency Injection, and simple testability are important. A clean, testable Web API version of that service might look something like this:
public class TheWebService : ApiController {
private readonly IUserSession _userSession;
public TheWebService(IUserSession userSession)
{
_userSession = userSession;
}
public string SomeMethod(string SomeInput)
{
MySessionModel TheSession = _userSession.Get();
return SomeClass.SomeMethod(SomeInput, TheSession);
}
}
public interface IUserSession
{
MySessionModel Get();
}
You could still use HttpContext.Current.Session["UserSession"] in a class like this:
public class CurrentContextUserSession : IUserSession
{
public MySessionModel Get()
{
return HttpContext.Current.Session["UserSession"] as MySessionModel;
}
}
You would then use an IoC container such as Unity or Ninject to set CurrentContextUserSession as the implementation of IUserSession for Web API to use when constructing instances of TheWebService. But when you were writing your tests, you could use a mock or stub implementation of IUserSession that had no dependency on HttpContext.Current.
In your specific example, you are using the Session only inside the WebMethod, which is fine as it is already coupled to ASP.NET but many people tend to use this at other layers of their application which is a really bad design.
Common problems of using HttpContext.Current in those layers are:
the code cannot be easily unit tested in isolation
the code is tightly coupled to an ASP.NET context
This being said, having stateful services that depend on the session is bad design. In the example you have shown, that's an ASMX WebService which is depending on the ASP.NET Session state meaning that the client should be passing cookies around in order to invoke this service. Also stateful services are harder to scale.
Asp.net MVC 5 seems to have left behind using the AuthorizeAttribute class where you could create a custom authorize attribute by implementing the AuthorizeAttribute class, override its methods and hiding the SiteRole property incase you wanted to bake in your own roles. All the examples I have seen either suggest using OWIN or the identity framework. Are these the only two ways to do authentication and authorization in the new ASP.Net framework?. Will I miss out on anything if I do it the old fashioned way? I dont want to have the framework create all the user and role tables for me. What if I want to add an existing user and role table to a new application?
I also really don't see a need for social integration in every application as yet and don't think I will need it immediately as well. Is there any article that explains starting off with a bare minimum by using a custom authorize attribute and then goes on to add the new authentication features. I want something that basically explains all the clutter in a newly created project with No Authentication or Individual User Authentication selected.
You can still customize the AuthorizeAttribute in MVC 5 using ASP.NET Identity. There is an example of doing this in the SimpleSecurity Project. Here is a customized AuthorizedAttribute you can use for controllers and here is customized AuthorizeAttribute you can use for Web API's. The concept behind these custom AuthorizeAttributes is to decouple your security model from your application model which is discussed here. The one for the Web API's also supports basic authentication.
The security pipeline has changed with the introduction of OWIN and I did run into some issues with the behavior of AuthorizeAttribute for Web API's, which is discussed here.
You will also find examples in the SimpleSecurity project on porting of the old membership provider called SimpleMembership to MVC 5. Some of the issues with the upgrade process are discussed here. I did get it to work though so you could go with the old membership provider implementation. But my recommendation would be to go with the ASP.NET Identity as this is the way going forward that Microsoft will be supporting, it is a more flexible architecture, and it eliminates many of the issues found in the old membership provider implementations.
Ben Foster has a two-part series that takes you through steps on implementing cookie-based authentication with ASP.NET Identity from the ground up, starting off with a new Web app with no authentication selected. Follow along "ASP.NET Identity Stripped Bare" Part 1 and Part 2.
use the following Authorize attribute to handle unauthorized access when the user is already authenticated.
public class LoggedOrAuthorizedAttribute : AuthorizeAttribute
{
public LoggedOrAuthorizedAttribute()
{
View = "error";
Master = String.Empty;
}
public String View { get; set; }
public String Master { get; set; }
public override void OnAuthorization(AuthorizationContext filterContext)
{
base.OnAuthorization(filterContext);
CheckIfUserIsAuthenticated(filterContext);
}
private void CheckIfUserIsAuthenticated(AuthorizationContext filterContext)
{
// If Result is null, we’re OK: the user is authenticated and authorized.
if (filterContext.Result == null)
return;
// If here, you’re getting an HTTP 401 status code. In particular,
// filterContext.Result is of HttpUnauthorizedResult type. Check Ajax here.
if (filterContext.HttpContext.User.Identity.IsAuthenticated)
{
if (String.IsNullOrEmpty(View))
return;
var result = new ViewResult {ViewName = View, MasterName = Master};
filterContext.Result = result;
}
}
}
Let's say I have an ASP.NET site (MVC in this case) that uses Forms authentication and a typical membership system. The site allows both authenticated and anonymous users.
When I release the site as a private beta I want to add another layer of security on top of the application, like superuser's simple password system, for example. Once a user has passed this layer of security, I still want my forms authentication/membership system in place so beta testers can view the site as authenticated or anonymous users.
What's the most unobtrusive way to achieve this? I'm looking for the easiest solution that will require the least amount of new or modified code. E.g. I don't want to modify every controller to check for a special cookie. There must be a better way...
There's a very similar question here, but it seems the site in question (once public) will only serve anonymous requests, so it doesn't necessarily compare to my situation. This answer suggests ServerFault used some cookie system, but there are no further details about how it might have been implemented.
Implement security at server level, in IIS and add the accounts/passwords in Active Directory of Windows running the IIS server.
You won't need to change any of the code.
Well, I know you don't want to modify your current controllers but here's what I did for a similar behaviour.
I've created a custom ActionFilterAttribute that I've given to every controller that requires to have that specific access check. You can have something like this :
public class CheckBetaAccess : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
if (!canAccess) {
filterContext.Controller.ViewData["someViewData"] = "some text";
filterContext.Result = new ViewResult {
ViewName = "the-view-anonymous-users-should-see",
ViewData = filterContext.Controller.ViewData
};
filterContext.Result.ExecuteResult(filterContext);
}
}
}
Then I decorated my controllers :
[CheckBetaAccess]
public class SomeController : Controller {
//....
}