Reputation: 11412
Using ASP.NET MVC 5, I would like to return appropriate HTTP status code for different scenarios (401 for user is not authenticated, 403 when user has no right for some resource, etc.), then handle them in jQuery.
But the problem is, when I try to return 401, it always returns "200: OK". MVC 5 RC1 was giving "302: Found" instead of 401, so I could use a workaround (HttpStatusCodeResult(401) returns "302 Found"). But now I moved from MVC 5 RC1 to MVC 5 and this behaviour changed. Now it is always "200: OK". So my workaround is useless, of course I can't replace 200 with anything else.
public ActionResult My()
{
if (User.Identity.IsAuthenticated == false)
{
return new HttpStatusCodeResult(401, "User is not authenticated.");
// Returns "200: OK"
}
// ... other code ...
}
How to solve this?
Upvotes: 9
Views: 6704
Reputation: 4021
In Statup.cs I had to set AutomaticChallenge
to false
. Once I did that it stopped doing the URL redirect (which resulted in 200 status) and gave me the 401 status I desired.
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.Cookies.ApplicationCookie.AutomaticChallenge = false; //<@@@@@@@
//...
})
.AddEntityFrameworkStores<ApplicationDbContext, int>()
.AddDefaultTokenProviders();
}
If you have set some cookie middleware:
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IAntiforgery antiforgery)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationScheme = "__auth__",
LoginPath = "/account/login",
AccessDeniedPath = "/account/forbidden",
AutomaticAuthenticate = true,
AutomaticChallenge = false //<@@@@@@@
});
}
Upvotes: 1
Reputation: 2004
For Identity middleware, redirect can be disabled by removing in LoginPath option in Startup.Auth.cs:
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
...
LoginPath = new PathString("/Account/Login"), // Remove this line
});
Upvotes: 0
Reputation: 7495
The MVC 5+ Pipeline modifies 401 response codes.
Option 1 With .net 4.5
you can set HttpContext.Response.SuppressFormsAuthenticationRedirect
to true.
e.g. in your custom AuthoriseAttribute.cs
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new JsonResult
{
Data = "_Logon_",
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
}
Option 2. If not using .net 4.5
public class SuppressFormsAuthenticationRedirectModule : IHttpModule
{
private static readonly object SuppressAuthenticationKey = new object();
public static void Register()
{
DynamicModuleUtility.RegisterModule(
typeof(SuppressFormsAuthenticationRedirectModule));
}
public static void SuppressAuthenticationRedirect(HttpContext context)
{
context.Items[SuppressAuthenticationKey] = true;
}
public static void SuppressAuthenticationRedirect(HttpContextBase context)
{
context.Items[SuppressAuthenticationKey] = true;
}
public void Init(HttpApplication context)
{
context.PostReleaseRequestState += OnPostReleaseRequestState;
context.EndRequest += OnEndRequest;
}
public void Dispose()
{
}
private void OnPostReleaseRequestState(object source, EventArgs args)
{
var context = (HttpApplication)source;
var response = context.Response;
var request = context.Request;
if (response.StatusCode == 401 && request.Headers["X-Requested-With"] == "XMLHttpRequest")
{
SuppressAuthenticationRedirect(context.Context);
}
}
private void OnEndRequest(object source, EventArgs args)
{
var context = (HttpApplication)source;
var response = context.Response;
if (context.Context.Items.Contains(SuppressAuthenticationKey))
{
response.TrySkipIisCustomErrors = true;
response.ClearContent();
response.StatusCode = 401;
response.RedirectLocation = null;
}
}
}
and in web.config
<modules>
<add name="SuppressFormsAuthenticationRedirectModule" type="SuppressFormsAuthenticationRedirectModule"/>
</modules>
Upvotes: 5
Reputation: 644
Solution to the issue can be found in http://kevin-junghans.blogspot.in/2013/12/returning-401-http-status-code-on.html
You need to modify your Startup
class like this:
public partial class Startup
{
private static bool IsAjaxRequest(IOwinRequest request)
{
IReadableStringCollection query = request.Query;
if ((query != null) && (query["X-Requested-With"] == "XMLHttpRequest"))
{
return true;
}
IHeaderDictionary headers = request.Headers;
return ((headers != null) && (headers["X-Requested-With"] == "XMLHttpRequest"));
}
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed in user
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
OnApplyRedirect = ctx =>
{
if (!IsAjaxRequest(ctx.Request))
{
ctx.Response.Redirect(ctx.RedirectUri);
}
}
}
});
// Use a cookie to temporarily store information about a user logging in with a third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
}
}
Upvotes: 3
Reputation: 622
I use simuler code that return 404. Your code could be:
public ActionResult My()
{
if (User.Identity.IsAuthenticated == false)
{
return new HttpUnauthorizedResult();
}
// ... other code ...
}
Upvotes: 1