Token Based Authentication using ASP.NET Web API with OWIN

I have been consuming many 3rd party APIs (as well as mines) for a while, however I have never implemented OAuth2 server for myself. I developed a simple app that lets user register and and consume authentication required resource. I intentionally tried to keep it simple to better understand behind the scene. Fortunately, Web API project template shipped with Visual Studio supports OAuth2 out-of-box to authenticate request. Let's define the project goals and dive into coding.

Here is my goals;

  • Register a user via Web API call
  • Retrieve authentication token for the registered user
  • Access a resource which required authentication

Setup Web API project

I fired up Visual Studio and created a new ASP.Net Web Application project. In the New Project dialog, selected the Web API template. If you follow though, it's important to select Web API template instead of ASP MVC project and add Web API; because they are slightly different templates. We are interested in Web API project!

Upon project load, I updated the NuGet packages to latest versions; since I wanted to use latest and brightest. It may ask you to restart to VS in order to complete the installation. I quickly took a look at the solution explorer; there is an Areas folder, okay; under the Controllers folder two controllers are inherited from ApiContoller: Account and Values. Additionally, Providers and Results folders. All seemed simple enough. Time to test the project setup.

I ran the solution, there are two header menu; Home and API. API links took me to help (it's the area) shows primitive API help page: neat!

ASP.NET Web API Help Page

After quick exploration of the project, I typed /api/values to address bar and received Authorization has been denied for this request. message as it was expected. So, I was on the right path. Then I fired up Fiddler and composed same address and received 401 Unauthorized response.

401 Unauthorized response Fiddler

Enable SSL for the project

I also realized that I was hitting to HTTP. Since a real application would have run on HTTPS protocol, I decided to enable SSL for the project and enforce all of the request to go secure channel. I stop the project and went to Solution Explorer brought up the project properties dialog (press F4).

Enabling SSL for the project

Changed SSL Enabled to true. Copied the SSL URL and went to the Project Properties (ALT + Enter) > Web. Changed the Project URL with the copied SSL Url. That's all. If you have done it before you won't get any warning message. If it's first time for you to use SSL IIS Express generated, Visual Studio will ask you to if you would trust the IIS Express SSL certificate then a Security Warning dialog in your browser to install the certificate representing localhost.

Ran the solution again, SSL is working properly. One second!! If I type the HTTP url (remember on the project properties dialog) it's working too. Himm.

It's easier to achieve desired HTTPS required effect with MVC pipeline but never inject that for Web API pipeline before. So first thing first: I added RequireHttpsAttribute attribute to FilterConfig.

// Enforce HTTPS protocol
filters.Add(new RequireHttpsAttribute());

Then I found Working with SSL in Web API on Internet which was easy to implement. I created Filter folder inside the project and created RequireWebApiHttpsAttribute class then I copied and pasted the code. I also implemented the attribute to WebApiConfig. So that I don't need to add RequireHttps attribute all of the WebApi controllers.

// Enforce HTTPS protocol
config.Filters.Add(new Filters.RequireWebApiHttpsAttribute());

After those changes, if you want to make a request to API without HTTPS then you would get 403 Forbidden HTTP response code along with custom message of HTTPS Required back.

Before I move programmaticaly register a user, I will note one more change I made on App_Code > Startup.Auth.cs which was took out AllowInsecureHttp = true from OAuthAuthorizationServerOptions. I will touch this option in details on customization section again.

For real application I might have spend more time to redirect to correct response or give different types of messages. But, I thought it is good enough for this sample project.

Register a User

Regular MVC project template has a Register action inside the Account controller, same goes for Web API project template but the different is Account is not a MVC controller but API controller.

I wanted to have a simple one page application; but not the sense of AngularJS or Knockout since I'm not really playing with those technologies, rather a simple application which has only one page and uses pure HTML, JQuery and JavaScript. So, I placed user register form into the home page, again using pure HTML not MVC. I quickly realized that how much I relay on MVC framework's validation and binding capabilities when I work with forms. Long story short, I added bunch of HTML5 attributes to the form to validate it. Then some JQuery codes to handle the validation even better and post the form in Ajax, receive result and show the user. Final result looks like below.

Registration form

Opened the Home controller and added following code for registration call.

[HttpPost]
public async Task<HttpStatusCodeResult> Register(string email, string password, string passwordConfirm)
{
    if (String.IsNullOrEmpty(email) || 
                String.IsNullOrEmpty(password) || 
                !String.Equals(password,passwordConfirm))
            {
                return new HttpStatusCodeResult(HttpStatusCode.BadRequest, 
                    "Registration form is invalid");
            }

    using (var client = new HttpClient())
    {
        client.BaseAddress = new Uri(ApiUri);
        client.DefaultRequestHeaders.Accept.Clear();

        var registerModel = new Dictionary<string, string>
        {
            {"Email", email},
            {"Password", password},
            {"ConfirmPassword", password}
        };

        var response = await client.PostAsJsonAsync("api/Account/Register", registerModel);

        return new HttpStatusCodeResult(response.StatusCode, response.ReasonPhrase);
    }
}

The Account API controller was waiting for a call to Register action with RegisterBindingModel as expected model. It has 3 members: Email, Password and ConfirmPassword. I made an dictionary on my Register action which calls to Register action on API. I was expecting to receive a green message box saying User Registered instead I received failed response. I turned out the reason I failed was that the password wasn't strong enough. Good! I'd make that happen but before I need to show errors to users. So I added following code to the action.

I added a simple class (ResponseErrors) to receive errors and show it to user. I override ToString() method to show the error more user friendly. Following code add to Register action.

var response = await client.PostAsJsonAsync("api/Account/Register", registerModel);

//New code:
if (response.IsSuccessStatusCode) 
    return new HttpStatusCodeResult(response.StatusCode);

var errors = await response.Content.ReadAsAsync<ResponseErrors>();
return new HttpStatusCodeResult(response.StatusCode, errors.ToString());

Registration Error

After everything was taken care of, then I successfully registered a user via Web API call.

Retrieve Access Token

I had successfully registered a user, it was time to authenticate the user and retrieve access token. I went back to Account API controller to find any clue where I should have make that call. Nothing, how come? There are bunch of methods for user but login!! So, I started to looking around and found it under ConfigureAuth method inside the Startup.Auth.cs file. The token end point path was "/Token". I didn't like it, so I changed it to "/api/authtoken". So, I knew where to make to call.

I created a simple form where it takes username (the email address) and password. I call the end point then I received error message "unsupported_grant_type".

The call suppose to go GrantResourceOwnerCredentials method but it wasn't! I scratched my head for a while then I figured out that the problem was the request call; content type should have been application/x-www-form-urlencoded not Json! I changed my call from PostAsJsonAsync to PostAsync on my code.

var tokenModel = new Dictionary<string, string>
{
    {"grant_type", "password"},
    {"username", tokenEmail},
    {"password", tokenPassword},
};

// This is not working
//var response = await client.PostAsJsonAsync("api/authtoken", tokenModel);

// This is working!!
var response = await client.PostAsync("/api/authtoken", new FormUrlEncodedContent(tokenModel));

I bind the returned body to following Token class.

public class Token
{
    [JsonProperty("access_token")]
    public string AccessToken { get; set; }

    [JsonProperty("token_type")]
    public string TokenType { get; set; }

    [JsonProperty("expires_in")]
    public int ExpiresIn { get; set; }

    [JsonProperty("userName")]
    public string Username { get; set; }

    [JsonProperty(".issued")]
    public DateTime Issued { get; set; }

    [JsonProperty(".expires")]
    public DateTime Expires { get; set; }
}

Accessing Resources

The Web API template comes with simple Values API Controller. Before I write anything, I wanted make GET request it using Fiddler.

I made to call to https://localhost:44301/api/values address and I received 401 Unauthorized response. Then, I added the token Authorization: Bearer nybx5yvItZm5EsBuiHb92nSc3... to request header, when I click the "Execute" button I got 200 response along with values.

Here is the C# code to do same thing.

using (var client = new HttpClient())
{
    client.BaseAddress = new Uri(ApiUri);
    client.DefaultRequestHeaders.Accept.Clear();

    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _myToken.AccessToken);
    var authorizedResponse = client.GetAsync("api/values").Result;

    if (!authorizedResponse.IsSuccessStatusCode) return null;

    var response = await authorizedResponse.Content.ReadAsAsync<List<string>>();
    return response;
}

Conclusion

Setting up Auth server is relatively simple task thanks to Web API template shipped with Visual Studio. As all other templates, it requires ton of customization, but it gives a solid starting point. As long as, you call the API correctly it surely response to you. I achieved my goals at this stage with this sample application. Next blog post, I will complete the application with CRUD operations and further customization on server side.

You can download the code from GitHub.