Thursday, October 6, 2016

Azure deployment of ASP.NET Web API CORS-enabled app with token authentication

In this tutorial we will develop an ASP.NET Web API application that is CORS enabled. We will then deploy it to Azure and test it out to ensure that works properly.

What is CORS?

CORS stands for Cross-Origin-Resource-Scripting. This is essentially a mechanism that allows restricted resources (e.g. web services) on a web page to be requested from another domain outside the domain from which the resource originated. The default security model used by JavaScript running in browsers is that unless the server allows it, a service cannot be accessed from a domain other than the domain of the web page. CORS defines a way in which a browser and server can interact to determine whether or not it is safe to allow the cross-origin request.

Let us start by developing a simple ASP.NET Web API Code-First Entity Framework application using Visual Studio 2015. We will then see why it is necessary to enable CORS. Thereafter, we shall force authorization on the controller. Since Web API apps are non-visual, we will need to register and login by making API requests to the server app. This tutorial will give us a simple way to address the token exchanges that take place between the client’s jQuery app and the server.
We will model the following Student class:

image

We shall first create an ASP.NET Web API web application.

1. Start Visual Studio 2015

2. File >> New >> Project

3. Templates >> Visual C# >> Web >> ASP.NET Web Application (.NET Framework)

4. I named the web application “SchoolAPI”. Go ahead and give it whatever name you fancy.

image

5. Click OK

6. Under “Select a template” select “Web API” and leave “Host in the cloud” and “Add unit tests” unchecked:

image

7. Click OK

8. Once the application is created, build it by hitting Shift + Ctrl + B on the keyboard.

9. Run the application by hitting Ctrl + F5. You will see a fully functional ASP.NET Web API app that looks like this:

image

10. The simplistic API available is this template is served from the ValuesController.cs file. To view its output, add /api/values to the URL address. You will see the following:

image

11. It is obvious from this message that authentication is enabled on this controller. Let’s disable it. Go to ValuedController.cs and comment out (or delete) [Authorize] on line 10. Recompile your app then refresh the page in your browser. You should see the following XML output:

image

12. We can force our application to server JSON data. To do this, add the following code to the bottom end of the Register() method in App_Start/WebApiConfig.cs:
var json = config.Formatters.JsonFormatter;
json.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
config.Formatters.Remove(config.Formatters.XmlFormatter);
13. Recompile your app then refresh the page in your browser. You should see the following much simpler JSON output:

image

14. Now we are ready to code our Student model. Create a Student.cs class file in the Models folder:
public class Student {
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Major { get; set; }
    public DateTime DateOfBirth { get; set; }
}
15. Next will need to create an EF Context class. In the Models folder, create another C# class file named SchoolContext.cs. Add to it the following code:
public class SchoolContext : DbContext {
    public SchoolContext() : base("DefaultConnection") { }
    public DbSet<Student> Students { get; set; }
}
16. It is now possible for us to create the database using Entity Framework’s code-first paradigm. Go to the “Package Manager Console” by clicking on Tools >> Nuget Package Manager >> Package Manager Console. Execute the following EF migration command in order to create the Configuration.cs file in a Migrations\School folder:

enable-migrations -ContextTypeName SchoolContext -MigrationsDirectory Migrations\School
17. You will experience a response that looks like this:
PM> enable-migrations -ContextTypeName SchoolContext -MigrationsDirectory Migrations\School
Checking if the context targets an existing database...
Code First Migrations enabled for project SchoolAPI.


18. The other by-product of what we just did is that you will notice Configuration.cs was created in the Migrations\School directory. In that file is a Seed() method that allows us to enter some dummy data. Let us take advantage of this capability. Add the following method to the Configuration class:
public static List<Student> GetSampleStudents() {
  List<Student> students = new List<Student>() {
  new Student {
      FirstName = "Ann",
      LastName = "Lee",
      Major = "Medicine",
      DateOfBirth = Convert.ToDateTime("2004/09/09")
  },
  new Student
  {
      FirstName = "Bob",
      LastName = "Doe",
      Major = "Engineering",
      DateOfBirth = Convert.ToDateTime("2005/09/09")
  },
  new Student {
      FirstName = "Sue",
      LastName = "Douglas",
      Major = "Pharmacy",
      DateOfBirth = Convert.ToDateTime("2006/01/01")
  },
  new Student {
      FirstName = "Tom",
      LastName = "Brown",
      Major = "Business",
      DateOfBirth = Convert.ToDateTime("2000/09/09")
  },
  new Student {
      FirstName = "Joe",
      LastName = "Mason",
      Major = "Health",
      DateOfBirth = Convert.ToDateTime("2001/01/01")
  }
  };
  return students;
}
19. Replace the contents of the Seed() method with the following code:
context.Students.AddOrUpdate(
    s => new { s.FirstName, s.LastName },
    GetSampleStudents().ToArray()
);
context.SaveChanges();
20. Compile your application.

21. The next step is to add a migration. Execute the following command in the “Package Manager” console:

add-migration -ConfigurationTypeName SchoolAPI.Migrations.School.Configuration "InitialCreate"
Make sure you adjust the fully qualified class namespace in the above command to match your environment.

22. This time the output will look similar to the following:

PM> add-migration -ConfigurationTypeName SchoolAPI.Migrations.School.Configuration "InitialCreate"
Scaffolding migration 'InitialCreate'.
The Designer Code for this migration file includes a snapshot of your current Code First model. This snapshot is used to calculate the changes to your model when you scaffold the next migration. If you make additional changes to your model that you want to include in this migration, then you can re-scaffold it by running 'Add-Migration InitialCreate' again.


23. No database has been created yet. The next command should create the database and seed the Students table:

update-database -ConfigurationTypeName SchoolAPI.Migrations.School.Configuration

24. This time your output will look similar to this:

PM> update-database -ConfigurationTypeName SchoolAPI.Migrations.School.Configuration
Specify the '-Verbose' flag to view the SQL statements being applied to the target database.
Applying explicit migrations: [201610061908330_InitialCreate].
Applying explicit migration: 201610061908330_InitialCreate.
Running Seed method.


25. To view the contents of the database, go to View >> SQL Server Object Explorer. Navigate to your database and you should see the Students table.

image

26. If you check the contents of the Students table, you will see that it has indeed been populated with our dummy data.

image

27. Let us create a Web API controller that serves Student data. Right click on: Controllers then select Add followed by Controller.

image

28. Choose “Web API 2 Controller with actions, using Entity Framework” then click on the Add button. Enter these values in the dialog:
Model class: Student
Data context class: SchoolContext
Controller name: StudentsController
image

29. Click on Add. This will create StudentsController.cs. Compile your application then point your browser to /api/students. You should see your student data in JSON format.

image

From now on it starts getting very interesting because we are about to deal with the challenges of CORS and authentication.

CORS

To experience CORS, let us create another empty ASP.NET empty application to our project.

1. File >> Add >> New Project >> Visual C# >> Web >> ASP.NET Web Application (.NET Framework) >> Name it SchoolClient >> OK

image

2. Choose Empty then click on OK. This adds an empty ASP.NET project to your solution.

image

3. We will be needing jQuery and Bootstrap in this new empty project. Therefore, add these libraries using the Nuget Packet Manager. Right-click on the main SchoolClient project node and choose “Manage Nuget Packages…”. Click on Browse in top-left corner and enter jquery in the search box. Choose jQuery then click on the Install button.

image

4. Do the same for bootstrap.

5. Your SchoolClient project now has both the jQuery & Bootstrap libraries.

image

6. Add a plain HTML file to the SchoolClient project named index.html.

7. Change contents of the <title> tag to “School Client”.

8. Drag & drop Scripts/jquery-3.1.1.min.js (your version of jQuery may be more recent than mine) and Content/bootstrap.min.css into the <head> section of index.html. Your index.html page will look similar to this:

<!DOCTYPE html>
<html>
<head>
    <title>School Client</title>
    <meta charset="utf-8" />
    <link href="Content/bootstrap.min.css" rel="stylesheet" />
    <script src="Scripts/jquery-3.1.1.min.js"></script>
</head>
<body>

</body>
</html>

9. Add the following HTML code into the <body> section of index.html:
<div class="container">
    <h3>Cors Request</h3>
    <button id="btnGet"
class="btn btn-primary">Get Students</button>
    <pre id="preOutput"></pre>
</div>
<script>
    var baseUrl = "
http://localhost:51458/";
    $(function () {
        var getStudents = function () {
            var url = baseUrl + "api/students/";
            $.get(url).always(showResponse);
            return false;
        };

        var showResponse = function (object) {
            $("#preOutput").text(JSON.stringify(object, null, 4));
        };

        $("#btnGet").click(getStudents);
    });
</script>


10. Run the previous project “SchoolAPI” project so that it shows you the following screen:

image

11. In index.html, adjust the value of baseUrl so that the port number matches the port number in the above page.

12. Right-click on index.html and choose “View in browser (…)”. This will serve this HTML page in your browser. At this point we should hit F12 in your browser in order to open developer tools. Click on the Console tab.

image

13. Now click on the “Get Students” button. This should produce an error in the console panel that suggests that our server app does not support CORS. This is because the two sites that we are dealing with have different port numbers and are treated as different domain names.

image

14. The solution to this problem is not hard. Firstly, we need to add a Nuget package named “Microsoft.AspNet.WebApi.Cors” to our server application.

image

15. Firstly, add config.EnableCors(); in App_Start/WebApiConfig.cs at the top of the register method.

16. Secondly, add [EnableCors("*", "*", "GET")] just above the class declaration of the StudentController class. This allows all GET verb requests from other domains.

image

17. Build your solution and refresh index.html. You should now see the students being served from our server application.

image

Authorization tokens

Let us add decorate the StudentsController with the [Authorize] annotation.
image

Compile your app then click on the “Get Students” button. As expected, you should receive this message suggesting that authentication is necessary:
image

1. Since our Web API application has no UI for user registration, our first challenge is to register a user. We will need to do so by sending RESTful requests to the AccountController. Since CORS restrictions apply to the AccountController too, do not forget to add [EnableCors("*", "*", "*")] to this class declaration.

image

2. Looking further in the AccountController class, you will notice that there is a Register() action method which we will need to post to with Email, Password and ConfirmPassword data items.

3. Add the following registration form right below the <h3> tag in the index.html client project:

<div class="row bg-info">
    <div class="col-sm-6">
      <form id="frmRegister" role="form">
        <div class="form-group">
          <input type="text" name="Email" placeholder="Email" />
        </div>
        <div class="form-group">
          <input type="password" name="password" placeholder="Password" />
        </div>
        <div class="form-group">
          <input type="password" name="confirmPassword" placeholder="Confirm Password" />
        </div>
        <div class="form-group">
          <input type="button" id="btnRegister" value="Register" class="btn btn-primary" />
        </div>
      </form>
    </div>
    <div class="col-sm-6">
    </div>
</div>


4. Add this jQuery code inside the $(function() { … } block to submit the registration data:
var register = function () {
  var url = baseUrl + "api/account/register";
  var data = $("#frmRegister").serialize();
  $.post(url, data).always(showResponse);
  return false;
};

$("#btnRegister").click(register);
5. If you click on the Register button without any data, you will see this validation error message:

image

6. Enter real data then click on Register. If you receive "" as a response, then the new user registration is successful.
image

7. How about if you click on “Get Student”s button? You will get error message: Authorization has been denied for this request.

8. You guessed right. The next challenge is to login with the credentials that we just created. Add the following login form to index.html inside the empty <div class="col-sm-6"> tag:

<form id="frmLogin" role="form">
    <input type="hidden" name="grant_type" value="password" />
    <div class="form-group">
        <input type="text" name="userName" placeholder="UserName" />
    </div>
    <div class="form-group">
        <input type="password" name="password" placeholder="Password" />
    </div>
    <div class="form-group">
        <input type="button" id="btnLogin" value="Login" class="btn btn-primary" />
    </div>
</form>


9. Add this jQuery code to handle the login process:
var login = function () {
    var url = baseUrl + "Token";
    var data = $("#frmLogin").serialize();
    $.post(url, data).always(showResponse);
    return false;
};

$("#btnLogin").click(login);
10. Refresh index.html in your browser. Your page now has a login form.

image

11. Enter the email and password you created earlier, then click on Login. You will get a CORS error, just like we did previously. This suggests that the /Token controller is not enabled for CORS. To solve this problem, find the GrantResourceOwnerCredentials() method in /Providers/ApplicationoAuthProvider.cs. Add following code to the bottom of this method:

context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });

12. Build your solution then try again to login. This time you will get a successful response containing an access_token. We will need to pass on this access_token whenever we make requests for any API service on a secured controller.

image

13. Add this global variable to save the value of the access_token into it:

var accessToken = "";

14. Replace the current login() function with this function that saves the token into variable accessToken:
var login = function () {
    var url = baseUrl + "Token";
    var data = $("#frmLogin").serialize();
    $.post(url, data)
        .done(saveAccessToken)
        .always(showResponse);
    return false;
};
15. Next, add these two helper functions just below the global variables:
var saveAccessToken = function (data) {
    accessToken = data.access_token;
};

var getHeaders = function () {
    if (accessToken) {
        return { "Authorization": "Bearer " + accessToken };
    }
};

The first function saves the access_token into the accessToken global variable. The second function constructs the Authorization header token value that needs to be sent with each request for API service.
16. Replace the getStudents() function with the following code so that the request for student data is also accompanied with the appropriate access_token:
var getStudents = function () {
    var url = baseUrl + "api/students/";
    $.ajax(url, {
        type: "GET",
        headers: getHeaders()
    }).always(showResponse);
    return false;
};
17. Refresh index.html in your browser, login again, then click on “Get Students”. You should see the students being displayed.

We have successfully registered a user, logged-in with our credentials and were able to read data from a secured controller.

Publishing ASP.NET Web API project to Azure

1. Right-click on the SchoolAPI Web API project and choose Publish.

image

2. Click on “Microsoft Azure App Service”.

image

3. You will need to login into your Azure account. Click on “Microsoft Account” in the top right corner. Select an account or choose “Add an account…”.

image

4. Click on New… button.

image

5. Simplify the “Web App Name” such that it is unique when combined with azurewebsites.net. Also, create a resource group if you do not have one already. I already had a resource group so I did not create another. However, I needed to add an “App Service Plan” so I created one as follows:

image

6. Back in the “Create App Service” dialog, it looked like this:

image

7. To setup the database on SQL-Azure, click on “Explore additional Azure services”.

image

image

8. Click in + icon to add a SQL database on Azure.

image

9. It is necessary to have at least one database server to host many database instances. To this end, name your database and enter an administrator username and password. Make sure you remember the administrator username and password as you will be needing these credentials over and over again. Also, it is worth giving your database a simpler name. Note that the connection string default name is DefaultConnection, which is indeed what our Web API application is using.

image

10. Click OK.

image

11. Click Create. It may take a few minutes to complete the steps needed to create the database.

 image

12. You can click on the “Validate Connection” button to ensure that Visual Studio can connect to your app service. When you click Next you will be taken to the settings tab. Enable both checkboxes “Use this connection string at runtime (update destination web.config)” and “Execute Code First Migrations (runs on application start)”.

image

13. Click on “Next”. You will be taken to the Preview tab.

image

14. Click Publish. Take note of the activity in the Output pane in Visual Studio. The application is being deployed to Azure. Upon completion of deployment, the web application will be displayed in your default browser.

image

15. Back in Visual Studio, edit the index.html file in the SchoolClient project and change the value of the variable baseUrl to point to the URL of the deployed site in Azure instead of localhost.

image

16. Right-click on index.html and select “View in browser”. Register an account, login, then click on “Get Students”. If you see student data then it is clear that a database was created, database migrations were processed and registration/authentication is working.

image

I hope you had the patience to get this far and benefited from the learning process.

References

https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
https://azure.microsoft.com/en-us/documentation/articles/web-sites-dotnet-deploy-aspnet-mvc-app-membership-oauth-sql-database/






























































































































No comments:

Post a Comment