I am inheriting from System.Web.Http.AuthorizeAttribute to create a custom authorization/authentication routine to meet some unusual requirements for a web
To add to the already accepted answer: Checking current sourcecode (aspnetwebstack.codeplex.com) for System.Web.Http.AuthorizeAttribute
, it looks like the documentation is out of date. Base OnAuthorization()
just calls/checks private static SkipAuthorization()
(which just checks if AllowAnonymousAttribute
is used in context to bypass the rest of the authentication check). Then, if not skipped, OnAuthorization()
calls public IsAuthorized()
and if that call fails, it then calls protected virtual HandleUnauthorizedRequest()
. And that's all it does...
public override void OnAuthorization(HttpActionContext actionContext)
{
if (actionContext == null)
{
throw Error.ArgumentNull("actionContext");
}
if (SkipAuthorization(actionContext))
{
return;
}
if (!IsAuthorized(actionContext))
{
HandleUnauthorizedRequest(actionContext);
}
}
Looking inside IsAuthorized()
, that's where Principle is checked against roles and users. So, overriding IsAuthorized()
with what you have above instead of OnAuthorization()
would be the way to go. Then again, you'd still have to probably override either OnAuthorization()
or HandleUnauthorizedRequest()
anyway to decide when to return a 401 vs a 403 response.
The best solution for my scenario appears to be bypass the base OnAuthorization completely. Since I have to authenticate each time cookies and caching the principle are not of much use. So here is the solution I came up with:
public override void OnAuthorization(HttpActionContext actionContext)
{
string username;
string password;
if (GetUserNameAndPassword(actionContext, out username, out password))
{
if (Membership.ValidateUser(username, password))
{
if (!isUserAuthorized(username))
actionContext.Response =
new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
}
else
{
actionContext.Response =
new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
}
}
else
{
actionContext.Response =
new HttpResponseMessage(System.Net.HttpStatusCode.BadRequest);
}
}
I developed my own method for validating the roles called isUserAuthorized and I am not using the base OnAuthorization any more since it checks the current Principle to see if it isAuthenticated. IsAuthenticated only allows gets so I am not sure how else to set it, and I do not seem to need the current Principle. Tested this out and it works fine.
Still interested if anyone has a better solution or can see any issues with this this one.
To add to the absolutely correct answer by Kevin, I'd like to say that I may slightly modify it to leverage the existing .NET framework path for the response object to ensure downstream code in the framework (or other consumers) is not adversely affected by some weird idiosyncrasy that can't be predicted.
Specifically this means using this code:
actionContext.Response = actionContext.ControllerContext.Request.CreateErrorResponse(HttpStatusCode.Unauthorized, REQUEST_NOT_AUTHORIZED);
rather than:
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
Where REQUEST_NOT_AUTHORIZED
is:
private const string REQUEST_NOT_AUTHORIZED = "Authorization has been denied for this request.";
I pulled that string
from the SRResources.RequestNotAuthorized
definition in the .NET framework.
Great answer Kevin! I implemented mine the very same way because executing OnAuthorization
in the base class made no sense because I was verifying an HTTP Header that was custom to our application and didn't actually want to check the Principal at all because there wasn't one.