I have a database with users, the users can have different roles.
I want to implement basic authentication for my MVC Web API and I want to be able to tag methods with an Authorize tag and also pass userType as a parameter.
[Authorize(admin)]
public bool Test()
{
}
[Authorize(user)]
public bool Test1()
{
}
I can't figure out how to make this attribute, for example how do I make an attribute that simply makes a method always return false?
[AttributeUsage(AttributeTargets.All)]
public class TestAttribute: System.Attribute
{
//Return false?
}
I'm looking for some advice.
EDIT:
I made the following class:
ilterContext.HttpContext.Response.StatusCode = 401;
filterContext.Result = new EmptyResult();
filterContext.HttpContext.Response.End();
}
}
}
private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
then i add [BasicAuthorize] to a method, but it still let me access it without basic autentication.
any idea?
I suggest you read some basics about authorization and authentication and how the Authorize attribute can be applied.
Roles are out of the box. Just use the built-in capabilities of the Authorize attribute: Example:
[Authorize(Roles="admin")]
public bool Test()
{
}
If you need a custom implementation you need to inherit from System.Web.Mvc.AuthorizeAttribute instead of from System.Attribute. Everything else is pretty straight forward from here. Basic example explained here: Basic Authorization Attribute in ASP.NET MVC
Related
Is there a way to require a specific Authorization Scheme when using the [Authorize] Attribute on a Controller in asp.net MVC 4?
I expected something like this (which is totally possible in .net core btw)
[Authorize(AuthenticationSchemes = "Bearer")]
public class MyController : Controller { }
As far as I know, there is nothing out of the box that would allow you to write this.
The standard authorize attribute doesn't support this.
But you could write your own attribute and check the claims of the identity coming in.
I used an backport of ASP.NET Core authorization policies to .NET Full framework: https://github.com/DavidParks8/Owin-Authorization to write such rules.
How to check of you come from which token?
Normally you will see a claim similar to "idp": "oidc"
How to get the claims? ((ClaimsPrinciple)User).Claims ( in Controller code)
As suggested by #Chetan Ranpariya in the comments I ended up implementing a derived attribute (from AuthorizeAttribute). According to the documentation, overriding the AuthroizeCore method is the way to do it.
When overridden, provides an entry point for custom authorization checks.
Here is a working example for future reference
public class MyAuthorizeAttribute : AuthorizeAttribute
{
public string AuthSchemes { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (this.AuthSchemes != null)
{
string scheme = httpContext?.User?.Identity?.AuthenticationType;
if (string.IsNullOrWhiteSpace(scheme))
{
return false;
}
return this.AuthSchemes.Split(',').Contains(scheme);
}
return base.AuthorizeCore(httpContext);
}
}
The attribute can be used like this
[MyAuthorize(AuthSchemes = CookieAuthenticationDefaults.AuthenticationType)]
public class MyController : Controller { }
For some experimental/exercise purposes, I'm trying to implement a primitive login system before proceeding to Identity services.
My idea was creating a custom data annotation -just like [Authorize] of Identity- and passing inputted form data carried by Session[]. Hence, I would use my custom annotation to authorize certain controllers or methods.
public class IdCheck: ValidationAttribute
{
public string currentId { get; set; }
public override bool IsValid(object value)
{
currentId = value as string;
if (currentId != null)
{
return true;
}
else
{
return false;
}
}
}
and I tried to use it like that:
[IdCheck(currentId = Session["UserId"])]
public class SomeController : Controller
{
//methods
}
However, I got an error with the message: "An object reference is required for the non static field, method or property 'Controller.Session'"
I can neither access the Session[] before a controller nor before method declaration. I have no idea how to instantiate or refere.
Is there a way to utilize Session[] in my case? Because I don't want to manually check each method with if statements like
if(Session["UserId"] != null) {}
Any alternative solution is more than welcomed.
Note: I am aware of that using Session is not a recommended way of implementing such a task. But my intention is to have an understanding of how login operations work before utilizing advanced Identity services.
Is there a way to utilize Session[] in my case?
Not as you're doing now, but you could use HttpContext.Current.Session inside your custom attribute code instead of passing it as property.
Thanks a lot Oscar. What you proposed actually worked. However, I managed to find a better way yo carry out my "primitive login" operation using a custom data annotation.
After some more research it turned out that implementing FilterAttribute class along with IAuthorizationFilter filter does the trick. I believe this could help someone else as well:
public class IdCheck : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Session["UserId"] == null)
{
filterContext.Result = new RedirectResult("/");
}
}
}
and the usage is the same of course:
[IdCheck]
public class SomeController : Controller
{
//methods
}
I have the following code:
CookieHeaderValue cookie = Request.Headers.GetCookies("session").FirstOrDefault();
var isAuthenticated = _userService.IsAuthenticated(cookie);
if (!isAuthenticated)
return Request.CreateErrorResponse(HttpStatusCode.Unauthorized, "");
I'd like this code to execute as soon as any part of my api is called. I havn't found any good solutions or ways to do this so i thought i would ask here instead.
(what I do now is execute the code in every get/post/put/delete which is horrible).
The best place to solve this would be an authorization filter attribute. See Authentication Filters in ASP.NET Web API 2.
The subject is too broad to repeat here in its entirety, but it comes down to creating an attribute:
public class CookieAuthenticationFilterAttribute : Attribute, IAuthenticationFilter
{
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
// your cookie code
}
}
And applying it to the controller or action methods:
[YourCookieAuthentication]
But be sure to read the link.
You can use an ActionFilter or AuthorizationFilter for this purpose. These are attribute classes that you can use on specific controllers/actions or globally. So you don't need to repeat the code for every action.
See this link for details. It shows the general authentication/authorization flow in ASP.NET Web API and how you can customize it.
So i found the best solution for my problem was the following code:
public class CookieFilterAttribute : AuthorizeAttribute
{
[Inject]
public IUserService UserService { get; set; }
protected override bool IsAuthorized(HttpActionContext actionContext)
{
CookieHeaderValue cookie = actionContext.Request.Headers.GetCookies("session").FirstOrDefault();
var isAuthenticated = UserService.IsAuthenticated(cookie);
return isAuthenticated;
}
}
I'm having some problem with my custom AuthorizeAttribute
public class ExplicitAuthorizeAttribute : AuthorizeAttribute
{
private readonly MembershipUserRole[] _acceptedRoles;
public ExplicitAuthorizeAttribute()
{
}
public ExplicitAuthorizeAttribute(params MembershipUserRole[] acceptedRoles)
{
_acceptedRoles = acceptedRoles;
}
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//Validation ...
}
}
I use it like this:
[ExplicitAuthorize[(MembershipUserRole.Admin, MembershipUserRole.SuperAdmin)]
It works perfectly for HttpGet and HttpPost to validate my controllers and methods.
But when I use it in a ApiController and make ajax calls, AuthorizeCore isn't running and I got a security breach. :/
My enum looks like this
[Flags]
public enum MembershipUserRole
{
Admin= 1,
SuperAdmin = 2
}
Does anyone know why my AuthorizeCore isn't validating in this context?
By the way If I use
[Authorized(Roles ="Admin, SuperAdmin")]
It's validates perfectly, but I'd like to have Stronly Typed Roles,that's why I'm using enums.
You have derived from the wrong class: System.Web.Mvc.AuthorizeAttribute whereas for a Web API controller you should derive from System.Web.Http.AuthorizeAttribute.
Don't forget that ASP.NET MVC and ASP.NET Web API are 2 completely different frameworks and even if they share some common principles and names, the corresponding classes are located in 2 completely different namespaces.
So what you have done is decorate an ASP.NET Web API action with an AuthorizeAttribute that it doesn't know anything about.
If you want to make authorization in ASP.NET Web API make sure you have derived from the correct attribute:
public class ExplicitAuthorizeAttribute : System.Web.Http.AuthorizeAttribute
{
private readonly MembershipUserRole[] _acceptedRoles;
public ExplicitAuthorizeAttribute()
{
}
public ExplicitAuthorizeAttribute(params MembershipUserRole[] acceptedRoles)
{
_acceptedRoles = acceptedRoles;
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
//Validation ...
}
}
Okay, I'm obviously missing something, as this should be pretty simple. I've created the following attribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class EntitleAttribute : Attribute
{
public EntitleAttribute(string permissionName)
{
bool hasPermission = ...
if (!hasPermission)
{
throw new HttpException(403, "Forbidden");
}
}
}
I put it on a method:
[Entitle("Access Application")]
public ActionResult MyMethod(...) { ... }
However, it never gets called. What am I missing?
Attributes don't do anything by themselves. There needs to be some code that would look for the attribute and do something with it.
Based on code sample you want to perform some per-authentication in ASP.Net MVC application. In this case you should derive your attribute from MVC's AuthorizeAttribute. If you need more general handling in MVC site - base FilterAttribute or even IMvcFilter provide more options.