Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keep using cookies for authentication in Web API with OWIN and app.UseWebApi

I'd like to keep using same cookies in MVC and API parts of my app. I know this isn't very secure but still. Everything works if I create a new MVC project in VS, Web API is set up from Global.asax using GlobalConfiguration.Configure(WebApiConfig.Register). But as soon as I'm trying to use OWIN to configure Web API I run into a problem where User is always null in my API controllers.

Here's my code from Startup.cs:

var config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseWebApi(config);

Controllers work, routes too, same WebApiConfig.cs file is used. However the User is null in my API controllers now. What's missing from my instance of HttpConfiguration that is present in GlobalConfiguration.Configuration?

I need to use my own instance of HttpConfiguration instead of using GlobalConfiguration.Configuration because I'm planning to use Autofac and it doesn't work with GlobalConfiguration as mentioned here

EDIT:

My Startup.Auth.cs:

// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/Account/Login"),
    Provider = new CookieAuthenticationProvider
    {
        // Enables the application to validate the security stamp when the user logs in.
        // This is a security feature which is used when you change a password or add an external login to your account.  
        OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
            validateInterval: TimeSpan.FromMinutes(30),
            regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
    }
});    
like image 952
devmiles.com Avatar asked May 07 '16 11:05

devmiles.com


People also ask

What is Owin based authentication?

OWIN (Open Web Interface for . NET) is a standard for an interface between . NET Web applications and Web servers. It is a community-owned open-source project. The OAuth authorization framework enables a third-party application to obtain limited access to a HTTP service.

Can I use cookies for authentication and authorization in a WebAPI?

That’s not the case. You can do authentication and authorization in a Web Api using cookies the same way you would for a normal web application, and doing so has the added advantage that cookies are easier to setup than for example JWT tokens. There are just a few things you need to be aware.

Do I need to use tokens to secure a web API?

There’s this frequent notion that you need to use tokens to secure a web api and you can’t use cookies. That’s not the case. You can do authentication and authorization in a Web Api using cookies the same way you would for a normal web application, and doing so has the added advantage that cookies are easier to setup than for example JWT tokens.

How do I add cookies to a web API response?

Cookies in Web API. To add a cookie to an HTTP response, create a CookieHeaderValue instance that represents the cookie. Then call the AddCookies extension method, which is defined in the System.Net.Http.

What do I need to do server-side to enable Cookie authentication?

What you need to do server-side is to configure ASP.NET’s cookie authentication middleware and also setup CORS so that your Web Api “declares” it accepts requests from the domain where your client is hosted. To setup the cookie middleware you have to setup the authentication middleware in your Startup.cs ‘ ConfigurateServices method:


2 Answers

What's missing from my instance of HttpConfiguration that is present in GlobalConfiguration.Configuration?

GlobalConfiguration.cs Source code from Codeplex

The main difference between when you create a new HttpConfiguration and the one In GlobalConfiguration...

public static class GlobalConfiguration
{
    private static Lazy<HttpConfiguration> _configuration = CreateConfiguration();

    //...other code removed for brevity

    /// <summary>
    /// Gets the global <see cref="T:System.Web.Http.HttpConfiguration"/>.
    /// </summary>
    public static HttpConfiguration Configuration
    {
        get { return _configuration.Value; }
    }

    //...other code removed for brevity

    private static Lazy<HttpConfiguration> CreateConfiguration()
    {
        return new Lazy<HttpConfiguration>(() =>
        {
            HttpConfiguration config = new HttpConfiguration(new HostedHttpRouteCollection(RouteTable.Routes));
            ServicesContainer services = config.Services;
            Contract.Assert(services != null);
            services.Replace(typeof(IAssembliesResolver), new WebHostAssembliesResolver());
            services.Replace(typeof(IHttpControllerTypeResolver), new WebHostHttpControllerTypeResolver());
            services.Replace(typeof(IHostBufferPolicySelector), new WebHostBufferPolicySelector());
            services.Replace(typeof(IExceptionHandler),
                new WebHostExceptionHandler(services.GetExceptionHandler()));
            return config;
        });
    }

    //...other code removed for brevity
}

Also when looking at how the UseWebAPi extension in WebApiAppBuilderExtensions.cs

public static IAppBuilder UseWebApi(this IAppBuilder builder, HttpConfiguration configuration)
{
    if (builder == null)
    {
        throw new ArgumentNullException("builder");
    }

    if (configuration == null)
    {
        throw new ArgumentNullException("configuration");
    }

    HttpServer server = new HttpServer(configuration);

    try
    {
        HttpMessageHandlerOptions options = CreateOptions(builder, server, configuration);
        return UseMessageHandler(builder, options);
    }
    catch
    {
        server.Dispose();
        throw;
    }
}

...the configuration is wrapped in its own HttpServer which overrides the default one used by GlobalConfiguration.

Looking through the documentation you included, I eventually came across this

For standard IIS hosting, the HttpConfiguration is GlobalConfiguration.Configuration.

For self hosting, the HttpConfiguration is your HttpSelfHostConfiguration instance.

For OWIN integration, the HttpConfiguration is the one you create in your app startup class and pass to the Web API middleware.

With standard IIS hosting, IIS handles user Authentication and Identification which it plugs into the HttpConfiguration and pipeline under the hood for you. When you new up HttpConfiguration your self you don't have the benefits of IIS to manage Authentication for you so your User remains null.

From your post you indicate that you are using more than one instance of HttpConfiguration which looks like you are trying to mix IIS and OWIN.

Looking at this question : OWIN Cookie Authentication

The answer shows that in the WebApi Config the following line was ignoring the cookie.

// Configure Web API to use only bearer token authentication.
// If you don't want the OWIN authentication to flow to your Web API then call 
// SuppressDefaultHostAuthentication on your HttpConfiguration. 
// This blocks all host level authentication at that point in the pipeline.
config.SuppressDefaultHostAuthentication();

Commenting it out made the cookie based Authentication work.

UPDATE:

You indicated...

Controllers work, routes too, same WebApiConfig.cs file is used. However the User is null in my API controllers now

Take a look at...

Combining Authentication Filters with Host-Level Authentication

“Host-level authentication” is authentication performed by the host (such as IIS), before the request reaches the Web API framework.

Often, you may want to to enable host-level authentication for the rest of your application, but disable it for your Web API controllers. For example, a typical scenario is to enable Forms Authentication at the host level, but use token-based authentication for Web API.

To disable host-level authentication inside the Web API pipeline, call config.SuppressHostPrincipal() in your configuration. This causes Web API to remove the IPrincipal from any request that enters the Web API pipeline. Effectively, it "un-authenticates" the request.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.SuppressHostPrincipal();

        // Other configuration code not shown...
    }
}

If in your scenario you have the following in your web api configuration, it would explain why your User is always null. I suggest you comment it out or remove it all together.

like image 171
Nkosi Avatar answered Oct 20 '22 00:10

Nkosi


I had exactly this problem when I transferred to OWIN from a WebApi only service. My user was also null even though it was correctly authenticated. In my case I had missed adding the HostAuthenticationFilter in after suppressing the Default Host Authentication.

config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(YourAuthenticationType));

Check that you have not forgotten your authentication filter here. I've done this successfully using Unity (rather than Autofac) but the principal is exactly the same. In my OWIN startup this order:

ConfigureAuth(app);
WebApiConfig.Register(httpConfiguration);
app.UseWebApi(httpConfiguration);
like image 20
The Senator Avatar answered Oct 19 '22 23:10

The Senator