简体   繁体   中英

Authorization with ASP.NET Identity & Autofac OWIN integration

(an update has been added at the bottom of this question)

I have a web application that uses both MVC5 and WebAPI2 with Autofac for DI. The app uses ASP.NET Identity and oAuth bearer tokens, though the latter may be beside the point. This has all worked perfectly well, but at this point I need the same instances of my injected services to be shared throughout the OWIN pipeline as well as the rest of my application and therefore I'm trying to setup Autofac's OWIN integrations for MVC and Web API. I seem to be close- everything seems to work except for AuthorizeAttibutes on ApiControllers . The oAuth process completes successfully and I end up signed in with a bearer token, but subsequent attempts to authorize with said token on WebAPI controllers/actions fail.

Specifically, in the IsAuthorized method of System.Web.Http.AuthorizeAttribute , IPrincipal.Identity seems that it has not been instantiated correctly in that it doesn't have the appropriate claims and the IsAuthenticated property is always false. The developers of Autofac indicate that this attribute should work with the OWIN integrations , even though that code uses GlobalConfiguration which is not advisable for the OWIN integrations . I've seen multiple recommendations to remove config.SuppressDefaultHostAuthentication() ( here and here ), which, while not advisable, I've tried out of desperation and yet to no avail- for my particular configuration this causes IPrincipal to come back as null. I've also tried to modify a much simpler example project than my own to use AuthorizeAttribute on the WebAPI controller, also with no success. At this point I'm out of things to try, and help would be greatly appreciated.

Here is my Startup.cs:

[assembly: OwinStartup(typeof (Startup))]
namespace Project.Web
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            var builder = new ContainerBuilder();
            builder.RegisterControllers(Assembly.GetExecutingAssembly());
            var config = new HttpConfiguration();
            builder.RegisterHttpRequestMessage(config);
            builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
            RegisterGeneralTypes(builder);
            var container = builder.Build();
            WebApiConfig.Register(config);
            config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
            DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
            WebApiFilterConfig.RegisterGlobalFilters(config.Filters);

            app.UseAutofacMiddleware(container);
            app.UseAutofacWebApi(config);
            app.UseAutofacMvc();
            app.UseWebApi(config);

            ConfigureAuth(app);
        }

        private static void RegisterGeneralTypes(ContainerBuilder builder)
        {
            builder.Register(c => new DomainModelContext())
                .AsSelf()
                .InstancePerRequest();

            builder.Register(c => HttpContext.Current.User.Identity)
                .As(typeof (IIdentity));

            builder.RegisterType<EmailService>()
                .AsImplementedInterfaces()
                .InstancePerRequest();

            builder.Register(c => new IdentityFactoryOptions<DomainUserManager>
            {
                DataProtectionProvider = DataProtectionProvider
            }).InstancePerRequest();

            builder.RegisterType<DomainUserManager>()
                .AsSelf()
                .UsingConstructor(typeof (IIdentityMessageService),
                    typeof (IdentityFactoryOptions<DomainUserManager>),
                    typeof (CustomUserStore))
                .InstancePerRequest();

            builder.RegisterType<CustomUserStore>()
                .AsImplementedInterfaces()
                .AsSelf()
                .InstancePerRequest();

            builder.Register(c => HttpContext.Current.GetOwinContext().Authentication)
                .As<IAuthenticationManager>();
        }
    }
}

and my Startup.Auth.cs:

public partial class Startup
{
    internal static IDataProtectionProvider DataProtectionProvider;
    public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
    public static string PublicClientId { get; private set; }

    public void ConfigureAuth(IAppBuilder app)
    {
        var onValidateIdentity = SecurityStampValidator
            .OnValidateIdentity<DomainUserManager, DomainUser, int>(
                validateInterval: TimeSpan.FromMinutes(30),
                regenerateIdentityCallback: (manager, user) =>
                    user.GenerateUserIdentityAsync(manager, CookieAuthenticationDefaults.AuthenticationType),
                getUserIdCallback: id => id.GetUserId<int>());

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            LoginPath = new PathString("/account/login"),

            Provider = new CookieAuthenticationProvider
            {
                OnValidateIdentity = onValidateIdentity
            }
        });
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

        // Configure the application for OAuth based flow
        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/token"),
            Provider = new ApplicationOAuthProvider(PublicClientId),
            AuthorizeEndpointPath = new PathString("/api/v1/account/externallogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
        };

        // Enable the application to use bearer tokens to authenticate users
        app.UseOAuthBearerTokens(OAuthOptions);

        DataProtectionProvider = app.GetDataProtectionProvider();
    }
}

I think that covers it, but I will gladly post additional code upon request.

UPDATE

So based on jumuro's answer , I changed my order of registrations as recommended. However, this simply transferred the exact same issue from Web API authorization to MVC authorization. Since I had MVC auth working before the update, I ultimately tried registering auth in the pipeline twice as follows:

app.UseAutofacMiddleware(container);
ConfigureAuth(app);
app.UseAutofacWebApi(config);
app.UseAutofacMvc();
app.UseWebApi(config);
ConfigureAuth(app);

This works, but I really can't say that I understand why and I can't imagine that it's good to be doing this twice. So now I have new questions:

  1. It makes sense that WebAPI would need Auth registered in the pipeline first, but why in the world does MVC want me to register Auth last ?
  2. How can I clean this up and avoid calling ConfigureAuth twice?

You have to add middlewares to the application pipeline in the correct order. Bearer token has to be validated before the MVC and Web Api middlewares process the request.

Try this order in your Configuration() method:

public void Configuration(IAppBuilder app)
{
    ...
    app.UseAutofacMiddleware(container);
    ConfigureAuth(app);
    app.UseAutofacMvc();
    app.UseWebApi(config);
    app.UseAutofacWebApi(config);
    ...
}

I hope it 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