DavidParks8 / Owin-Authorization

Backport of Asp.Net core's policy based authorization to Asp.Net 4
Other
60 stars 16 forks source link

How to get the IAuthorizationService instance inside of a controller? #54

Closed Shazwazza closed 7 years ago

Shazwazza commented 7 years ago

I can see this class AuthorizationDependencyHelper is internal which is used to extract the authorization options and therefore the IAuthorizationService but i can't see a way to get access to the IAuthorizationService inside of a WebApi controller without duplicating this code. I know i can create an instance of AuthorizationHelper but that doesn't let me execute AuthorizeAsync with a resource specified (such as AuthorizeAsync(this IAuthorizationService service, ClaimsPrincipal user, object resource, AuthorizationPolicy policy))

Any tips? I can make a PR to update AuthorizationHelper to have an overload or publicize AuthorizationDependencyHelper if needed

DavidParks8 commented 7 years ago

I don't think any changes will be necessary to achieve what you want. You can access the IAuthorizationService instance through the AuthorizationOptions that you have configured. Get a reference to your AuthorizationOptions and look at options.Dependencies.Service. From there, you should be able to use the IAuthorizationService as you desire. The service will be initialized by default if it has not been overloaded.

There are many ways to get a reference to your AuthorizationOptions and they all depend upon your specific implementation. If you are using owin and an IoC framework, then I would register the options instance as a singleton in your owin startup, then inject it into your controller. If you are using a more classic asp.net approach, then I would probably create the options in Global.asax and statically hold a reference to it (which would not be ideal due to testability problems, but hey, that's part of why owin became a thing). The key here is that you need to somehow save a reference to your AuthorizationOptions because then you can access it later in your controller.

DavidParks8 commented 7 years ago

Does that solve your issue?

Shazwazza commented 7 years ago

Hi, yup i know i can get a reference to IAuthorizationService from the AuthorizationOptions but those are only available in Owin startup. I need a reference to IAuthorizationService inside of my WebApi controller because I need to manually call:

AuthorizationService.AuthorizeAsync(ClaimsPrincipal, new ContentResourceAccess(id), AuthorizationPolicies.ContentRead);

The important part here is the middle parameter because I need to pass an instance of my resource because this inline authorization policy needs this contextual information which cannot be done using the attribute. For example this is what I'm after: https://docs.microsoft.com/en-us/aspnet/core/security/authorization/resourcebased

However, i am not using IoC so i cannot register IAuthorizationService in a container and have it injected. I will need to access IAuthorizationService from the OwinContext (or similar) and there doesn't seem to be a public way to do that. For now, my work around is as follows:


//Need a wrapper to add to the OwinContext which needs to be IDisposable
internal class AuthorizationServiceWrapper : IDisposable
{
    public IAuthorizationService AuthorizationService { get; }

    public AuthorizationServiceWrapper(IAuthorizationService authorizationService)
    {
        AuthorizationService = authorizationService;
    }

    public void Dispose()
    {
    }
}

//Add this to the OwinContext when configuring my auth policies:
app.CreatePerOwinContext(() => new AuthorizationServiceWrapper(options.Dependencies.Service));

//Then in my controller I can get this wrapper from the OwinContext
var authService = Request.GetOwinContext().Get<AuthorizationServiceWrapper>().AuthorizationService;
Shazwazza commented 7 years ago

I've just seen this reference https://github.com/DavidParks8/Owin-Authorization/issues/53#issuecomment-286955199

So is the preferred way to do this like this instead?

var authorizationService = AuthorizationDependencies.Create(new AuthorizationOptions()).Service;
DavidParks8 commented 7 years ago

AuthorizationDependencies.Create still requires that you have a reference to your AuthorizationOptions in the controller. I would recommend this instead:

public static class AuthorizationServiceContext
{
    public static IAuthorizationService Current { get; set; }
}

and in startup.cs...

AuthorizationServiceContext.Current = options.Dependencies.Service;

This fits the pattern of Asp.net old without dependency injection (like HttpContext.Current), and it is not coupled to OWIN. This will, however, make testing concurrently very annoying due to race conditions in the Static setter (unless you know how to manipulate the AppDomain or have already solved this). If you see that as being an issue, then we should consider exposing the IAuthorizationService via an extension method in the core library such as: Request.GetOwinContext().GetAuthorizationService();.

Shazwazza commented 7 years ago

Thanks. I think for now i'll stick to my AuthorizationServiceWrapper work around since that is still easily unit testable and i can just extract the IAuthorizationService that i referenced from my options in the OwinContext

DavidParks8 commented 7 years ago

At any rate, you've identified an easy-to-add extensibility point, and I'll be including the extension in the next release.

Shazwazza commented 7 years ago

Great! :) thanks for your feedback.

DavidParks8 commented 7 years ago

A new version has been released with the new extension method.

Shazwazza commented 7 years ago

wow, that was very quick :) cheers!