piotrwalat.net

Basic HTTP authentication in ASP.NET Web API using message handlers

30 comments

In one of my previous posts I was investigating how to implement Basic HTTP authentication in ASP.NET Web API by extending AuthorizeAttribute (controller or action level filter). This approach has several disadvantages including quite messy implementation and necessity to couple concrete authentication mechanism with an abstract (i.e. authentication mechanism agnostic) [Authorize] attribute.

To address these problems we can implement a custom message handler (by deriving from DelegateHandler class) that will provide Basic HTTP authentication mechanism.

The actual work done by the handler will be twofold:

  1. to intercept HttpRequestMessage and check for Authorization header being present (in which case we want to authenticate user with credentials provided)
  2. to intercept HttpResponseMessage and make sure that for every 401 http response we inform client of expected authentication method (by including WWW-Authenticate header in http response)

Remember that Basic HTTP authentication sends credentials in plaintext (unencrypted) which means you must use secure transport layer (SSL) to provide encryption.

Let’s jump into code as it is pretty straightforward. To implement custom message handler, we simply derive DelegatingHandler class and override SendAsync(HttpRequestMessage request,CancellationToken cancellationToken) method.

 

In SendAsync() we check for the presence of http auth header in the request, if it is present we parse it (ParseAuthorizationHeader()) to extract username and password. Once this is done we use IProvidePrincipal implementation (custom interface), which should be provided by whoever is instantiating our message handler (either manually or by dependency injection). We use this approach as we don’t want our credential verification mechanism (be it SQL Server membership provider, flat text file or some kind of enterprise security service) to be baked into the code responsible for http authentication.

Once the principal is retrieved we set Thread.CurrentPrincipal property. This is actually a property used by Web API’s AuthorizeAttribute to verify if user has been authorized or not. Please note that we are not changing Web.HttpContext.Current.User (used by MVC AuthorizeAttribute).

Let’s create a simple IProvidePrincipal implementation for testing purposes:

Once we have all of above in place, we can simply add BasicAuthMessageHandler instance to web app global configuration:

From now on, whenever we use Authorize attribute, our handler will intercept and modify 401 http response and make the browser present the user with a standard login dialog.

Once the user enters credentials we authenticate him against mechanism of our choice. We don’t have to modify any of our existing controllers nor actions and thus is a much cleaner solution that custom action filters.

You can find source code here (.zip archive here).

 

Written by Piotr Walat

August 22nd, 2012 at 9:09 am

  • WbAPi

    The type or namespace name Credentials could not be found.

  • WbAPi

    Nice article.

    I created a web api project with .Net framework 4.0, under BasicAuthMessageHandler “Credentials” does not resolve. It complains that “type or namespace name ‘Credentials’ could not be found”.

    What am I missing here? Also, it would be nice if you could provide working project with the source code.

    Thanks. 

    • http://www.piotrwalat.net/ Piotr Walat

      Forgot to add Credentials class:
      public class Credentials
      {
          public string Username {get;set;}
          public string Password {get;set;}
      }

      I will add source code soon, stay tuned :)

      • WbAPi

        Piotr,

        I am still getting an error on statement
        public IProvidePrincipal PrincipalProvider { get; set; }

        Inconsistent accessibility: property type ‘BasicAuth.IProvidePrincipal’ is less accessible than property ‘BasicAuth.BasicAuthMessageHandler.PrincipalProvider’

        Thanks.

        • http://www.piotrwalat.net/ Piotr Walat

          Hmm do you have “public” qualifier before “interface IProvidePrincipal” (“public interface IProvidePrincipal”) ?Please check source code included and let me know if you still have problems :)

          • WbAPi

            That fixed the issue, thanks for sharing the source code.

  • Pingback: Interesting .NET Links - August 23 , 2012 | TechBlog

  • Pingback: Distributed Weekly 169 — Scott Banwart's Blog

  • http://twitter.com/nacho10f Ignacio Fuentes

    Wow. much cleaner than the previous approach. Great Job!

  • http://www.dotnetjalps.com/ Jalpesh Vadgama

    Hi can we use form authentication and other authentication with Web API. My intention is  to give particular user only particular rights?. For example I have 3 web apis then x user can use only 2 and y user can use all three.

  • Jeremy Sheldon

    Would love to see a followup posting using a custom Membership and Role provider in WebAPI.  Would like to call a Login() WebAPI method from a client application which returns a valid customer Membership profile which allows me to show/hide sections of the client application based on the user’s Role.

  • Jeremy Kruer

    I implemented this solution and when I test it locally everything works great.  However, when I deploy it to our QA server (Running IIS6) the authentication does not work (everything else does).  When I enter the un/pw it just keeps popping up the login box again.

    I added some logging into the BasicAuthMessageHandler class.  When I run it locally, this class gets called twice (once before the login popup shows up (without any Authorization header) and once after (with UN/PW)).  However, when I roll it to QA environment (IIS 6), it is only called once before the login popup (without any Authorization Header).  After that it never gets called again regardless of how many times I enter the Username and password. 

    Any ideas, help, suggestions?

    I appreciate it! Thanks!

    • http://www.facebook.com/profile.php?id=100002562434494 Joshua Barrett

      I am having this same issue… any solution thus far?

      • Fabian Betancur

        I have this same problem. Any ideas? Also works locally but doens’t work on server with iis6

    • http://www.facebook.com/profile.php?id=100002562434494 Joshua Barrett

      Not sure if it relates to you, but I had this same issue on my maxASP(now cbeyond) webhost.  it worked fine when I moved it to windows azure

    • http://www.facebook.com/lucasbpassos Lucas Belo Passos

      I’m having the same problem.

    • Fabian Betancur

      I also tried it on IIS7 with Server 2008. Same problem.

    • Jeremy Kruer

      I had the exact same problem.  Turns out that I had IIS Authentication turned on for my Web API website.  I turned off IIS Authentication and everything worked.  In IIS right click your website > Properties > Directory Security > Authentication and access control > Edit > Uncheck all Authentication access methods.

      • http://twitter.com/leemuro Lee Muro

        Is there an equivalent to this for IIS7?  I am having the same problem.

  • Alex Tercete

    Great article. Just one observation, the error code for Unauthorized is 401, not 403.

    • http://www.piotrwalat.net/ Piotr Walat

      Thanks ,corrected

  • Tom Eck

    If you want to use the standard ASP.NET Membership and Role providers, just change DummyPrincipalProvider.CreatePrincipal to look like this:

            public IPrincipal CreatePrincipal(string username, string password)        {            if (!Membership.ValidateUser(username, password))            {                return null;            }            var identity = new GenericIdentity(username, “Basic”);            string[] roles = Roles.Provider.GetRolesForUser(username);            var principal = new GenericPrincipal(identity, roles);            return principal;        }

  • andrewbschultz

    Piotr – the entry for adding the BasicAuthMessageHandler to the Global.asax.Aplication_Start method is throwing the following error in MVC4 Web API running in an Azure Web Role. Have you seen this?

    Inheritance security rules violated by type: ‘System.Net.Http.Formatting.JsonContractResolver’. Derived types must either match the security accessibility of the base type or be less accessible.

  • mfraser27

    This is working great for me BUT I also need to implement a Role Provider which should be trivial.  It gets initialized but the methods in it never get called – could this be because of the message handler I am using here?

  • Hardeep Chagger

    How would I go about getting the user (username) once the authentication is passed in a controller action?

  • Ron Wang

    Hi Piotr,
    Very nice post. Can you provide some info how to integrate with simpleMembership provided by MVC4 by default?
    Thanks

  • tarini.venugopal

    Hello Piotr Walat,
    Can we use Windows Authentication with ASP.Net membership provider for asp.net web api ?? How one api will authenticate with other api which is also windows identity enabled ??

  • daniele fusi

    Great post, thank you! As a noob here, I’d like to ask what you would suggest for integrating your solution into an MVC4 WebSecurity-based web app. I have an MVC4 web app exposing its UI to authenticated users, but I’d like to expose some of its functions via webapi for authorized clients only. The clients would be JS scripts (probably via jQuery ajax calls) and mobile apps, which thus cannot rely on the cookies used when accessing the webapp with a browser. So I’d like to use the same membership data and roles used by WebSecurity for webapi authentication: how could I create a principal provider for integrating your code? Or could you suggest other approaches?

  • Pingback: HMAC authentication in ASP.NET Web API at piotrwalat.net