(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:
ConfigureAuth
twice?Authentication is the process of determining a user's identity. Authorization is the process of determining whether a user has access to a resource. In ASP.NET Core, authentication is handled by the authentication service, IAuthenticationService, which is used by authentication middleware.
Windows Authentication relies on the operating system to authenticate users of ASP.NET Core apps. You can use Windows Authentication when your server runs on a corporate network using Active Directory domain identities or Windows accounts to identify users.
Action. To restrict access for specific actions, add the attribute to the action method. public class StudentsController : ApiController{ public HttpResponseMessage Get() { ... } // Require authorization for a specific action. [Authorize] public HttpResponseMessage Post() { ... } }
The Authorize Attribute In ASP.NET MVC, any incoming request is bound to a controller/method pair and served. This means that once the request matches a supported route and is resolved to controller and method, it gets executed no matter what.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With