Showing posts with label Authentication. Show all posts
Showing posts with label Authentication. Show all posts

Saturday, June 30, 2018

Seeding & Extending Users and Roles with ASP.NET Core 2.1 Identity in Visual Studio 2017

In this post I shall describe the steps you need to follow if you want to add more data fields to the standard users & roles database table and seed both users and roles data. The approach being followed is code first development with Entity Framework Core. In order to proceed with this tutorial you need to have the following prerequisites:
  • You are using Visual Studio 2017 running under the Windows 10 operating system
  • You have installed ASP.NET 2.1 Core

Getting Started

In Visual Studio 2017, start a new ASP.NET Core 2.1 application by clicking on File >> New >> Project

Select ASP.NET Core Web Application and name your project with a name like IdentityCore.

Click on the OK button. On the next dialog:
  • select ASP.NET Core 2.1 from the drop-down list
  • select "Web Application (Model-View-Controller)" from the templates
  • click on the "Change Authentication" button and choose "Individual User Accounts"

Click on OK. Run the application by hitting Ctrl + F5 on your keyboard. Click on the Register link on the top-right side of your keyboard to add a new user. 

When you click on the Register button, you may receive a message that looks like this:

Do not be alarmed. The message simple reminds you that the Entity Framework migrations have not been applied yet. Simple click on the blue "Apply Migrations" button then refresh the page in your browser. The home page will display as shown below:


Click on Logout in the top-right corner.

Suppose we want to capture more data about the user, in addition to email and password. Let us assume we want to extend user data with the following properties:

FirstName
LastName
Street
City
Province
PostalCode

An easy way to do this is to create a new class that extends IdentityUser and adds the above properties. In the Models folder, add a new class named ApplicationUser and add to it the following class code:

    public class ApplicationUser : IdentityUser
    {

        public ApplicationUser() : base() { }

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Street { get; set; }
        public string City { get; set; }
        public string Province { get; set; }
        public string PostalCode { get; set; }
        public string Country { get; set; }
    }

We may also wish to extend the standard roles table with these properties:

Description
CreatedDate

Just like we did with users, we will also create another class for roles that inherits from IdentityRole. In the Models folder, create a new class named ApplicationRole and add to it the following class code:

    public class ApplicationRole : IdentityRole
    {

        public ApplicationRole() : base() { }

        public ApplicationRole(string roleName) : base(roleName) { }

        public ApplicationRole(string roleName, string description,
            DateTime createdDate)
            : base(roleName)
        {
            base.Name = roleName;

            this.Description = description;
            this.CreatedDate = createdDate;
        }

        public string Description { get; set; }
        public DateTime CreatedDate { get; set; }

    }

Edit Data/ApplicationDbContext.cs file and get ApplicationDbContext to inherit from IdentityDbContext<ApplicationUser, ApplicationRole, string>. The ApplicationDbContext class code should look like this:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string> {
    public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
        : base(options) { }
}

Let us now create some sample data for roles and users. Create class named DummyData in Data folder, then add to it the following method:

public class DummyData {
    public static async Task Initialize(ApplicationDbContext context,
                          UserManager<ApplicationUser> userManager,
                          RoleManager<ApplicationRole> roleManager)
    {
        context.Database.EnsureCreated();

        String adminId1 = "";
        String adminId2 = "";

        string role1 = "Admin";
        string desc1 = "This is the administrator role";
        
        string role2 = "Member";
        string desc2 = "This is the members role";

        string password = "P@$$w0rd";

        if (await roleManager.FindByNameAsync(role1) == null) {
            await roleManager.CreateAsync(new ApplicationRole(role1, desc1, DateTime.Now));
        }
        if (await roleManager.FindByNameAsync(role2) == null) {
            await roleManager.CreateAsync(new ApplicationRole(role2, desc2, DateTime.Now));
        }

        if (await userManager.FindByNameAsync("aa@aa.aa") == null) {
            var user = new ApplicationUser {
                UserName = "aa@aa.aa",
                Email = "aa@aa.aa",
                FirstName = "Adam",
                LastName = "Aldridge",
                Street = "Fake St",
                City = "Vancouver",
                Province = "BC",
                PostalCode = "V5U K8I",
                Country = "Canada",
                PhoneNumber = "6902341234"
            };

            var result = await userManager.CreateAsync(user);
            if (result.Succeeded) {
                await userManager.AddPasswordAsync(user, password);
                await userManager.AddToRoleAsync(user, role1);
            }
            adminId1 = user.Id;
        }

        if (await userManager.FindByNameAsync("bb@bb.bb") == null) {
            var user = new ApplicationUser {
                UserName = "bb@bb.bb",
                Email = "bb@bb.bb",
                FirstName = "Bob",
                LastName = "Barker",
                Street = "Vermont St",
                City = "Surrey",
                Province = "BC",
                PostalCode = "V1P I5T",
                Country = "Canada",
                PhoneNumber = "7788951456"
            };

            var result = await userManager.CreateAsync(user);
            if (result.Succeeded) {
                await userManager.AddPasswordAsync(user, password);
                await userManager.AddToRoleAsync(user, role1);
            }
            adminId2 = user.Id;
        }

        if (await userManager.FindByNameAsync("mm@mm.mm") == null) {
            var user = new ApplicationUser {
                UserName = "mm@mm.mm",
                Email = "mm@mm.mm",
                FirstName = "Mike",
                LastName = "Myers",
                Street = "Yew St",
                City = "Vancouver",
                Province = "BC",
                PostalCode = "V3U E2Y",
                Country = "Canada",
                PhoneNumber = "6572136821"
            };

            var result = await userManager.CreateAsync(user);
            if (result.Succeeded) {
                await userManager.AddPasswordAsync(user, password);
                await userManager.AddToRoleAsync(user, role2);
            }
        }

        if (await userManager.FindByNameAsync("dd@dd.dd") == null) {
            var user = new ApplicationUser {
                UserName = "dd@dd.dd",
                Email = "dd@dd.dd",
                FirstName = "Donald",
                LastName = "Duck",
                Street = "Well St",
                City = "Vancouver",
                Province = "BC",
                PostalCode = "V8U R9Y",
                Country = "Canada",
                PhoneNumber = "6041234567"
            };

            var result = await userManager.CreateAsync(user);
            if (result.Succeeded) {
                await userManager.AddPasswordAsync(user, password);
                await userManager.AddToRoleAsync(user, role2);
            }
        }
    }
}

In the Startup class, replace the call to services.AddDefaultIdentity so that it uses the new ApplicationUser. Add the call to AddDefaultUI with the following code replacement:

services.AddIdentity<ApplicationUser, ApplicationRole>(
    options => options.Stores.MaxLengthForKeys = 128)
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddDefaultUI()
    .AddDefaultTokenProviders();

Edit Views/Shared/_LoginPartial.cshtml and change:

@inject SignInManager<IdentityUser> SignInManager
@inject UserManager<IdentityUser> UserManager

TO


@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager

We will use the magic of dependency injection to make available to us the ApplicationDbContext, RoleManager and UserManager objects in the Configure() method in Startup.cs. Change the signature of the Configure() method by adding additional arguments (UserManager & RoleManager) so that it looks like this:

public void Configure(IApplicationBuilder app, 
  IHostingEnvironment env,
  ApplicationDbContext context,
  RoleManager<ApplicationRole> roleManager,
  UserManager<ApplicationUser> userManager
)
{ . . . . . }

Add the following to the bottom of the Configure() method in Startup.cs:

DummyData.Initialize(context, userManager, roleManager).Wait();// seed here

Note that in the Data/Migrations folder there are Entity Framework Code First migrations files that were added by the initial Visual Studio project template.


Since we changed the model for both users and roles, we need to add another migration and subsequently update the database. Therefore, execute the following commands from inside the Package Manager Console (Tools >> Nuget Package Manager >> Package Manager Console):

Add-Migration ExtendedUserRole -Context ApplicationDbContext

Update-Database -Context ApplicationDbContext

At this stage the tables are created, however data is not yet seeded. Let us run our application so that the sample roles and users get seeded. Hit CTRL + F5 on your keyboard. When the application starts, Logout if you are already logged in.

To prove that user and role data have been successfully seeded, login with one of the below credentials that were previously seeded:

Email: a@a.a    Password: P@$$w0rd
Email: b@b.b    Password: P@$$w0rd
Email: d@d.d    Password: P@$$w0rd
Email: m@m.m    Password: P@$$w0rd


The next task we need to accomplish is to modify the registration page so that the application can capture extended data such as First Name, Last Name, Street, City, Province, Postal Code and Country. ASP.NET Core 2.1 (and later) provide ASP.NET Core Identity as a Razor Class Library. This means that the registration UI is baked into the assemblies and is surfaced with the .AddDefaultUI() option with the services.AddIdentity() command in the ConfigureServices() method in Startup.cs.

Since we need to modify the registration controller and view, we instruct the scaffolder to generate the code used for registration. To do this, right-click on the project node in the Solution Explorer pane then:  Add >> New Scaffolded Item:


On the next Add Scaffold dialog, click on Identity on the left side, highlight Identity in the middle pane then click on the Add button.

On the Add Identity dialog, enable the "Override all files" checkbox, select the ApplicationDbContext class then click on the Add button.

Many Razor view pages will be generated for you under folder Areas/Identity/Pages/Account.


Edit the code-behind file Areas\Itentity\Pages\Account\Register.cshtml.cs. Add the following properties to the InputModel class:

[Required]
[DataType(DataType.Text)]
[StringLength(50, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 2)]
[Display(Name ="First Name")]
public string FirstName { get; set; }

[Required]
[DataType(DataType.Text)]
[StringLength(50, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 2)]
[Display(Name = "Last Name")]
public string LastName { get; set; }

[DataType(DataType.Text)]
[MaxLength(50)]
public string Street { get; set; }

[DataType(DataType.Text)]
[MaxLength(50)]
public string City { get; set; }

[DataType(DataType.Text)]
[MaxLength(50)]
public string Province { get; set; }

[DataType(DataType.Text)]
[MaxLength(15)]
[Display(Name = "Postal Code")]
public string PostalCode { get; set; }

[DataType(DataType.Text)]
[MaxLength(35)]
public string Country { get; set; }

In the same  \Itentity\Pages\Account\Register.cshtml.cs code-behind file, edit the code in the OnPostAsync() method so that line:

var user = new ApplicationUser { UserName = Input.Email, Email = Input.Email };

is changed to:

var user = new ApplicationUser {
  UserName = Input.Email,
  Email = Input.Email,
  FirstName = Input.FirstName,
  LastName = Input.LastName,
  Street = Input.Street,
  City = Input.City,
  Province = Input.Province,
  PostalCode = Input.PostalCode,
  Country = Input.Country
};

Next, let's update the UI. Edit the Razor view page Areas\Itentity\Pages\Account\Register.cshtml. Add the following markup to the form right before the email block:

<div class="form-group">
  <label asp-for="Input.FirstName"></label>
  <input asp-for="Input.FirstName" class="form-control" />
  <span asp-validation-for="Input.FirstName" class="text-danger"></span>
</div>
<div class="form-group">
  <label asp-for="Input.LastName"></label>
  <input asp-for="Input.LastName" class="form-control" />
  <span asp-validation-for="Input.LastName" class="text-danger"></span>
</div>
<div class="form-group">
  <label asp-for="Input.Street"></label>
  <input asp-for="Input.Street" class="form-control" />
  <span asp-validation-for="Input.Street" class="text-danger"></span>
</div>
<div class="form-group">
  <label asp-for="Input.City"></label>
  <input asp-for="Input.City" class="form-control" />
  <span asp-validation-for="Input.City" class="text-danger"></span>
</div>
<div class="form-group">
  <label asp-for="Input.Province"></label>
  <input asp-for="Input.Province" class="form-control" />
  <span asp-validation-for="Input.Province" class="text-danger"></span>
</div>
<div class="form-group">
  <label asp-for="Input.PostalCode"></label>
  <input asp-for="Input.PostalCode" class="form-control" />
  <span asp-validation-for="Input.PostalCode" class="text-danger"></span>
</div>
<div class="form-group">
  <label asp-for="Input.Country"></label>
  <input asp-for="Input.Country" class="form-control" />
  <span asp-validation-for="Input.Country" class="text-danger"></span>
</div>

Run the web application and click on the Register button on the top-right side.

When you click on the register button all the user data is saved in the database. You can verify that data has indeed been saved by viewing data in the AspNetUsers database table using "SQL Server Object Explorer":


We have succeeded in seeding user and role data and subsequently updating the registration page so that additional user data is captured. Thanks for coming this far in the tutorial.

References:

ASP.NET Core 2.1.0-preview1: Introducing Identity UI as a library

Thursday, February 9, 2017

Enabling CORS & Token Authentication in an ASP.NET WEB API application

Learn how to develop an ASP.NET WEB API application that works across domains by enabling CORS. Also, find out how you can
  • create users by making JSON requests to the account controller
  • login
  • obtain a token
  • use the token whenever a request for a service is made
  • use Postman to simulate the login process.


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/