Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

More control on ASP.Net MVC's Authorize; to keep AJAX requests AJAXy

I have some action methods behind an Authorize like:

[AcceptVerbs(HttpVerbs.Post), Authorize]
public ActionResult Create(int siteId, Comment comment) {

The problem I have is that I'm sending a request through AJAX to Comment/Create with

X-Requested-With=XMLHttpRequest

which helps identify the request as AJAX. When the user is not logged in and hits the Authorize wall it gets redirected to

/Account/LogOn?ReturnUrl=Comment%2fCreate

which breaks the AJAX workflow. I need to be redirected to

/Account/LogOn?X-Requested-With=XMLHttpRequest

Any ideas how that can be achieved? Any ways to gain more control over what happens when Authorization is requested?

like image 568
pupeno Avatar asked Jun 03 '09 06:06

pupeno


3 Answers

Thanks to Lewis comments I was able to reach this solution (which is far from perfect, posted with my own comments, if you have the fixes feel free to edit and remove this phrase), but it works:

public class AjaxAuthorizeAttribute : AuthorizeAttribute {
    override public void OnAuthorization(AuthorizationContext filterContext) {
        base.OnAuthorization(filterContext);
        // Only do something if we are about to give a HttpUnauthorizedResult and we are in AJAX mode.
        if (filterContext.Result is HttpUnauthorizedResult && filterContext.HttpContext.Request.IsAjaxRequest()) {
            // TODO: fix the URL building:
            // 1- Use some class to build URLs just in case LoginUrl actually has some query already.
            // 2- When leaving Result as a HttpUnauthorizedResult, ASP.Net actually does some nice automatic stuff, like adding a ReturnURL, when hardcodding the URL here, that is lost.
            String url = System.Web.Security.FormsAuthentication.LoginUrl + "?X-Requested-With=XMLHttpRequest";
            filterContext.Result = new RedirectResult(url);
        }
    }
}
like image 68
pupeno Avatar answered Nov 18 '22 05:11

pupeno


Recently I ran into exactly the same problem and used the code posted by J. Pablo Fernández with a modification to account for return URLs. Here it is:

public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    override public void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);
        // Only do something if we are about to give a HttpUnauthorizedResult and we are in AJAX mode. 
        if (filterContext.Result is HttpUnauthorizedResult && filterContext.HttpContext.Request.IsAjaxRequest())
        {
            // TODO: fix the URL building: 
            // 1- Use some class to build URLs just in case LoginUrl actually has some query already. 
            HttpRequestBase request = filterContext.HttpContext.Request;
            string returnUrl = request.Path;
            bool queryStringPresent = request.QueryString.Count > 0;
            if (queryStringPresent || request.Form.Count > 0)
                returnUrl += '?' + request.QueryString.ToString();
            if (queryStringPresent)
                returnUrl += '&';
            returnUrl += request.Form;
            String url = System.Web.Security.FormsAuthentication.LoginUrl +
                         "?X-Requested-With=XMLHttpRequest&ReturnUrl=" +
                         HttpUtility.UrlEncode(returnUrl);
            filterContext.Result = new RedirectResult(url);
        }
    }
}
like image 3
SlimShaggy Avatar answered Nov 18 '22 04:11

SlimShaggy


Instead of using the authorize attribute, I've been doing something like the following.

public ActionResult SomeCall(string someData)
{
    if (Request.IsAjaxRequest() == false)
    {
        // TODO: do the intended thing.
    }
    else
    {
        // This should only work with AJAX requests, so redirect
        // the user to an appropriate location.
        return RedirectToAction("Action", "Controller", new { id = ?? });
    }
}
like image 1
Jarrett Meyer Avatar answered Nov 18 '22 05:11

Jarrett Meyer