Using Web API Authentication

I have a Web Forms application with which I am trying to use the new beta version of the Web API. The endpoints that I am viewing should be accessible only to the authenticated user of the site, as they are designed to use AJAX. In my web.config, I found that it forbade all users if they are not authenticated. This works the same as in web forms, but does not work with MVC or the web API.

I created an MVC controller and a web API controller for testing. What I see is that I cannot access the MVC or web API endpoints until I authenticate, but then I can continue to delete these endpoints, even after closing my browser and reusing application pool. But if I delete one of my aspx pages that sends me back to my login page, then I cannot hit the MVC or web API endpoints until I authenticate again.

Is there a reason MVC and web APIs are not working since my ASPX pages once my session was invalid? In his opinion, only an ASPX request clears my forms authentication cookie, which I assume is the problem here.

+6
source share
3 answers

If your web API is just used in an existing MVC application, I recommend creating your own AuthorizeAttribute filter for your MVC and WebApi controllers; I create what I call the AuthorizeSafe filter, which is the default blacklist, so if you forget to apply the authorization attribute to the controller or method, you will be denied access (I think that by default the whitelist is unsafe).

Two attribute classes are provided for expansion; System.Web.Mvc.AuthorizeAttribute and System.Web.Http.AuthorizeAttribute ; the first is used with MVC forms authentication, and the second also connects to forms authentication (this is very good because it means that you do not need to create a whole separate authentication architecture for authentication and API authorization). Here's what I came up with - it denies access to all default MVC controllers / actions and WebApi controllers / actions by default unless the AllowAnonymous or AuthorizeSafe attribute is used. First, an extension method to help with custom attributes:

 public static class CustomAttributeProviderExtensions { public static List<T> GetCustomAttributes<T>(this ICustomAttributeProvider provider, bool inherit) where T : Attribute { List<T> attrs = new List<T>(); foreach (object attr in provider.GetCustomAttributes(typeof(T), false)) { if (attr is T) { attrs.Add(attr as T); } } return attrs; } } 

A helper authorization class that uses AuthorizeAttribute extensions:

 public static class AuthorizeSafeHelper { public static AuthActionToTake DoSafeAuthorization(bool anyAllowAnonymousOnAction, bool anyAllowAnonymousOnController, List<AuthorizeSafeAttribute> authorizeSafeOnAction, List<AuthorizeSafeAttribute> authorizeSafeOnController, out string rolesString) { rolesString = null; // If AllowAnonymousAttribute applied to action or controller, skip authorization if (anyAllowAnonymousOnAction || anyAllowAnonymousOnController) { return AuthActionToTake.SkipAuthorization; } bool foundRoles = false; if (authorizeSafeOnAction.Count > 0) { AuthorizeSafeAttribute foundAttr = (AuthorizeSafeAttribute)(authorizeSafeOnAction.First()); foundRoles = true; rolesString = foundAttr.Roles; } else if (authorizeSafeOnController.Count > 0) { AuthorizeSafeAttribute foundAttr = (AuthorizeSafeAttribute)(authorizeSafeOnController.First()); foundRoles = true; rolesString = foundAttr.Roles; } if (foundRoles && !string.IsNullOrWhiteSpace(rolesString)) { // Found valid roles string; use it as our own Roles property and auth normally return AuthActionToTake.NormalAuthorization; } else { // Didn't find valid roles string; DENY all access by default return AuthActionToTake.Unauthorized; } } } public enum AuthActionToTake { SkipAuthorization, NormalAuthorization, Unauthorized, } 

The two extension classes themselves:

 public sealed class AuthorizeSafeFilter : System.Web.Mvc.AuthorizeAttribute { public override void OnAuthorization(AuthorizationContext filterContext) { if (!string.IsNullOrEmpty(this.Roles) || !string.IsNullOrEmpty(this.Users)) { throw new Exception("This class is intended to be applied to an MVC web API application as a global filter in RegisterWebApiFilters, not applied to individual actions/controllers. Use the AuthorizeSafeAttribute with individual actions/controllers."); } string rolesString; AuthActionToTake action = AuthorizeSafeHelper.DoSafeAuthorization( filterContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(false).Count() > 0, filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>(false).Count() > 0, filterContext.ActionDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>(false), filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>(false), out rolesString ); string rolesBackup = this.Roles; try { switch (action) { case AuthActionToTake.SkipAuthorization: return; case AuthActionToTake.NormalAuthorization: this.Roles = rolesString; base.OnAuthorization(filterContext); return; case AuthActionToTake.Unauthorized: filterContext.Result = new HttpUnauthorizedResult(); return; } } finally { this.Roles = rolesBackup; } } } public sealed class AuthorizeSafeApiFilter : System.Web.Http.AuthorizeAttribute { public override void OnAuthorization(HttpActionContext actionContext) { if (!string.IsNullOrEmpty(this.Roles) || !string.IsNullOrEmpty(this.Users)) { throw new Exception("This class is intended to be applied to an MVC web API application as a global filter in RegisterWebApiFilters, not applied to individual actions/controllers. Use the AuthorizeSafeAttribute with individual actions/controllers."); } string rolesString; AuthActionToTake action = AuthorizeSafeHelper.DoSafeAuthorization( actionContext.ActionDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0, actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AllowAnonymousAttribute>().Count > 0, actionContext.ActionDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>().ToList(), actionContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes<AuthorizeSafeAttribute>().ToList(), out rolesString ); string rolesBackup = this.Roles; try { switch (action) { case AuthActionToTake.SkipAuthorization: return; case AuthActionToTake.NormalAuthorization: this.Roles = rolesString; base.OnAuthorization(actionContext); return; case AuthActionToTake.Unauthorized: HttpRequestMessage request = actionContext.Request; actionContext.Response = request.CreateResponse(HttpStatusCode.Unauthorized); return; } } finally { this.Roles = rolesBackup; } } } 

And finally, an attribute that can be applied to methods / controllers to allow users in certain roles to access them:

 public class AuthorizeSafeAttribute : Attribute { public string Roles { get; set; } } 

Then we register our AuthorizeSafe filters globally from Global.asax:

  public static void RegisterGlobalFilters(GlobalFilterCollection filters) { // Make everything require authorization by default (whitelist approach) filters.Add(new AuthorizeSafeFilter()); } public static void RegisterWebApiFilters(HttpFilterCollection filters) { // Make everything require authorization by default (whitelist approach) filters.Add(new AuthorizeSafeApiFilter()); } 

Then, to open the action, for example. anonymous access or admin access only:

 public class AccountController : System.Web.Mvc.Controller { // GET: /Account/Login [AllowAnonymous] public ActionResult Login(string returnUrl) { // ... } } public class TestApiController : System.Web.Http.ApiController { // GET API/TestApi [AuthorizeSafe(Roles="Admin")] public IEnumerable<TestModel> Get() { return new TestModel[] { new TestModel { TestId = 123, TestValue = "Model for ID 123" }, new TestModel { TestId = 234, TestValue = "Model for ID 234" }, new TestModel { TestId = 345, TestValue = "Model for ID 345" } }; } } 
+3
source

It should work in the Normal MVC controller. you just need to decorate the action with the [Authorize] attribute.

In web api you need to have special authorization. You can find the link below.

http://www.codeproject.com/Tips/376810/ASP-NET-WEB-API-Custom-Authorize-and-Exception-Han

+1
source

If you use the MVC Authorize attribute, it should work the same for WebAPI, as for regular MVC controllers.

-1
source

Source: https://habr.com/ru/post/914182/


All Articles