I have an MVC webapi site that uses OAuth/token authentication to authenticate requests. All the relevant controllers have the right attributes, and authentication is working ok.
The problem is that not all of the request can be authorised in the scope of an attribute - some authorisation checks have to be performed in code that is called by controller methods - what is the correct way to return a 401 unauthorised response in this case?
I have tried throw new HttpException(401, "Unauthorized access");
, but when I do this the response status code is 500 and I get also get a stack trace. Even in our logging DelegatingHandler we can see that the response is 500, not 401.
HttpResponseException
versus when to return an Unauthorized()
. Using the exception for an 'expected' error is a bit of an anti-pattern, so if there are cases you expect the call to make this mistake, returning Unauthorized()
is probably the right call. Save HttpResponseException
for the truly unexpected.
You should be throwing a HttpResponseException
from your API method, not HttpException
:
throw new HttpResponseException(HttpStatusCode.Unauthorized);
Or, if you want to supply a custom message:
var msg = new HttpResponseMessage(HttpStatusCode.Unauthorized) { ReasonPhrase = "Oops!!!" };
throw new HttpResponseException(msg);
Just return the following:
return Unauthorized();
return Content<string>(HttpStatusCode.Unauthorized, "Message");
to do this.
As an alternative to the other answers, you can also use this code if you want to return an IActionResult
within an ASP.NET controller.
ASP.NET
return Content(HttpStatusCode.Unauthorized, "My error message");
Update: ASP.NET Core
Above code does not work in ASP.NET Core, you can use one of these instead:
return StatusCode((int)System.Net.HttpStatusCode.Unauthorized, "My error message");
return StatusCode(Microsoft.AspNetCore.Http.StatusCodes.Status401Unauthorized, "My error message");
return StatusCode(401, "My error message");
Apparently the reason phrase is pretty optional (Can an HTTP response omit the Reason-Phrase?)
ControllerBase
class (used by ASP.NET Core WebAPI) no-longer has a Content
overload that accepts a HTTP status code.
HttpStatusCode.Unauthorized
), not 200. Content(...)
simply a shorthand for returning any given content with a given HTTP status code. If you want to send 200 you can use Ok(...)
You get a 500 response code because you're throwing an exception (the HttpException
) which indicates some kind of server error, this is the wrong approach.
Just set the response status code .e.g
Response.StatusCode = (int)HttpStatusCode.Unauthorized;
Response
property.
To add to an existing answer in ASP.NET Core >= 1.0 you can
return Unauthorized();
return Unauthorized(object value);
To pass info to the client you can do a call like this:
return Unauthorized(new { Ok = false, Code = Constants.INVALID_CREDENTIALS, ...});
On the client besides the 401 response you will have the passed data too. For example on most clients you can await response.json()
to get it.
In .Net Core You can use
return new ForbidResult();
instead of
return Unauthorized();
which has the advantage to redirecting to the default unauthorized page (Account/AccessDenied) rather than giving a straight 401
to change the default location modify your startup.cs
services.AddAuthentication(options =>...)
.AddOpenIdConnect(options =>...)
.AddCookie(options =>
{
options.AccessDeniedPath = "/path/unauthorized";
})
you can use follow code in asp.net core 2.0:
public IActionResult index()
{
return new ContentResult() { Content = "My error message", StatusCode = (int)HttpStatusCode.Unauthorized };
}
You also follow this code:
var response = new HttpResponseMessage(HttpStatusCode.NotFound)
{
Content = new StringContent("Users doesn't exist", System.Text.Encoding.UTF8, "text/plain"),
StatusCode = HttpStatusCode.NotFound
}
throw new HttpResponseException(response);
Success story sharing
HttpResponseException
is part of NuGet packageMicrosoft.AspNetCore.Mvc.WebApiCompatShim
which provides compatibility in ASP.NET Core MVC with ASP.NET Web API 2. That is, it allows you to go the non-Core way in Core projects. So for Core projects, this is not the "Correct way".