Reputation: 2992
I have an angularJS application which communicates with MVC Controller to get some data.
I have implemented a class RBACAuthorizeAttribute
which inherits from AuthorizeAttribute
to check if the user has permission to execute the particular action on the controller. Implementation of this class is shown below:
public class RBACAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
var webAction = filterContext.ActionDescriptor.ActionName;
var user = filterContext.HttpContext.User.Identity.Name;
if (!HasPermission(controllerName, webAction, user))
{
filterContext.Result = new RedirectToRouteResult(
new RouteValueDictionary {
{ "action", "Index" },
{ "controller", "Unauthorized" } });
}
}
private bool HasPermission(string controllerName, string webAction, string user)
{
//Check if the user has permission here.
//return true or false.
return false;
}
}
The actual controller on which I am checking the permission is given below:
public class MyDataController : Controller
{
[Route("IndividualDetails/{id}")]
[RBACAuthorize]
public JsonResult GetIndividualDetails(string id)
{
var data = GetDataFromSomeTableInDatabase(id);
return data;
}
UnauthorizedController
class definition is:
public class UnauthorizedController : Controller
{
public ActionResult Index()
{
return new HttpStatusCodeResult(HttpStatusCode.Unauthorized);
}
}
Below is the AngularJS function which invokes the controller:
function getDetails(id) {
return $resource(remoteServiceUrl + '/MyData/IndividualDetails/:id', { id: id })
.get().$promise;
};
function loadUserDetails(id) {
getDetails(id).then(
function (result) {
console.log(result);
},
function (reason) {
console.log(reason);
}
);
}
I am expecting the 401-Unauthorized response in the above loadUserDetails
function but I am getting quite a big array in result
which contains some html page. The issue seems to be in the implementation of RBACAuthorizeAttribute
class. So to get the 401-Unauthorized error on client side, can anyone point me to the right direction please.
Edit: I am using Asp.Net Identity system and the version of Asp.NET is 5.
Upvotes: 1
Views: 2704
Reputation: 18175
I've run into this very thing. When you return the 401 the ASP.NET infrastructure is redirecting you to the login page. If you examine the bytes that come back from your call I would be willing to bet it's either your login page or an error page (if you don't have a login page defined).
To solve this you need to modify the Provider
for your CookieAuthenticationOptions
and implement your own 'OnApplyRedirect' method to only do the redirect on a 401 if it's not an AJAX request.
Somewhere in your startup code you'll have something like:
var options = new CookieAuthenticationOptions
{
LoginPath = new PathString(loginPath),
CookieSecure = CookieSecureOption.Always,
CookieName = cookieName,
Provider = new CookieAuthenticationProvider()
{
OnApplyRedirect = context =>
{
if (!context.Request.IsAjaxRequest())
{ context.Response.Redirect(context.RedirectUri); }
}
}
};
app.UseCookieAuthentication(options);
The variables loginPath
and cookieName
should contain the path to your login page/action and the name of the cookie your site uses respectively (e.g. "/Auth" and ".MySiteAuthCookie").
I originally the answer in the following blog post and tweaked it a bit for our environment.
Upvotes: 0
Reputation: 1039418
I am expecting the 401-Unauthorized response in the above loadUserDetails function but I am getting quite a big array in result which contains some html page
I suppose that this HTML page is the login page. You could add a custom key to the HttpContext:
public class UnauthorizedController : Controller
{
public ActionResult Index()
{
this.HttpContext.Items["SuppressAuthenticationKey"] = true;
return new HttpStatusCodeResult(HttpStatusCode.Unauthorized);
}
}
and then in your Global.asax subscribe to the EndRequest
method and prevent the redirect if this key is present:
protected void Application_EndRequest(object sender, EventArgs e)
{
var context = (HttpApplication)sender;
var response = context.Response;
if (context.Context.Items.Contains("SuppressAuthenticationKey"))
{
response.TrySkipIisCustomErrors = true;
response.ClearContent();
response.StatusCode = 401;
response.RedirectLocation = null;
}
}
If you are using Forms Authentication and .NET 4.5 or later you could set the SuppressFormsAuthenticationRedirect
property to true in your Unauthorized action.
Also I see it kind of redundant to make an additional redirect to your Unauthorized
action. You could directly return 401 from your custom RBACAuthorizeAttribute
:
filterContext.Result = new HttpStatusCodeResult(HttpStatusCode.Unauthorized);
Upvotes: 2