3

In my web api application I implemented OAuth2. On GrantResourceOwnerCredentials of ApplicationOAuthProvider I am calling my custom membership service to login and get the token.The problem is I have to inject the membership service into ApplicationOAuthProvider in order to use the service but it didn't allow it due to owinStartup class which don't support the parameter constructor.How do I inject/use my membership service in GrantResourceOwnerCredentials method.

  public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
  {
      private readonly string _publicClientId;
      private readonly IMembershipService _membershipService;

      public ApplicationOAuthProvider(string publicClientId, 
           IMembershipService membershipService)
      {
        if (publicClientId == null)
        {
            throw new ArgumentNullException("publicClientId");
        }

        _publicClientId = publicClientId;
        this._membershipService = membershipService;
      }

      public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
      {
            AccountLogin LogCredentials = new AccountLogin();
            LogCredentials.UserName = context.UserName;
            LogCredentials.Password = context.Password;

            ProviderLoginResponse providerLoginResponse = 
                _membershipService.UserLogin(LogCredentials);
            if (providerLoginResponse.LoginStatus != "Y")
            {
                context.SetError("invalid_grant", "The user name or password 
                is incorrect.");
                return;
            }

            var claims = new List<Claim>()
            {
                    new Claim(ClaimTypes.Sid, Convert.ToString(1)),
                    new Claim(ClaimTypes.Name, providerLoginResponse.UserName),
                    new Claim(ClaimTypes.Email, providerLoginResponse.UserEmail)
            };

            ClaimsIdentity oAuthIdentity = new ClaimsIdentity(claims,
                    Startup.OAuthOptions.AuthenticationType);

            AuthenticationProperties properties = CreateProperties(context.UserName);

            AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);

            context.Validated(ticket);
      }
  }

My owin startup class :

 public partial class Startup
 {
    private readonly IMembershipService _membershipService;

    //This will cause a runtime error owin startup class only support parameterless constructor
    public Startup(IMembershipService membershipService)
    {
        this._membershipService = membershipService;
    }

    public void ConfigureAuth(IAppBuilder app)
    {
          OAuthOptions = new OAuthAuthorizationServerOptions
          {
            TokenEndpointPath = new PathString("/Token"),

            //Here passing the _membershipService to ApplicationOAuthProvider constructor
            Provider = new ApplicationOAuthProvider(PublicClientId,_membershipService ),

            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            // In production mode set AllowInsecureHttp = false
            AllowInsecureHttp = true
          };
    }
 }
Mussammil
  • 864
  • 4
  • 16
  • 38
  • @Marcus Höglund I am using autofac in my project I put public static IContainer IoC { get; set; } in startup class . var builder = new ContainerBuilder(); builder.RegisterType() .As().InstancePerLifetimeScope(); var container = builder.Build(); Startup.IoC = container; But IContainer dont have any resolve method – Mussammil – Mussammil Jan 19 '18 at 09:54
  • You can add the namespace "Autofac.ResolutionExtensions" to the startup class which has a Resolve() method. – Marcus Höglund Jan 19 '18 at 13:00
  • @Marcus Höglund Autofac.ResolutionExtensions.Resolve has the signature public static TService Resolve(this IComponentContext context) ,so where did I get the IComponentContext.I cannot inject the IComponentContext in the startup class as well – Mussammil Jan 22 '18 at 06:38
  • Take a look at this answer here. After calling build on the ContainerBuilder it returns an IContainer. Store that in the Startup.cs and then call Resolve on that https://stackoverflow.com/questions/6262704/how-to-resolve-interface-based-on-service-where-its-passed-to – Marcus Höglund Jan 22 '18 at 07:06

1 Answers1

8

One solution is to store the dependecy resolver as a static variable in, for example, the Startup.cs class and then resolve the interface against that.

As you tagged this question with MVC I'm guessing you have the Global.asax file along with the Startup.cs class. The Global.asax will be executed before the Startup.cs and that we can use in this solution.

In this solution I use Unity as container but you could use what ever you like.

Declare the static variable in Startup.cs class

public partial class Startup
{
    public static UnityContainer IoC { get; set; }

    ...

Then in the WebApiConfig.Register() method, where the resolver is attached to the current HttpConfiguration, also set the variable in Startup.cs (Note that this will be called before the Startup.cs class)

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );

        var container = new UnityContainer();
        //register all your interfaces in the container
        container.RegisterType<IMembershipService, MembershipService>(new HierarchicalLifetimeManager());
        Startup.IoC = container;
        config.DependencyResolver = new UnityResolver(Startup.IoC);
    }
}

Then use it in your Startup.cs

public partial class Startup
 {
    public static UnityContainer IoC { get; set; }

    public void ConfigureAuth(IAppBuilder app)
    {
          OAuthOptions = new OAuthAuthorizationServerOptions
          {
            TokenEndpointPath = new PathString("/Token"),

            //Resolve the interface here
            Provider = new ApplicationOAuthProvider(PublicClientId, Startup.IoC.Resolve<IMembershipService>() ),

            AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
            // In production mode set AllowInsecureHttp = false
            AllowInsecureHttp = true
          };
    }
 }
Marcus Höglund
  • 16,172
  • 11
  • 47
  • 69