In our asp.net mvc/web api project, we want to customize the authorization using AuthorizeAttribute
. We have noticed that there are two different AuthorizeAttribute
, one in System.Web.MVC
namespace for MVC and the other in System.Net.Http
namespace for web api.
It works in MVC, our code like this:
public class MyPrincipal : IPrincipal
{
//some custom properties
public bool IsValid()
{
//custom authentication logic
}
private IIdentity identity;
public IIdentity Identity
{
get { return this.identity; }
}
public bool IsInRole(string role)
{
return true;
}
}
//override AuthorizeCore
public class MyAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
MyPrincipal user = new MyPrincipal();
if (user.isValid())
{
httpContext.User = user;
}
else
{
httpContext.Response.Redirect("~/Common/NoAuthorize", true);
}
}
}
[MyAuthorizeAttribute]
public class BaseMyController : Controller
{
protected virtual new MyPrincipal User
{
get { return HttpContext.User as MyPrincipal; }
}
}
Then in MVC controller,we can get the user information via MyPrincipal
user property.
However, when we start to use the same way in web api, we found that the web api has no HttpContext
property and in System.Web.Http.AuthorizeAttribute
, the method to be override accepts a HttpActionContext
argument, it also has no HttpContext
property or some where else we can set the MyPrincipal
instance.
I notice that the System.Web.Http.AuthorizeAttribute
summary says
Specifies the authorization filter that verifies the request's IPrincipal
It seems that there is some other way to set the IPrincipal
instance.
I have no idea about it, any good advice? By the way, why does the asp.net web api controller have no HttpContext
? Is there any design pattern about it?
The related questions ASP.NET MVC - Set custom IIdentity or IPrincipal
I implemented something as a proof of concept following mostly this: Authentication Filters in ASP.NET Web API 2
For Web API you can create an Attribute, IAuthenticationFilter. If I remember rightly, you can add it as a filter to the global filters in WebApiConfig
config.Filters.Add(new YourAuthenticationAttribute());
Or you can use it as an attribute on the api controller/ method.
You can then implement AuthenticateAsync, get the request's authorization header, check the scheme, and validate the parameter, and if all is valid, set the principal.
I think the idea is that you can add multiple of these filters in a chain and they can all authenticate against a specific set of requirements, like the scheme they look for, and somewhere in the chain the principal gets set, or a challenge is returned.
public class YourAuthenticationAttribute : Attribute, IAuthenticationFilter
{
public Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
HttpRequestMessage request = context.Request;
if (request.Headers.Authorization != null &&
request.Headers.Authorization.Scheme.Equals("yourScheme", StringComparison.OrdinalIgnoreCase))
{
// get the value sent with the header.
string authParam = request.Headers.Authorization.Parameter;
// do some validation on the parameter provided...
// if it's all valid, create a principal with claims:
List<Claim> claims = new List<Claim>()
{
new Claim(ClaimTypes.Name, "Eddie Admin"),
new Claim(ClaimTypes.Role, "Admin"),
// new Claim(ClaimTypes.Role, "Delete"),
};
// create an identity with the valid claims.
ClaimsIdentity identity = new ClaimsIdentity(claims, "yourScheme");
// set the context principal.
context.Principal = new ClaimsPrincipal(new[] { identity });
When creating the principal you can apply claims and these are checked against the normal authorize attribute. e.g.
[Authorize(Roles = "Admin")]
I haven't used it beyond doing this, but hopefully this points you in the right direction.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With