WEB API - Authorize at controller or action level (no authentication) - c#

I have an existing API that has No Authentication. It`s a public Web API which several clients use by making simple requests.
Now, there is the need to authorize access to a certain method.
Is there any way to do this, keeping the rest of the controllers and respective methods "open" for the clients that already use this Web API?
How can i identify if the request has permissions to access this "protected" method?

What you'll need to do is add an [Authorize] attribute to the methods you want to protect optionally using the overload that accepts one or more role names that the calling user must be in.
Then what you'll have to implement is a way to ensure that authentication data of the caller is transformed into a Principal object. Setting the Principal is generally something you don't do yourself, but instead have the framework do for you.
If you do want to provide your own interface, you can using an authentication filter implementing the System.Web.Http.Filters.IAuthenticationFilter interface.
So what you'll get is this:
[MyAuthentication]
[Authorize]
public SomeClass MyProtectedMethod() {
return new SomeClass();
}
And then implement the MyAuthentication attribute. Below is an example, the important thing is that you use the context of the incoming request and end up setting the context.Principal property with a new Principal
public class MyAuthentication : ActionFilterAttribute, System.Web.Http.Filters.IAuthenticationFilter {
public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
{
// 1. Look for credentials in the request.
HttpRequestMessage request = context.Request;
AuthenticationHeaderValue authorization = request.Headers.Authorization;
// 2. If there are no credentials, do nothing.
if (authorization == null)
{
return;
}
// 3. If there are credentials but the filter does not recognize the
// authentication scheme, do nothing.
if (authorization.Scheme != "Basic")
{
return;
}
// 4. If there are credentials that the filter understands, try to validate them.
// 5. If the credentials are bad, set the error result.
if (String.IsNullOrEmpty(authorization.Parameter))
{
context.ErrorResult = new AuthenticationFailureResult("Missing credentials", request);
return;
}
Tuple<string, string> userNameAndPasword = ExtractUserNameAndPassword(authorization.Parameter);
if (userNameAndPasword == null)
{
context.ErrorResult = new AuthenticationFailureResult("Invalid credentials", request);
}
string userName = userNameAndPasword.Item1;
string password = userNameAndPasword.Item2;
IPrincipal principal = await AuthenticateAsync(userName, password, cancellationToken);
if (principal == null)
{
context.ErrorResult = new AuthenticationFailureResult("Invalid username or password", request);
}
// 6. If the credentials are valid, set principal.
else
{
context.Principal = principal;
}
}
... other interface methods here
}
I hope this helps you get on the right track. For more information check this post:
http://www.asp.net/web-api/overview/security/authentication-filters

You can use [Authorize] attribute at particular API method as well as at controller level. In case you put the [Authorize] attribute at controller level then you can use [AllowAnonymous] attribute for those API method which you want to access without authentication.

By default, authorization is globally disabled on application. You can force your controller to only allow authorized requests by adding the action filter [Authorize].
[Authorize] // This will enforce all methods inside should be authorized
public class AuthorizeController : ApiController
{
//this method will only be called if user is authorized
public IHttpActionResult GetList()
{
return Ok();
}
}
You can also force only certain methods be authorized:
public class AuthorizeController : ApiController
{
[Authorize] //this method will only be called if user is authorized
public IHttpActionResult GetList()
{
return Ok();
}
// This method can still be called even if user is not authorized
public IHttpActionResult GetListUnauthorized()
{
return Ok();
}
}
Or just disable authorization on some methods inside a controller that requires authorization:
[Authorize]
public class AuthorizeController : ApiController
{
//this method will only be called if user is authorized
public IHttpActionResult GetList()
{
return Ok();
}
[AllowAnonymous]// This method can be called even if user is not authorized due the AllowAnonymous attribute
public IHttpActionResult GetListUnauthorized()
{
return Ok();
}
}
You can also set who's allowed to access your method by using:
[Authorize(Users="Joey,Billy")]
Or by Rules using:
[Authorize(Roles="Administrator,Manager")]
Or even build a more complex Authorize attribute like in this answer (Based on Claims): Authorization Attribute by Claims

We solved it using [AllowAnonymous] on the method, who we didn't want to be Authenticated but Authorizated, overriding the Authorization.

The Execution flow will goes to method level then its goes to Controller Level. So if you mention as "AllowAnonymous" will execute with out Authorization check.

Related

Is there a way to functionally perform RedirectToAction in C# ASP.NET Core MVC?

I have an existing ASP.NET core application where I need to block a set group of users from actions on some of my controllers. I can do this on a per task basic but am looking for a way to function the entire block to avoid as much duplication as possible.
An example of how I did this for a Task is:
public async Task<IActionResult> Index()
{
if(RestrictedUserCheckConditions) return RedirectToAction(nameof("RestrictionView"), "UserErrors");
//continue with action for unrestricted users.
...
}
Is there a way I can function this to achive something like below to avoid too much duplication? Otherwise I have a lot of code to add to a lot of Tasks!
public async Task<IActionResult> Index()
{
RestrictedUserCheck();
//continue with action for unrestricted users.
...
}
Thanks in advance
You can create a Custom Authorize attribute to prevent user access the action method:
For example: create a CustomAuthorizeAttribute with the following code:
public class CustomAuthorizeAttribute : TypeFilterAttribute
{
//this method will access the block users, and transfer the data to the ClaimRequirementFilter
public CustomAuthorizeAttribute(string blockUser) : base(typeof(ClaimRequirementFilter))
{
Arguments = new object[] { new Claim(ClaimTypes.Name, blockUser) };
}
}
public class ClaimRequirementFilter : IAuthorizationFilter
{
readonly Claim _claim; //we can get the block users from this property.
public ClaimRequirementFilter(Claim claim)
{
_claim = claim;
}
public void OnAuthorization(AuthorizationFilterContext context)
{
//query current user's claims and check whether current user is in the block user list
var hasClaim = context.HttpContext.User.Claims.Any(c => c.Type == _claim.Type && _claim.Value.ToLower().Split(',').Contains(c.Value.ToLower()));
//if current user is the block user, prevent user access the action method, and return a forbidresult.
if (hasClaim)
{
context.Result = new ForbidResult();
}
}
}
Then apply the above attribute on the action method:
[CustomAuthorize("bb#hotmail.com,cc#hotmail.com")]
public IActionResult Privacy()
{
return View();
}
the result as below:
[Note] In the above sample, we are using the Asp.net core Identity, after login, it already adds the current user claims. If you don't use Asp.net core Identity, you have to add the claims to the HttpContext by yourself.
Besides, you can also use Policy based and Role based authorization to do it, refer the following links:
Policy-based authorization in ASP.NET Core
Policy-Based And Role-Based Authorization In ASP.NET Core 3.0 Using Custom Handler

How to get user claims inside ASP Core 2 method without Authorize attribute?

I have a method on an API that can be accessed anonymously. I want to use resource authorization to determine if the user has access. If the object is "public" than it can be accessed by anyone (including anonymous users). If the object is "private" than it can only be viewed by logged in users. This logic works fine if I have an authorize attribute on the method, but if not the User has no claims even when they are logged in.
Is there a way to get the user's claims in a method without an Authorize attribute?
Method looks like this:
[HttpGet]
[Route("name/{name}")]
public async Task<IActionResult> Get(string name)
{
var activity = Repo.GetCommandLineActivity(name);
if (activity == null)
{
return NotFound();
}
var isAuthed = await _authService.AuthorizeAsync(User, activity, new ViewIPublicPrivateRequirement());
if (isAuthed.Succeeded)
{
return Ok(activity);
}
return Unauthorized();
}
The solution was actually very simple, adding [AllowAnonymous] and [Authorize] did the trick.
It is possible to retrieve the ClaimsPrincipal even without the [Authorize] attribute, but it truly feels like a hack, and I wouldn't recommend it. If I were you, I'd create two different endpoints, one for public access, and the other for authenticated users.
That being said, the way to retrieve the ClaimsPrincipal is to call the AuthenticateAsync method. Note that this code is for ASP.NET Core 2.0, it would be slightly different for 1.1.
Here's the modified method:
[HttpGet("name/{name}")]
[AllowAnonymous]
public async Task<IActionResult> Get(string name)
{
var activity = Repo.GetCommandLineActivity(name);
if (activity == null)
{
return NotFound();
}
// based on the way your authentication is configured in services.AddAuthentication(),
// this can be omitted (in which case, the default authenticate scheme will be used)
var authenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme;
var auth = await HttpContext.AuthenticateAsync(authenticationScheme);
if (auth.Succeeded)
{
// CAUTION: HttpContext.User will STILL be null
var user = auth.Principal;
return Ok(activity);
}
return Unauthorized();
}
Caution: HttpContext.User will NOT be set if the [Authorize] attribute is omitted (or if [AllowAnonymous] is specified.
Short answer - you cannot.
If you check closely you will see that, when you have the Authorize attribute, the User object is of type ClaimsPrincipal and when you don't have it - it is of type WindowsPrincipal.
But you can always add custom Authorize attribute, or Custom policy-based authorization, and check the user claims there and do your stuff.

Custom IAuthenticationFilter and AllowAnonymous in Web API

I would like to make use of AllowAnonymous and a custom AuthenticationFilter. Can someone point me in the right direction to make use of AllowAnonymous or another alternative? Thanks
I've created my own custom filter that inherits from System.Attribute and implements System.Web.Http.Filters.IAuthenticationFilter
public class MyCustomAuthenticationAttribute : Attribute, IAuthenticationFilter
I have been able to successfully add the logic for the AuthenticateAsync method
public async Task AuthenticateAsync(
HttpAuthenticationContext context,
CancellationToken cancellationToken) {}
My problem is that I need to ignore some of my Web API controller actions or controllers. I thought that I could use System.Web.Http.AllowAnonymousAttribute to do this. For example here is a really simple example showing intent.
[MyCustomAuthentication]
public class HomeController : ApiController
{
// no authentication needed allow anonymous
[HttpGet]
[Route("hianonymous")]
[AllowAnonymous]
public IHttpActionResult Hello(string name) {
return Ok(new { message = "hello " + name });
}
// needs to be authenticated
[HttpGet]
[Route("hiauthenticated")]
public IHttpActionResult Hello() {
var name = User.Identity.Name;
return Ok(new { message = "hello authenticated user " + name });
}
}
The problem is that Authenticate() is still called on MyCustomAuthenticationAttribute. I would like to use AllowAnonymous or some other method to accomplish this. Thanks for any input.
I know that I can use my custom authentication attribute at the action level and not controller level but there are cases I would like an entire controller or even as a global filter so I need to be able to excluded on an individual action or controller basis.
Your implementation of IAuthenticationFilter should do NOTHING if it does not find an Authorization scheme it does not recognize.
http://www.asp.net/web-api/overview/security/authentication-filters
// 2. If there are no credentials, do nothing.
if (authorization == null)
{
return;
}
// 3. If there are credentials but the filter does not recognize the
// authentication scheme, do nothing.
if (authorization.Scheme != "Basic")
{
return;
}
The idea is that your filter is simply a way to AUTHENTICATE using a known scheme.
You will still need to use the built in AuthorizeAttribute and AllowAnonymousAttribute to control AUTHORIZATION.

Securing WebAPI with [Authorize] attribute vs. User.Identiy.IsAuthenticated

I have a WebAPI controller that requires users to be authenticated and I'm using MS Identity 2.0 for authentication. The controller looks somewhat like this:
[Route("MyRoute")]
[Authorize]
[HttpPost]
public HttpResponseMessage Post([FromBody] string value)
{
if (User.Identity.IsAuthenticated == true)
{
....
}
else
{
return new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
}
If I remove one of these options at a time, in both cases, when an unauthorized user calls the controller, it returns a Forbidden response. What's difference between these two options and there one that's better than the other?
With an [Authorize] attribute, the authorization logic can be overridden with filters and will be located at a central location in code.
The
if (User.Identity.IsAuthenticated == true)
{
....
}
else
{
return new HttpResponseMessage(System.Net.HttpStatusCode.Forbidden);
}
basically is the same as the default [Authorize] functionality, but you'll be repeating yourself over and over.
A technical detail though, the authorization filters [Authorize] are higher up in the pipeline, so a Forbidden there will be more efficient for your server.
see: http://www.dotnet-tricks.com/Tutorial/mvc/LYHK270114-Detailed-ASP.NET-MVC-Pipeline.html
By "Authorize" attribute you can centrally create your request filter for all your request. its easy to manage. like if want to use different authentication provider like WebSecurity then you just need to change in one class instead of all your web apis like following :
[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : AuthorizationFilterAttribute
{
public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
{
base.OnAuthorization(actionContext);
////check authentication and return if not authorized
if (actionContext != null)
{
if (!WebSecurity.IsAuthenticated)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized) { RequestMessage = actionContext.ControllerContext.Request };
return;
}
}
}
}

Authorize attribute for any roles

Let's say I have WebApi Controller
[Authorize]
public class SomeApiController : ApiController
Controller action methods itself does not have any [Authorize] or [AllowAnonymous] attributes.
I want Authorize attribute to return 401 (Unauthorized) error if user has no roles - seems logical (if user had role and now doesn't have ANY - he shouldn't be allowed to perform action even though user is authenticated). I have looked to asp.net mvc webstack I have found the following code in Authorize attribute:
if (_rolesSplit.Length > 0 && !_rolesSplit.Any(user.IsInRole))
{
return false;
}
So looks like if we didn't passed roles authorize attribute just checks if user is authenticated. Setting each role in Roles list is not an option for me ( I mean [Authorize(Roles="role1,role2,...")]).
Therefore question - can I somehow achieve setting Authorize attribute to check if user has ANY role. Or it's better to write custom attribute inherited from above?
Create custom attribute like below:
public override void OnAuthorization(AuthorizationContext filterContext)
{
string[] userRoles = System.Web.Security.Roles.GetRolesForUser(filterContext.HttpContext.User.Identity.Name);
if (!userRoles.Any())
{
throw new HttpException(401, "Unauthorized");
}
base.HandleUnauthorizedRequest(filterContext);
}
}
Hope it helps

Categories

Resources