سؤال

I have a ASP.Net WebAPI 2.1 that I have just transitioned across to using Identity 2.0 using bearer tokens. This works fine. Now I am attempting to pull in some MVC code to create a set of login and user management pages. My issue is that I can't seem to get Request.IsAuthenticated to work from my Razor views when I set the WebApi HttpConfigurationto SuppressDefaultHostAuthentication.

Below is my code, and I'm out of ideas as to how I can get this to work for both scenarios :(

Here's my Startup.cs which sets up the Identity OWIN module, and the WebAPI:

public class Startup
{
    public void Configure(IAppBuilder app)
    {
        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);

        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/token"),
            Provider = new ApplicationOAuthProvider(PublicClientId),
            AuthorizeEndpointPath = new PathString("/api/account/externalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14)
        };
        app.UseOAuthBearerTokens(OAuthOptions);

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            Provider = new CookieAuthenticationProvider
            {
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager, DefaultAuthenticationTypes.ApplicationCookie))
            }
        });

        var httpConfiguration = new HttpConfiguration();
        // Disable this line to allow Request.IsAuthenticated to work
        // But by doing this, it allows the 'redirect' to kick in on unauthenticated API requests, which returns a HTML page for a webapi call, rather than the JSON 'unauthenticated' response
        httpConfiguration.SuppressDefaultHostAuthentication();
        httpConfiguration.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ApplicationCookie));
        httpConfiguration.MapHttpAttributeRoutes();
        app.UseWebApi(httpConfiguration);
    }
}

Here's my Global.asax.cs, which sets up the MVC side of things (AFAIK OWIN doesn't support any form of app.UseMvc()):

public class WebApiApplication : HttpApplication
{
    protected void Application_Start()
    {
        // pretty much the defaults here for everything, just renamed
        AreaRegistration.RegisterAllAreas();
        MvcConfig.ConfigureFilters(GlobalFilters.Filters);
        MvcConfig.ConfigureRoutes(RouteTable.Routes);
        MvcConfig.ConfigureBundles(BundleTable.Bundles);
    }
}

Now in my Razor views, I would like to use Request.IsAuthenticated, as used in the Identity samples, but this fails when the httpConfiguration.SuppressDefaultHostAuthentication is enabled. I understand the goal of this extension is to remove the current identity after the Identity middleware has run -- so that the WebAPI authentication filter can do as it pleases. But I am hoping that on the MVC side of things, that the suppression wouldn't occur.

Example Razor view:

@if (Request.IsAuthenticated) // false when using httpConfiguration.SuppressDefaultHostAuthentication
{
  <div>User.Identity.Email</div>
}

Can anyone help me? Is this even possible?

Thanks!

هل كانت مفيدة؟

المحلول

Looks like it's all about the ordering of the app builder. If I place the Identity bearer configuration before the WebAPI, then my WebAPI requests still utilize the Identity OWIN module. By placing the Cookie configuration after the WebAPI configuration, the Cookie Identity resolution occurs after the WebAPI identity removal, and before the MVC execution.

Unsure if this is the 'correct' way to go about doing it, but it seems to solve all of the test cases I have open.

public class Startup
{
    public void Configure(IAppBuilder app)
    {
        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);

        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/token"),
            Provider = new ApplicationOAuthProvider(PublicClientId),
            AuthorizeEndpointPath = new PathString("/api/account/externalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14)
        };
        app.UseOAuthBearerTokens(OAuthOptions);

        var httpConfiguration = new HttpConfiguration();
        httpConfiguration.SuppressDefaultHostAuthentication();
        httpConfiguration.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ApplicationCookie));
        httpConfiguration.MapHttpAttributeRoutes();
        app.UseWebApi(httpConfiguration);

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            Provider = new CookieAuthenticationProvider
            {
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager, DefaultAuthenticationTypes.ApplicationCookie))
            }
        });
    }
}

EDIT The above works, but it seems to be better to utilize the app.MapWhen() functionality to do this.

public class Startup
{
    public void Configure(IAppBuilder app)
    {
        // setup auth for all requests
        app.CreatePerOwinContext(ApplicationDbContext.Create);
        app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);

        PublicClientId = "self";
        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/token"),
            Provider = new ApplicationOAuthProvider(PublicClientId),
            AuthorizeEndpointPath = new PathString("/api/account/externalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(14)
        };
        app.UseOAuthBearerTokens(OAuthOptions);

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            Provider = new CookieAuthenticationProvider
            {
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager, DefaultAuthenticationTypes.ApplicationCookie))
            }
        });

        // setup webapi for only /api requests
        app.MapWhen(
            context => context.Request.Uri.PathAndQuery.StartsWith("/api"),
            newApp => {
                var httpConfiguration = new HttpConfiguration();
                httpConfiguration.SuppressDefaultHostAuthentication();
                httpConfiguration.Filters.Add(new HostAuthenticationFilter(DefaultAuthenticationTypes.ApplicationCookie));
                httpConfiguration.MapHttpAttributeRoutes();
                app.UseWebApi(httpConfiguration);
            }
    }
}    
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top