December 01 2014

Web API 2 + Angular - Basic HTTP Authentication Example

Following from a previous post showing an example of how to setup a login using Basic HTTP Authentication with AngularJS, in this post I'll show how to implement the server side of the equation - Basic HTTP Authentication using ASP.NET Web API 2.

The entire solution including the Web API 2 and AngularJS projects is available on GitHub at https://github.com/cornflourblue/angular-webapi-authentication-example.

Even though the example uses AngularJS on the front end, any front end technology could be used with the Web API layer such as ReactJS, Android or iOS native apps.

How to Setup the Login Example in IIS

To get the login example working on your machine first download the solution from GitHub and compile it in Visual Studio 2013, then:

  1. Open IIS Manager (I'm using IIS 7.5 but this should all work for IIS 8/8.5 as well)
  2. Right click the Sites folder and select Add Web Site...
  3. Set the Site name to anything you like (e.g. Angular Web API Example)
  4. Set the Physical path to the location to the root directory of the AngularJS project (e.g. C:\Projects\GitHub\angular-webapi-authentication-example\AngularWebAPI.FrontEnd)
  5. Set the Host name to "localhost" (without quotes)
  6. Click OK
  7. Make sure the application pool for the new site is running .NET Framework version 4 or later.
  8. Right click the new website and select Add Application
  9. Set the Alias to "api" (without quotes)
  10. Set the Physical path to the root directory of the ASP.NET Web API 2 project (e.g. C:\Projects\GitHub\angular-webapi-authentication-example\AngularWebAPI.WebAPI).
 

Web API 2 BasicAuthHttpModule

The BasicAuthHttpModule is a custom HTTP Module that reads the Authorization header and authenticates the username and password for any API endpoints that require authorization (controller actions that are decorated with the [Authorize] attribute). It's added to the request pipeline in the web.config file.

public class BasicAuthHttpModule : IHttpModule
{
    private const string Realm = "AngularWebAPI";

    public void Init(HttpApplication context)
    {
        // Register event handlers
        context.AuthenticateRequest += OnApplicationAuthenticateRequest;
        context.EndRequest += OnApplicationEndRequest;
    }
        
    private static void SetPrincipal(IPrincipal principal)
    {
        Thread.CurrentPrincipal = principal;
        if (HttpContext.Current != null)
        {
            HttpContext.Current.User = principal;
        }
    }

    private static bool AuthenticateUser(string credentials)
    {
        var encoding = Encoding.GetEncoding("iso-8859-1");
        credentials = encoding.GetString(Convert.FromBase64String(credentials));

        var credentialsArray = credentials.Split(':');
        var username = credentialsArray[0];
        var password = credentialsArray[1];

        /* REPLACE THIS WITH REAL AUTHENTICATION
        ----------------------------------------------*/
        if (!(username == "test" && password == "test"))
        {
            return false;
        }

        var identity = new GenericIdentity(username);
        SetPrincipal(new GenericPrincipal(identity, null));

        return true;
    }

    private static void OnApplicationAuthenticateRequest(object sender, EventArgs e)
    {
        var request = HttpContext.Current.Request;
        var authHeader = request.Headers["Authorization"];
        if (authHeader != null)
        {
            var authHeaderVal = AuthenticationHeaderValue.Parse(authHeader);

            // RFC 2617 sec 1.2, "scheme" name is case-insensitive
            if (authHeaderVal.Scheme.Equals("basic", StringComparison.OrdinalIgnoreCase) && authHeaderVal.Parameter != null)
            {
                AuthenticateUser(authHeaderVal.Parameter);
            }
        }
    }

    // If the request was unauthorized, add the WWW-Authenticate header 
    // to the response.
    private static void OnApplicationEndRequest(object sender, EventArgs e)
    {
        var response = HttpContext.Current.Response;
        if (response.StatusCode == 401)
        {
            response.Headers.Add("WWW-Authenticate", string.Format("Basic realm=\"{0}\"", Realm));
        }
    }

    public void Dispose()
    {
    }
}

 

Web API 2 AuthenticationController

The AuthenticationController contains a single Authenticate action method that validates the username and password and sends a JSON response to the AngularJS application indicating if the username and password are valid.

public class AuthenticationController : ApiController
{
    [Route("authenticate")]
    public IHttpActionResult Authenticate(AuthenticateViewModel viewModel)
    {
        /* REPLACE THIS WITH REAL AUTHENTICATION
        ----------------------------------------------*/
        if (!(viewModel.Username == "test" && viewModel.Password == "test"))
        {
            return Ok(new { success = false, message = "User code or password is incorrect" });
        }

        return Ok(new { success = true });
    }
}

 

Web API 2 SecureDataController

The SecureDataController contains a simple Get action method that requires authorization and is used to verify that the example application is working as it should

[Authorize]
public class SecureDataController : ApiController
{
    public IHttpActionResult Get()
    {
        return Ok(new { secureData = "You have to be authenticated to access this!" });
    }
}

 

Before using in a real application

This may seem obvious but this example Web API project authenticates against hardcoded dummy values for the username and password ("test" and "test"), this should be replaced with real authentication e.g. a user service/repository that connects to a database containing real user credentials.

Authentication is performed by the AuthenticationController and the BasicAuthHttpModule, both places should be updated before using in a real application, you can find where by searching the solution for the code comment "REPLACE THIS WITH REAL AUTHENTICATION" (without quotes).

 

Web API and AngularJS Consultant Sydney

Feel free to drop me a line if you're looking for Web API or AngularJS development or consulting services in Sydney Australia, I also provide remote contracting services for clients outside Sydney.


Sponsored by