Supertext Home
Chief of the System Blog

Basic Http Authorization for Web API in MVC 4 Beta

April 19th, 2012 by

A little while ago I posted a solution to do Basic Http Authorization with the Web API Preview 6. Web API got then merged into the next ASP.NET MVC 4 Beta Release and in the process has changed a lot.

Since my old approach did not work anymore, I had to create something new.

 

Usage:

public class OrderController : ApiController
{
    // GET /api/orders/5
    [BasicHttpAuthorizeAttribute(RequireAuthentication = true)]
    public string Get(int id, string communicationLang)
    {
        //do your API Stuff
    }
}

And the Authentication class itself:

public class BasicHttpAuthorizeAttribute : System.Web.Http.AuthorizeAttribute  
{
    bool requireSsl = Convert.ToBoolean(ConfigurationManager.AppSettings["RequireSsl"]);
 
    public bool RequireSsl
    {
        get { return requireSsl; }
        set { requireSsl = value; }
    }
 
 
    bool requireAuthentication = true;
 
    public bool RequireAuthentication
    {
        get { return requireAuthentication; }
        set { requireAuthentication = value; }
    }
 
 
    /// <summary>
    /// For logging with Log4net.
    /// </summary>
    private static readonly ILog log = LogManager.GetLogger(typeof(BasicHttpAuthorizeAttribute));
 
 
    public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)        
    {
        //actionContext.Request
 
        if (Authenticate(actionContext) || !RequireAuthentication)
        {
            return;
        }
        else
        {
            HandleUnauthorizedRequest(actionContext);
        }
    }
 
    protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        var challengeMessage = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
        challengeMessage.Headers.Add("WWW-Authenticate", "Basic");
        throw new HttpResponseException(challengeMessage);
        //throw new HttpResponseException();
    }
 
 
    private bool Authenticate(System.Web.Http.Controllers.HttpActionContext actionContext) //HttpRequestMessage input)
    {
        if (RequireSsl && !HttpContext.Current.Request.IsSecureConnection && !HttpContext.Current.Request.IsLocal)
        {
            log.Error("Failed to login: SSL:" + HttpContext.Current.Request.IsSecureConnection);
            return false;
        }
 
        if (!HttpContext.Current.Request.Headers.AllKeys.Contains("Authorization")) return false;
 
        string authHeader = HttpContext.Current.Request.Headers["Authorization"];
 
        IPrincipal principal;
        if (TryGetPrincipal(authHeader, out principal))
        {
            HttpContext.Current.User = principal;
            return true;
        }
        return false;
    }
 
 
    private bool TryGetPrincipal(string authHeader, out IPrincipal principal)
    {
        var creds = ParseAuthHeader(authHeader);
        if (creds != null)
        {
            if (TryGetPrincipal(creds[0], creds[1], out principal)) return true;
        }
 
        principal = null;
        return false;
    }
 
 
    private string[] ParseAuthHeader(string authHeader)
    {
        // Check this is a Basic Auth header 
        if (authHeader == null || authHeader.Length == 0 || !authHeader.StartsWith("Basic")) return null;
 
        // Pull out the Credentials with are seperated by ':' and Base64 encoded 
        string base64Credentials = authHeader.Substring(6);
        string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(base64Credentials)).Split(new char[] { ':' });
 
        if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0]) || string.IsNullOrEmpty(credentials[0])) return null;
 
        // Okay this is the credentials 
        return credentials;
    }
 
 
    private bool TryGetPrincipal(string username, string password, out IPrincipal principal)
    {
        // this is the method that does the authentication 
 
        //users often add a copy/paste space at the end of the username
        username = username.Trim();
        password = password.Trim();
 
        //TODO
        //Replace this with your own Authentication Code
        Person person = AccountManagement.ApiLogin(username, password);
 
        if (person != null)
        {
            // once the user is verified, assign it to an IPrincipal with the identity name and applicable roles
            principal = new GenericPrincipal(new GenericIdentity(username), System.Web.Security.Roles.GetRolesForUser(username));
            return true;
        }
        else
        {
            if (!String.IsNullOrWhiteSpace(username))
            {
                log.Error("Failed to login: username=" + username + "; password=" + password);
            }
            principal = null;
            return false;
        }
    }
}

You will have to adjust the TryGetPrincipal() method to include our own Authorization code that works with your system. Or maybe plain normal ASP.NET Provider Authentication is enough in your case.

You can download the code from github:
https://github.com/rblaettler/BasicHttpAuthorization

Related Posts

  1. Basic Authentication with WCF Web API Preview 6
  2. The Supertext REST API
  3. See search results as you type – An ASP.NET Ajax Control
  4. Find checked Radio Button in ASP.NET

12 Comments to “Basic Http Authorization for Web API in MVC 4 Beta”

  • Ivan says on June 9th, 2012 at 11:31 am :

    Puting authentication in my rest service it has been a nightmare. Thanks a lot for your post.

  • Chris says on June 24th, 2012 at 4:25 pm :

    This code works fine under the built in web server in Visual Studio 2010 but it does NOT work under IIS 7.5 by default.

    To get it to work under IIS 7.5, you need to turn off all authentication mechanisms for your website within IIS EXCEPT “Anonymous Authentication”. See http://technet.microsoft.com/en-us/library/cc733010%28v=ws.10%29.aspx

    This class is basically rolling its own basic http authentication and having IIS’s auth mechanisms enabled seems to cause a conflict.

  • Rémy Blättler says on June 25th, 2012 at 3:19 am :

    Thanks for your hint. I forgot to mention that.

  • george says on July 3rd, 2012 at 5:37 am :

    Two things.

    1. I’m never getting a value of True for RequireAuthentication . It’s always false.

    2. On one of my controllers the OnAuthorization method gets called twice.

    why?

  • Rémy Blättler says on July 3rd, 2012 at 5:41 am :

    You’ve set it up like this:
    [BasicHttpAuthorizeAttribute(RequireAuthentication = true)]

    And this does not come through? Cause this is just basic framework functionality.

  • george says on July 9th, 2012 at 2:43 am :

    I had an unrelated problem. Works great.

  • Sam says on July 19th, 2012 at 12:15 pm :

    Thanks for the post this has been very helpful.

    I am having one issue. I set HttpContext.Current.User = principal in the Authenticate however in my controller this.User.Identity in not set to the principalI set – it is basically blank (IsAuthnticated = False and Name = “”). This is a problem as I need this information in my controller.

    Is something overwriting it?

  • Rémy Blättler says on July 23rd, 2012 at 9:38 pm :

    Not that I know of. Did you try Membership.GetUser()?

  • Sam says on July 24th, 2012 at 10:56 am :

    Unfortunately I am not using the membership provider. I am using a custom authentication solution and I would like the assign the username of the authenticated user to HttpContext.Current.User.

  • Rémy Blättler says on July 24th, 2012 at 2:05 pm :

    Right. That should be possible where I do this:

    TryGetPrincipal()

    Just replace my authentication code with your solution.

  • Basic Authentication with WCF Web API Preview 6 | Chief of the System Blog says on July 30th, 2012 at 5:56 pm :

    [...] Basic Http Authorization for Web API in MVC 4 Beta [...]

  • Lelala says on April 4th, 2013 at 11:03 am :

    Wow, better example than the one supplied by Microsoft themselves. Thanks for shortening their version :-)

Leave a Reply

  • Topics
  • Archive
  • Subscribe
  • Facebook
  • Twitter