Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependency Injection (using SimpleInjector) and OAuthAuthorizationServerProvider

New to Dependency Injection, so this is probably a simple matter, but i have tried and cant figure it out, i am using Simple Injector.

I have a WebApi that uses SimpleInjector perfectly fine, now i would like to implement security using OAuth.

To do this i started to follow this tutorial, which is very helpful, but doesnt use Dependancy Injection

http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/

I have my global.asax file looking like this, to setup dependancy injection (working perfect)

protected void Application_Start()
{
    SimpleInjectorConfig.Register();

    GlobalConfiguration.Configure(WebApiConfig.Register);
}

I have created a Startup.Auth.cs file to configure OAuth

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var OAuthServerOptions = new OAuthAuthorizationServerOptions()
        {
            AllowInsecureHttp = true,
            TokenEndpointPath = new PathString("/token"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
            Provider = new MyAuthorizationServerProvider() // here is the problem
        };

        // Token Generation
        app.UseOAuthAuthorizationServer(OAuthServerOptions);
        app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
    }
}

Now as i commented above, MyAuthorizationServerProvider is the problem. it takes a parameter of IUserService which i usually inject. I do not want to empty constructor because my IUserService also injects a repository. Here is the file

public class ApiAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
    private IUserService _service;
    public ApiAuthorizationServerProvider (IUserService service) 
    {
         _service = service;
    }

    public override async Task ValidateClientAuthentication(
        OAuthValidateClientAuthenticationContext context)
    {
        context.Validated();
    }

    public override async Task GrantResourceOwnerCredentials(
        OAuthGrantResourceOwnerCredentialsContext context)
    {
        context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", 
            new[] { "*" });

        IUserService service = Startup.Container.GetInstance<IUserService>();
        User user = _service.Query(e => e.Email.Equals(context.UserName) &&
            e.Password.Equals(context.Password)).FirstOrDefault();

        if (user == null)
        {
            context.SetError("invalid_grant", 
                "The user name or password is incorrect.");
            return;
        }

        var identity = new ClaimsIdentity(context.Options.AuthenticationType);
        identity.AddClaim(new Claim("sub", context.UserName));
        identity.AddClaim(new Claim("role", "user"));

        context.Validated(identity);

    }
}

How can i get this working with Dependency Injection? This must happen quite a lot and must be able to do something to handle it. I am sure its something simple, but i am still learning.

like image 669
Gillardo Avatar asked Sep 23 '14 14:09

Gillardo


People also ask

Which method is used for dependency injection?

Types of Dependency Injection The injector class injects dependencies broadly in three ways: through a constructor, through a property, or through a method. Constructor Injection: In the constructor injection, the injector supplies the service (dependency) through the client class constructor.

What is dependency injection with example?

Dependency injection (DI) is a technique widely used in programming and well suited to Android development. By following the principles of DI, you lay the groundwork for good app architecture. Implementing dependency injection provides you with the following advantages: Reusability of code.

Does ASP NET support dependency injection?

ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies. For more information specific to dependency injection within MVC controllers, see Dependency injection into controllers in ASP.NET Core.


2 Answers

When you start with Dependency Injection, Owin is probably not the most friendly API to start with.

I noticed this part in your code:

IUserService service = Startup.Container.GetInstance<IUserService>();

You are probably doing this as a workaround before you find out how to use the constructor. But I think that's your answer right there. The OAuthAuthorizationServerProvider is a singleton, so your IUserService will be a singleton also and all the dependencies of this class will be singleton as well.

You mentioned you use a repository in your user service. Your probably don't want this repository to be singleton as I suppose this repository will use a DbContext of some kind.

So the intermediate answer could be the solution you made already. Maybe there is a more elegant solution if you do some research on what the UseOAuthAuthorizationServer method does exactly. The source code of Katana can be found here: Katana source code

For the registration of the other asp.net identity classes the link in the comment of DSR will give you a good starting point.

like image 127
Ric .Net Avatar answered Oct 02 '22 18:10

Ric .Net


Firstly, this is a late answer. I just wrote this down in case somebody else come across the similar issue and somehow get linked to this page (like me) in the future.

The previous answer is reasonable, but will not solve the problem if the service is actually registered per Web API request, which I believe is what people usually do if they want to use dependency injection for identity framework object like UserManager.

The problem is when GrantResourceOwnerCredentials get called (usually when people hit the 'token' endpoint), simple injector won't start a api request life cycle. To solve this, all you need to do is start one.

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        //......
        using (Startup.Container.BeginExecutionContextScope())
        {
            var userService= Startup.Container.GetInstance<IUserService>();
            // do your things with userService..
        }
       //.....
    }

With BeginExecutionContextScope, simple injector will start a new context scope. However, remember it need to be disposed explicitly.

like image 23
user3682091 Avatar answered Oct 02 '22 18:10

user3682091