Building your own API and Securing it with oAuth 2.0 in ASP.NET WebAPI 2

Objectives:

  1. Make a true RESTful Web API (enable CRUD functions by HTTP POST, GET, PUT, and DELETE).
  2. Enable Cross-Origin Resource Sharing, i.e., CORS (the accessibility of the API by JavaScript can be controlled).
  3. Enable Secure Authorization for API calls (use the OAuth 2.0 authorization framework).
  4. Enable Transport Layer Security, i.e., SSL (reject every non-HTTPS request).

Required Tools:

  1. A Windows Computer.
  2. Microsoft Visual Studio Express 2013 for Web (free download).

Recommended Knowledge:

  1. Securing (ASP.NET) Web API based architectures (video).

AND/OR

  1. ASP.NET Web API 2 (tutorials).
  2. OAuth 2.0 Authorization Framework (specification).
  3. Open Web Interface for .NET (OWIN).
  4. Writing an OWIN Middleware in the IIS integrated pipeline (tutorial).
  5. Enabling Cross-Origin Requests in ASP.NET Web API (tutorial).

An Skeleton Project by ASP.NET Web API 2 (oAuth 2.0 and CORS support)

1. Creating the Project

Create an ASP.NET Web Application (Visual C#) in Microsoft Visual Studio Express 2013 for Web. Let’s name it Skeleton.WebAPI. Select the Web API template and Change Authentication from ‘No Authentication’ to ‘Individual User Accounts’, [screenshot].

The ‘Controllers/ValuesController.cs’ holds our sample API endpoints. Run the project and in the browser go to ‘http://localhost:port/api/values‘ and you should get a 401 Unauthorized.

2. Writing the Sample API Endpoints

In ‘Controllers/ValuesController.cs’, comment out [Authorize] to disable authorization. Run the project and in the browser go to ‘http://localhost:port/api/values‘ and now you should get 2 values.

Change ‘Controllers/ValuesController.cs’ with the following code to enable CRUD operations, [Link]. This controller now accepts HTTP GET, POST, PUT, and DELETE to receive a list of values, add a value, update a value, and delete a value. Currently, these values are stored in the Web Server memory, but ideally they will be saved in the database. I recommend using Repository Pattern for it, [More info].

Let’s write a simple html page to call these api endpoints. For example, the following code can be used for receiving the list of current values:

The full list of API calls by JavaScript can be found here. At this point, you will notice that all the API calls are returning errors. That’s because we haven’t added Cross-Origin Resource Sharing (CORS) support to the server.

3. Enabling Cross-Origin Resource Sharing (CORS) support 

To Enable CORS support, first install Microsoft.AspNet.WebApi.Cors by NuGet. Open up ‘App_Start/WebApiConfig.cs’ and add the following line in the Register method:

Then open your API Controller, ‘Controllers/ValuesController.cs’ and add the following line before the class definition:

It tells the server the accept all types requests. You can obviously filter out the requests. For more customization, read this.

4. Cleaning up the Views from the project

You should notice that the VS template created some views in the project. As we are building a pure Web API, there will be no views. So, delete App_Start/[BundleConfig,RouteConfig].cs, Areas/*, Content/*, Controllers/HomeController.cs, fonts/*, Scripts/*, Views/*, Project_Readme.html, and favicon.ico. Finally, remove the following lines from Global.asax.cs:

At this point, we have completed Objectives 1 and 2. The code up to this point can be  downloaded from https://github.com/rfaisal/ASPWebAPI_Example_OAuth_CORs/tree/crud_cors_only_v1.

5. Adding oAuth 2.0 support

In the simplest terms, here is how oAuth (or most token based authorization) works. The client request an Access Token from the Authorization Server. The Authorization Server verifies the identity of the client somehow and returns an Access Token, which is valid for a limited time. The client can use the Access Token to request resources from the Resource Server as long as the Access Token is valid. In our case, both Authorization Server and the Resource Server are the same server, but they can be separated easily.

Ideally, you need to write an OWIN Middleware in the IIS integrated pipeline for oAuth. But, the VS template generates the necessary codes for the OWIN Middleware. Watch this video to learn more about these implementations.

Now, we will add 2 more functionality, namely registering a new user and requesting a Access Token based on the username and password.

The user registration controller (Controllers/AccountController.cs) is already generated by the Template. First add the following line in the AccountController to allow CORS:

Then you can call the end point http://localhost:port/api/Account/Register/ and HTTP POST an object consisting of UserName, Password, and ConfirmPassword to register an account.

Now, the endpoint for requesting an Access Token is also generated by the template. The code is in App_Start/Startup.Auth.cs :

It says that the client can request an Access Token by calling http://localhost:port/Token endpoint and HTTP POSTing grant_type, username, and password as Url Encoded String. This endpoint, however, is not an API endpoint. To enable CORS to this endpoint we need to add the following code segment to the ConfigureAuth function of App_Start/Startup.Auth.cs file.

We are almost done. We have to modify all the API calls to pass this Access Token. First of all, un-comment [Authorize] attribute on the ValuesController and run to make sure that all of the API calls are returning 401 Unauthorized. Add the following property with each ajax call to pass the Access Token as the Bearer Token:

At this point, we have completed Objectives 1, 2, and 3. The code up to this point can be  downloaded from https://github.com/rfaisal/ASPWebAPI_Example_OAuth_CORs/tree/crud_cors_oauth.

6. Enforce SSL for all API calls

Any information that is not transferred over SSL is not secure. So, we are going to reject any request that doesn’t come over HTTPS. First we will write a filter that can be used to filter out non-https requests. Here is the code:

When we add this class to our project, we have [RequireHttps] available. Add this attribute to all the Controller class. For example,

Finally, we also want the Token request to be enforced over HTTPS. To do so, remove ‘AllowInsecureHttp = true’ from Startup.Auth.cs.

To test the SSL enforcement, change the SSL Enabled property to true for the Development Server in Visual Studio and use the SSL Url, [screenshot].

The final code can be  downloaded from https://github.com/rfaisal/ASPWebAPI_Example_OAuth_CORs/tree/crud_cors_oauth_ssl.

A screenshot of the client, [screenshot].

About these ads

Tagged: , , , , , , , , , , , , , , , , , , ,

13 thoughts on “Building your own API and Securing it with oAuth 2.0 in ASP.NET WebAPI 2

  1. Anas KVC April 3, 2014 at 11:49 pm Reply

    Great article.

    To run the sample project in VS 2012 we need to do the following changes.

    1. Add the missing nuget packages using package manager console

    install-package Microsoft.AspNet.Identity.Core
    install-package Microsoft.AspNet.Identity.EntityFramework

    2. Open the local db on vs server explorer.

    It looks like AspNetUsers table doesn’t have the default columns which is mapped from IdentityUser object. (It will throw the error when we try to register a new user)

    So,

    remove the __MigrationHistory table.
    remove the AspNetUsers table and execute the new query on server explorer.

    CREATE TABLE [dbo].[AspNetUsers] (
    [Id] NVARCHAR (128) NOT NULL,
    [Email] NVARCHAR (256) NULL,
    [EmailConfirmed] BIT NOT NULL,
    [PasswordHash] NVARCHAR (MAX) NULL,
    [SecurityStamp] NVARCHAR (MAX) NULL,
    [PhoneNumber] NVARCHAR (MAX) NULL,
    [PhoneNumberConfirmed] BIT NOT NULL,
    [TwoFactorEnabled] BIT NOT NULL,
    [LockoutEndDateUtc] DATETIME NULL,
    [LockoutEnabled] BIT NOT NULL,
    [AccessFailedCount] INT NOT NULL,
    [UserName] NVARCHAR (256) NOT NULL,
    CONSTRAINT [PK_dbo.AspNetUsers] PRIMARY KEY CLUSTERED ([Id] ASC)
    );

    This will resolve the issues.

  2. Faisal R. April 4, 2014 at 6:45 am Reply

    Thanks Anas!

  3. dave April 21, 2014 at 5:38 am Reply

    Is it possible to use Web API 2′s build in external authentication with Angular JS being hosted on a different web-server? If so does anyone know of an example?

    I’m struggling to get it working. The facebook request is giving me a CORS issue. I’m not sure what is going on.

    My only guess is the redirect is coming from a different domain than the web api is hosted on. Which is what is doing the facebook configuration.

    • Faisal R. April 21, 2014 at 6:53 am Reply

      Hi Dave-
      I am assuming you just uncomment app.UseFacebookAuthentication in Startup.Auth.cs and using it the same way the Web API template example is using, except the WebAPI and client is hosted in different servers.

      In that case, adding the domain urls for both your hosts to facebook app settings should work. If not, can you please send me your login flow.

  4. dave April 21, 2014 at 11:02 am Reply

    Sure. My login flow is as follows:

    1.) User navigates to my login page at http://localhost:8000/app/index.html#/login
    2.) On page load I make a request to my web api to load the ExternalLogins at api/Account/ExternalLogins?returnUrl=%2F&generateState=true. This loads the URLS for when the user clicks the login buttons (exactly how its done with Microsoft SPA template)
    3.) When the user clicks login with facebook button, the web api endpoint api/Account/ExternalLogin. The user is not authenticated to the ChallengeResult is returned. This is where the redirect to the facebook login page should happen. I see the request being made in chrome dev tools but the redirect gives me a No Access-Control-Allowed-Origin error.

    I notice the redirect request has a Origin header with a null value. I think this is the problem but I don’t know why its there. I can duplicate the api call in fiddler to my api/Account/ExternalLogin api, but remove the Origin header, and I dont get the No Access-Control-Allowed-Origin error.

    The origin header gets sent to my API from my angular js client, but im not sure why its on the redirect, or why the value is null.

    Any idea on what could be going on? Thanks

  5. dave April 21, 2014 at 11:47 am Reply

    Ok. Thanks for the response. I did find things like this – https://bugs.webkit.org/show_bug.cgi?id=98838

    Not sure if thats the problem.

  6. dave April 22, 2014 at 11:56 am Reply

    Any luck?

    • Faisal R. April 22, 2014 at 11:58 am Reply

      Hi Dave-
      Sorry, I haven’t had a chance to look into this yet. I will do it tonight, please check back tomorrow.

    • Faisal R. April 22, 2014 at 6:02 pm Reply

      Hi Dave-
      I tested it and it worked just fine for me. This is what I did:

      1. Added my facebook appId and appSecret to the project of the blog post. and run it
      2. Added http://localhost:49811/ in my facebook app’s “site url”
      3. Run api/Account/ExternalLogins?returnUrl=%2F&generateState=true to get a list of urls
      4. Created a new SPA project and its login.viewmodel.js–>self.login function –> set window.location to the url I get from Step 3 (hard coded)
      5. Run the SPA and click on the login with facebook button.

      It took me to WebAPI’s root url with a access_token in the url (meaning successful authentication)

  7. dave April 22, 2014 at 7:43 pm Reply

    Faisal,
    I’m going to start fresh and try exactly what you just did. Thanks for the response. I will let you know how it goes.

  8. Jimmy May 2, 2014 at 4:42 am Reply

    Hello

    is there a second part or has this tutorial changed after getting published ?

    I can’t see any details about DB in here so I’m confused how to authenticate and store user data for this application?

    I can see one comment regarding a table called ‘AspNetUsers’ and EF framework but not mentioned in the tutorial it self

    Cheers

    • Faisal R. May 2, 2014 at 12:45 pm Reply

      Hi Jimmy-
      No there is no 2nd part. You can change the database info in Web.config file. By default, it uses a .mdf file in your local file system for data storage

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: