This project is read-only.

Using RegisterPerWebApiRequest for authorisation in an OWIN pipeline

Mar 24, 2014 at 3:15 PM
Hi,

I am having a little difficulty with using Simple Injector in an OWIN hosted Web API project that uses authentication.

I have some repositories that are injected into my Web API controllers. The repositories share a common Entity Framework DbContext that is also injected in. This all works fine but now I want to add authentication when a Web API request is made - I hook this into the OWIN framework using the Thinktecture library that gives me a callback from which I invoke a repository. The trouble is that if I use RegisterPerWebApiRequest<> for my EF context then I get an error "The MyEntities is registered as 'Web API Request' lifestyle, but the instance is requested outside the context of a Web API Request" even though it is being invoked as part of a Web API request. If I use RegisterPerWebRequest<> instead then all works well - but this needs an HttpContext that itself needs IIS (and also has some other issues around await) - so I really do want to use RegisterPerWebApiRequest<> instead. I suspect that OWIN is performing the authentication step before the Web API framework has been fired up (when hosting in IIS, something like IIS -> OWIN -> Web API but we could also self host without IIS and have no HttpContext : Self Host -> OWIN -> Web API).

Would this be solved by something like a new RegisterPerOwinRequest<> lifecycle ? If not, any other suggestions ? Apologies if I have overlooked something or if this has been addressed elsewhere.

Many thanks in advance,

Simon.

PS. In case it helps, here is some code that should help to illustrate the above.
//---   OWIN startup

public partial class Startup
{
    private HttpConfiguration   config_ = ...
        
    public void Configuration( IAppBuilder app )
    {       
        //---   Simple Injector setup

        container.Register< IUserRepository, UserRepository >();
        container.Register< ISomeRepository, SomeRepository >();
        container.RegisterPerWebApiRequest< MyEntities >();

        config_.DependencyResolver = new SimpleInjectorWebApiDependencyResolver( container );

        //---   OWIN setup
        
        app.UseBasicAuthentication( "some realm", ValidateUser );
        app.UseWebApi( config_ );
    }

    private Task<IEnumerable<Claim>> ValidateUser( string username, string password )
    {
        // fetch from Simple Injector container
        IUserRepository     user_repo = (IUserRepository)config_.DependencyResolver.GetService( typeof( IUserRepository ) );
        
        bool    ok = user_repo.Login( username, password );
        
        ...
    }
}

//---   Repositories

public class UserRepository : IUserRepository
{
    public UserRepository( MyEntities entities )
        ...
        
    public bool Login( string username, string password ) 
        ... do login based on MyEntities
}

public class SomeRepository : ISomeRepository
{
    public SomeRepository( MyEntities entities )
        ...
        
    public SomeEntity   Lookup( int id )
        ... fetch something from MyEntities
}

//---   Web API controller

[Authorize]
[RoutePrefix( "api/v1/something" )]
public SomeApiController : ApiController
{
    public SomeApiController( ISomeRepository )
        ...
        
    [Route( "{id:int}" )]
    public SomeEntity   Get( int id )
        ... use ISomeRepository to get the entity
}
Mar 24, 2014 at 4:07 PM
I think this Q/A solves your problem.
Marked as answer by dot_NET_Junkie on 4/15/2014 at 1:49 AM
Mar 31, 2014 at 9:06 AM
I have a similar issue with some other OWIN middleware.
Steven, I tried with you suggestion on SO, and that works fine for DelegatingHandlers, but the problem is that HttpRequestMessage is not available yet in OWIN middleware.

I agree with sime_george that maybe a solution could be to somehow register services pr. OWIN request ?
Mar 31, 2014 at 10:51 AM
Web API requests function around the notion of a dependency scope (defined by IDependencyScope) and this scope is created by the GetDependencyScope extension method and the scope is stored in the HttpRequestMessage.Properties dictionary (which is the Web API equivalence of the the HttpContext.Items dictionary).

An Web API dependency scope can only be started when an HttpRequestMessage is available. If there is no request message, there can't be no dependency scope and in that case there can't be any Web API request-scoped instances. So if your OWIN code runs outside the context of a Web API dependency scope, you can't use the WebApiRequestLifestyle.

The WebApiRequestLifestyle however is in fact just an ExecutionContextScopeLifestyle. The WebApiRequestLifestyle and its extension methods are just convenient names to make it easier for developers. The convenient part about the Web API integration is that the SimpleInjectorWebApiDependencyResolver begins and ends a new ExecutionContextScope for you. But since it seems that the OWIN pipeline still runs outside of the whole Web API infrastructure, there's nothing stopping you from beginning and ending a new ExecutionContextScope yourself.

I'm not sure where to hook this in into the OWIN pipeline, but this would basically be the way to begin/end an ExecutionContextScope:
// using SimpleInjector.Extensions.ExecutionContextScoping;

using (container.BeginExecutionContextScope())
{
    // resolve and use services here
}
Jul 2, 2014 at 3:16 PM
Edited Jul 2, 2014 at 3:18 PM
Hi, I know this is an old thread, but I had the same issue as the OP and solved it with this extension to the IAppBuilder:
public static void UseOwinContextInjector(this IAppBuilder app, Container container)
{
     // Create an OWIN middleware to create an execution context scope
     app.Use(async (context, next) =>
     {
         using (var scope = container.BeginExecutionContextScope())
         {
             await next.Invoke();
         }
     });
}
Then all I did was (in owin Startup class):
public partial class Startup
{
    public Container Container = null;

    public void Configuration(IAppBuilder app)
    {
        app.UseErrorPage();

        this.Container = new Container();

        // Since this creates an OWIN middleware, it must be invoked before registering
        // other OWIN middleware like auth.
        app.UseOwinContextInjector(Container);
   
        Container.Register<IDataContext>(() => new EFDataContext("DefaultConnection"), new ExecutionContextScopeLifestyle());
            
        ConfigureAuth(app);

        // This is a self hosted webapi project..
        HttpConfiguration apiConfig = new HttpConfiguration();

        // Set webapi dependency resolver
        apiConfig.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(this.Container);

        WebApiConfig.Register(apiConfig);

        app.UseWebApi(apiConfig);
    }
}
Now you can do something like this on the ApplicationOAuthProvider:
public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
            var dataContext = Startup.Container.GetInstance<IDataContext>();
        
            ......
    }
}
Hope this helps anyone who runs onto the same problem!
Jul 11, 2014 at 7:46 PM
Edited Jul 11, 2014 at 7:54 PM
I nicodega, I believe I am facingthe same problem.

I need to inject an IAuthenticationManager in my business layer, but I can not seem to get it to work, I get a stackoverflow exception:

An unhandled exception of type 'System.StackOverflowException' occurred in SimpleInjector.dll
at container.Verify();

I would probably need to inject HttpContext.Current.GetOwinContext().Authentication. but thats not possible at this pont. So what to do?
[assembly: OwinStartup(typeof(MyApp.Startup))]
namespace MyApp
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var container = new Container();

            app.UseOwinContextInjector(container);

            container.Register(container.GetInstance<IAuthenticationManager>); // I need this one in my AuthenticationService
            container.RegisterEntityFramework(new WebRequestLifestyle());
            container.RegisterSecurity();
            container.RegisterFluentValidation();
            container.RegisterCommandTransactions();
            container.RegisterQueryTransactions();
            container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
            container.RegisterMvcAttributeFilterProvider();
            container.Verify();

            ConfigureAuth(app);

            DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));

            FluentValidation.Mvc.FluentValidationModelValidatorProvider.Configure(
                provider =>
                {
                    provider.ValidatorFactory = new ValidatorFactory(container);
                    provider.AddImplicitRequiredValidator = false;
                }
            );
        }
    }
}
This is my AuthenticationService which needs the IAuthenticationManager (from OWIN)
public class AuthenticationService : IAuthenticationService
{
    private readonly IAuthenticationManager _authenticationManager;
    private readonly UserManager<User, int> _userManager;

    public AuthenticationService(IAuthenticationManager authenticationManager, UserManager<User, int> userManager)
    {
        _authenticationManager = authenticationManager;
        _userManager = userManager;
    }

    public async Task SignOn(User user, bool isPersistent = false)
    {
        _authenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
        _authenticationManager.SignIn(new AuthenticationProperties { IsPersistent = isPersistent }, 
            await _userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie));
    }

    public Task SignOff()
    {
        _authenticationManager.SignOut();
        return Task.FromResult(0);
    }

    public async Task<ExternalLoginInfo> GetExternalLoginInfo()
    {
        return await _authenticationManager.GetExternalLoginInfoAsync();
    }

    public async Task<IEnumerable<Claim>> GetExternalLoginClaims(string authenticationType = DefaultAuthenticationTypes.ExternalCookie)
    {
        var claimsIdentity = await _authenticationManager.GetExternalIdentityAsync(authenticationType);
        return claimsIdentity.Claims;
    }
}
You have any idea what I can do?


Some debugging info:
C:\Program Files (x86)\IIS Express\Microsoft.Owin.Host.SystemWeb.pdb: Cannot find or open the PDB file.
C:\Users\Jan\AppData\Local\Temp\Temporary ASP.NET Files\root\7796cb42\b787c074\assembly\dl3\6eb794c4\e47282df_7b94cf01\Microsoft.Owin.Host.SystemWeb.pdb: Cannot find or open the PDB file.
c:\BuildAgent\work\485425247c27f279\RuntimeRelease\src\Microsoft.Owin.Host.SystemWeb\obj\net45\Release\Microsoft.Owin.Host.SystemWeb.pdb: Cannot find or open the PDB file.
C:\windows\Microsoft.Owin.Host.SystemWeb.pdb: Cannot find or open the PDB file.
C:\windows\symbols\dll\Microsoft.Owin.Host.SystemWeb.pdb: Cannot find or open the PDB file.
C:\windows\dll\Microsoft.Owin.Host.SystemWeb.pdb: Cannot find or open the PDB file.
Jul 12, 2014 at 3:39 PM
Can you post the relevant part of the stacktrace that shows where the recursion is?

I also noticed this strange registration which is certainly not correct:
container.Register(container.GetInstance<IAuthenticationManager>);
Due to C# type inference, that is exactly the same as the following:
container.Register<IAuthenticationManager>(container.GetInstance<IAuthenticationManager>);
Which is functionally equivalent to:
container.Register<IAuthenticationManager>(() => container.GetInstance<IAuthenticationManager>());
This is a cyclic registration, but I would expect Simple Injector to throw an ActivationException explaining that "IAuthenticationManager is directly or indirectly depending on itself.".

If changing or removing this configuration fixes the Stackoverflow exception, I would really like to see the relevent part of the stacktrace, because there might be a bug in the cyclic dependency detection system that causes this stackoverflow exception. And if it's a bug, I do like to be able to fix it a.s.a.p.
Jul 12, 2014 at 7:18 PM
Hi dot_NET_Junkie.

Yes, the registering was wrong indeed, I've removed the code and can not seem to reproduce this stracktrace. I will let you know if I ever hit it again.

About my problem it seems like the OWIN context is first created on a request, so I am not able to inject it into my service at this point where I do this registration. Should I just do poor mans DI to inject the OWIN context into my service when needed then?
Jul 13, 2014 at 11:34 AM
Edited Jul 13, 2014 at 11:34 AM
I think I've solved my problem with this:
container.RegisterPerWebRequest(() =>
{
    if (HttpContext.Current != null && HttpContext.Current.Items["owin.Environment"] == null && container.IsVerifying())
    {
        return new OwinContext().Authentication;
    }
    return HttpContext.Current.GetOwinContext().Authentication;
   
});
Not sure if this is OK, I am completely new to this field :-)
Sep 1, 2014 at 10:07 PM
Here's another solution to the Simple Injector to OWIN integration.