简体   繁体   中英

Writing extensible OWIN authentication

I'm working on a set of web services. I've implemented a custom OWIN authentication based on my company's security, so now the Authorize attribute works.

[Authorize]// It's working!

I want to package it up for others, but I want to support roles first (or at least have a plan/recommendation for how to support roles). Unfortunately, my company doesn't have a standard way of handling roles, so different services (or applications) may take different approaches.

[Authorize("Member")]// It's not working. :(

What is the best approach to make my authentication middleware easily extensible? My particular needs are geared towards supporting roles, but something more generic is fine. Some thoughts I've had:

  • I could expect them to extend my authentication.
    • This requires more maintenance by the developers.
    • Removes a large chunk of the value in making what I've written reusable.
  • I could provide events to hook into.
    • Many of the pre-built authentications seem to do something like this. They accept a provider in their options, and that provider supports a handful of events.
      • I'm not familiar with this approach; I'm not sure if it's the best solution.
      • My current options object is empty (like many tutorials) and I'm hesitant to change that.
    • I haven't written (and hardly consumed) any events before, so this would be new territory for me personally.

I'm inclined towards the latter approach, but I'm really just shooting in the dark. I'd greatly appreciate a confident confirmation of what would work or has worked best.

To support a custom attribute like [Authorize("Member")] you can extend the attributes and write a custom one. Extend the System.Web.Http.AuthorizeAttribute class and perform all checks your need to do.

This explains how to do it:

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

And this is an implementation in our solution.

public class ClaimsAuthorize : System.Web.Mvc.AuthorizeAttribute
    {
        public string PhenixPermissions { get; set; }
        public string[] NeededPermissions;

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            Logger.Logger.Trace("ClaimsAuthorize.AuthorizeCore");

            NeededPermissions = PhenixPermissions.Split(',');

            if (!(httpContext.User.Identity is ClaimsIdentity))
            {
                return false;
            }

            var claimsIdentity = httpContext.User.Identity as ClaimsIdentity;
            Claim encodedPermissions = claimsIdentity.FindFirst(ConfigurationManager.AppSettings["ApplicationPermissionsKey"]);
            string applicationId = ClaimsHelper.GetClaimValue("audience");

            if (encodedPermissions == null)
            {
                Logger.Logger.Warn("No PhenixPermissions found. Are you logged in?");
                return false;
            }

            var tenant = AppManager.GetApplication(applicationId);

            List<string> decodedPermissions = PermissionsDecodeHelper.BitDecodePermissions(tenant.ConnectionString, encodedPermissions.Value);

            bool hasRights = false;

            foreach (var neededPermission in NeededPermissions)
            {
                if (decodedPermissions.FirstOrDefault(x => x == neededPermission) != null)
                {
                    Logger.Logger.Debug(string.Format("User '{0}' authorized to access view '{1}'.", claimsIdentity.Name.ToString(), httpContext.Request.FilePath));
                    hasRights = true;
                }
            }

            return hasRights;
        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            Logger.Logger.Trace("ClaimsAuthorize.HandleUnauthorizedRequest");

            Logger.Logger.Warn(string.Format("Unauthorized to access '{0}'.", filterContext.Controller));

            filterContext.Controller.TempData["ErrorCode"] = "0123456789";
            filterContext.Controller.TempData["ErrorMessage"] = string.Format("'{0}' is not authorized to access access view '{1}'", HttpContext.Current.User.Identity.Name.ToString(), filterContext.Controller);

            filterContext.Result = new RedirectResult("~/Home/Error");
        }
    }

Some remarks: the PhenixPermissions is the string that comes in when using the attribute like [ClaimsAuthorize(PhenixPermissions = "ConsultExternalLink")]

The rest is some specific logic for our application but you should get it.

Just return AuthorizeCore true or false when allowed to call the Controller, API, etc...

Hope this helps

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM