DPS907 WSA500 Assignment 11

Clients that interact with a web service.

Read/skim all of this document before you begin work.

 

Due date

Wednesday, November 30, 2016, at 11:00pm ET

Grade value: 4% of your final course grade

If you wish to submit the assignment before the due date and time, you can do that.

 

Objective(s)

Demonstrate that you can interact with a web service, using two different kinds of clients.

 

Introduction to the problem to be solved

The IA Server is now familiar to you, and you can successfully interact with it by using the Fiddler client app.

Now, we need two simple client apps that will interact with the IA Server. Each will use a different client technology, but will do similar tasks. One will be an ASP.NET MVC web app, and the other will be an HTML5 app, which means JavaScript.

 

Specifications overview and work plan

The following specifications apply to all of your assignments:

  • Follows best practices
  • Implements the recommended system design guidance
  • Customized appearance on the landing web page

For this assignment, here is what we’re looking for:

  • Add a small number of app claims (to the master list of allowable claims)
  • Add a small number of user accounts with claims
  • Request an access token
  • Get all app claims
  • Get all user accounts

 

During the class/session, your professor will help you get started and make progress on this assignment.

Every week, in the computer-lab class/session, your teacher will record a grade when you complete a specific small portion of the assignment. We call this “in-class grading”.

The in-class grading will be announced in-class by your professor.

 

Getting started

In this week’s folder (Week 11) on the GitHub code example repository, you will see the IAServerV2 (IA Server) project. Get it, and plan to use it as the target web service that will be used by the new client apps.

Create a new project, named Assign11. It will be an ASP.NET MVC web app, with no security. This will be the project that will hold the two kinds of clients that will interact with the IA Server.

New: In the code example repository, there are two other code assets, Manager.cs, and manager.js. You can use these to help you design and code the important part of your Assign11 app. They can be used as-is and extended, or as a source code example of the typical client interactions.

Remember to customize the home controller’s index view with your personal information, and the _Layout.cshtml view template with the application name.

Build/compile, and run (without debugging), to ensure that the app’s home > index view loads in a browser.

 

Doing the work

The IA Server app should work correctly. However, it does not have any app claims yet, nor does it have any user accounts.

Fix that now. A couple of weeks ago, in Assignment 9, you created some app claims and user accounts. This was done in code, in the IdentityInitialize class.

For this assignment, you can re-use your code (if it worked correctly, and if you’re happy with it). In other words, you can create the same app claims. If you created additional users in code, you can reuse that code too. If not, then add three or four new users.

Test your work: Build/compile, and run, to ensure that the users and claims were created. Remember:

  • The existing Maintenance (web app) controller shows a list of user accounts, on a web page
  • Using Fiddler, the existing UAM (web service) controller will return a collection of user accounts
  • Using Fiddler, the existing AppClaims (web service) controller will return a collection of allowable app claims

All three controllers require authentication, so login (in the UI) or get an access token when you’re testing.

 

CORS for JavaScript clients

We must add CORS support to the IA Server, so that JavaScript clients can be supported.

As you recently learned (in the November 22 notes), add CORS support to the IA Server.

 

The remaining work will be done in the client apps. Both apps will implement the same use cases:

  1. Request an access token (i.e. “authenticate”)
  2. Get all app claims
  3. Get all user accounts

 

Client app – ASP.NET MVC web app

Make sure that you study the relevant portions of this week’s class notes, including the linked material.

You will recall the BTI420 (web apps) code example, Load Data From Web Service. Its Manager class has an “HTTP request factory” method. You will want this method for your own Manager class. Read its code comments to learn how it works, or to refresh your memory if you have used it in the past.

Therefore, create a Manager class in your new Assign11 web app. Add the “CreateRequest” method from the BTI420 code example. This will enable the other manager methods to easily create and use an HTTP client object. Before continuing, change the base address to the URL of your IA Server web service.

We can use the existing “Home” controller that is created by the new project template. We will add new methods to support the use cases. You can delete the existing About() and Contact() methods, and their views. And, in the _Layout.cshtml view, edit the navigation menu hyperlinks, and replace About and Contact with links to the use cases.

menu-bar

 

When you are writing code, make sure the IA Server is running, because it is the target app for your work below.

 

Add support for interacting with a web service

Using the NuGet Package Manager, add the following package:

Microsoft.AspNet.WebApi.Client

This will add media formatting (to convert an HTTP entity body to or from an object/collection), and content negotiation. We need these features.

Next, below, we present some information about the implementation of the use cases.

 

Request an access token

The use cases will work only for authenticated requests. Therefore, our first task is to figure out how to get an access token. We need the following:

  1. A Manager class method, that will send a request to the IA Server token endpoint, and then save the access token in the response
  2. A controller method pair; the first delivers a login page/view (HTML Form), the other handles the user input
  3. Views that support the controller method pair

 

Manager class method

First, we will describe the design and functionality. Then, we will summarize the discussion with a step-by-step coding plan. You can use the posted code example to guide your work.

The Manager class method can be named GetAccessToken(). It needs an argument for a “login view model” object, which has two strings, one for user name, and the other for password. For best results, make it return bool, so that we can determine whether the result was successful or not.

As you know from using Fiddler, a request to the token endpoint looks like this:

HTTP method: POST
URL: http://localhost:12121/token
Content-Type header: application/x-www-form-urlencoded
Request data (entity body): grant_type=password&username=user1@example.com&password=Password123!

 

We must create/compose the request data. How? It’s a two-step process:

  1. Package the data into the key-value pairs that we need
  2. Create an object that can be attached to an HTTP request

Packaging the data into key-value pairs can be done with a Dictionary<string, string> object, or with a List<KeyValuePair<string, string>> object. For example:

// Dictionary...
var requestData = new Dictionary<string, string>();
requestData.Add("grant_type", "password");
// etc.

// List of KeyValuePair...
var requestData = new List<KeyValuePair<string, string>>();
requestData.Add(new KeyValuePair<string, string>("grant_type", "password"));
// etc.

 

If an HTTP request includes request data (an entity body), its data type/format must be HttpContent. Essentially, it will convert/transform an object (or collection) into the input stream needed by the HTTP request. As an added feature, it will also create the header(s) that describe the data. In other words, if we are sending form URL encoded data, it will add the appropriate Content-Type header. Nice.

HttpContent has a subclass that’s specially designed for form URL encoded data, FormUrlEncodedContent. Its constructor needs a collection of key-value pairs. We have just created that above. So, we can create one of these FormUrlEncodedContent objects like this:

var content = new FormUrlEncodedContent(requestData);

 

At this point in time, we have the data we need to compose and send a request. We know the HTTP method that we want to use (POST), we know the URL (…/token), and we have the request data, properly packaged and described. What’s next? Creating and sending the request.

Using the HTTP client factory, create a new request object. The accepted coding pattern is to enclose this in a using statement. Take a minute now to review the coding pattern by studying the BTI420 Load Data From Web Service code example that you saw this week. It is also seen in the posted code example:

using (var request = CreateRequest())
{
  // Create a response object
  // If response was successful, do something with the response data
}

 

The BTI420 code examples all use “get” methods. We need HTTP POST. How can we do that? Well, if you review the HttpClient documentation, you will see several methods that begin with “PostAsync”. We’ll use the first one.

PostAsync() is an asynchronous method. The ASP.NET runtime will run it on a background thread. However, it is actually calling another asynchronous method: In the IA Server, the listener method is GrantResourceOwnerCredentials(), which itself is asynchronous. In this situation, when both the IA Server app and the Assign11 apps are running on the same computer/server, we can run into a situation where the handling of this method call chain fails. In other words, it could appear to hang and never complete.

So, we will force the PostAsync() method to complete, and return a result. How? By adding the Result property to the end of the PostAsync() statement:

var response = request.PostAsync("url-to-token-endpoint", content).Result;

 

Next we must do something with the response. As you have seen in the code example, response.Content has a useful ReadAsAsync<T>() method. Specify the type, and it will conveniently convert the response stream of data into something that we can use in our app.

So, let’s create a resource model that looks like the incoming data stream (or part of it anyway). What does the response from the token endpoint look like? You saw this first in the October 18 notes, and then have seen it repeatedly when you have been using Fiddler. It’s a JSON response:

{
  "access_token":"Ll49GG3k1bKjl0R-K2NHOFh(...very long string...)",
  "token_type":"bearer",
  "expires_in":1209599,
  "userName":"student8@example.com",
  ".issued":"Fri, 14 Oct 2016 17:57:30 GMT",
  ".expires":"Fri, 28 Oct 2016 17:57:30 GMT"
}

 

We suggest that you create a Home_vm.cs source code file in the Controllers folder, to hold all the resource models that you will need for this assignment.

Next, create a resource model class, maybe named TokenResponse. It will have properties with names and data types that match exactly to the response data.

Warning – when you attempt to create a C# property name that begins with a dot, you will see a compiler error. What can you do now? Two choices:

First, omit the property. In other words, don’t bother defining properties for the .issued and .expires data. You will probably not need that data, and you certainly will not need the data for this assignment.

Alternatively, omit the dot, and then prefix the property with an attribute that will help the convert/transform process do its work. For example, just before the “expires” property statement, add this:
[JsonProperty(“.expires”)]

 

Now you have what you need to convert/transform the response data into an object. After you get that object, you can extract the access token (which is a string), and save it. Save it where? Session state.

Before continuing, study (again) the HTTP client factory. Notice that it attempts to read a value from HttpContext.Current.Session. If found, it will create an “Authorization” header.

Session state is a private, per-session and in-memory storage area on the server. You can create and save values in session state easily, and then easily extract them. We use a simple name (e.g. “token”) for a value, and the value/data itself (for our work) is a string. To create a new session state value, we do something like this:

HttpContext.Current.Session["token"] = tokenResponse.access_token;

 

In summary, here is the coding design plan for the manager method:

  1. Clean the incoming user name and password data to make sure it’s valid and usable
  2. Package the data we need into a collection of key-value pairs
  3. Using the package of data, create a content object
  4. Create the HttpClient “request” object
  5. Create its “response” object by sending the request to the correct URL and with the content
  6. If the response was successful, read the response data into an object
  7. Extract the access token value, and save it in session state, and then return “true” (successful)
  8. If the response was not successful, return “false” (not successful)

 

Now, we can use this manager method. We now have to create a controller method pair, and a view.

 

Controller method pair to handle “login”

As you remember, in ASP.NET MVC, we use a pair of methods for a use case that gathers data from a user. A “GET” method will render an HTML Form to the user. A “POST” method handles the incoming data from the user.

Review, FYI…

You can go back to the beginning of the BTI420 course notes, assignments, and code examples to see the best-practice coding pattern.

The January 15 notes introduces the pattern.

Assignment 1 walks you through – in detail – the lifecycle. See the “Create a new phone object” section.

The AddNew code example has code and a view that you can study.

 

The “GET” method simply presents the view to the user.

You will need a resource model for the user’s credentials (user name and password). Write that now. Make sure that you use data annotations to control the quality of the data, and its appearance in the view.

When you create the view, here is a tip: Use the “Create” template, and specify the just-created model class. This will improve the scaffolding (code generating) result, and reduce the amount of editing work that you must do.

login

 

The “POST” method accepts data from the user, and passes it to a manager method call. If successful, it should redirect back to the Index view.

At this point in time, a user can login, and the app saves its access token for future requests.

 

Get all app claims

The IA Server has an AppClaims controller, and a method that will fetch all the active app claims. Let’s request that resource from our client app, and display them in a list view.

Please note that the current configuration of the IA Server allows only requests that have the “UserAccountAdministrator” role claim. Look at the Authorize attribute in the AppClaims controller to verify this.

Therefore, make sure that you login with a user account that has that role (e.g. admin@example.com).

 

First, we will need the AppClaim resource models. We suggest that you simply copy the AppClaim_vm.cs source code file from the IA Server project (and obviously edit its namespace).

Next, create a method in the Manager class to request the resource, and return the collection of AppClaimBase objects. The coding pattern will match the BTI420 code example (as it’s a GET, and not a POST). If the response does not have a success status code, return a new empty collection, instead of null. (That will make the controller coding easier.)

In the controller, add a method that calls the manager method.

The new view will use the “List” template.

all-claims

 

Get all user accounts

The IA Server has a UAM (user accounts manager) controller, and a method that will fetch all the user accounts. Let’s request that resource from our client app, and display them in a list view.

First, we will need the UAM resource models. We suggest that you simply copy the UAM_vm.cs source code file from the IA Server project (and obviously edit its namespace).

Next, create a method in the Manager class to request the resource, and return the collection of MSUserAccountBase objects. This is another GET method. As above, if the response does not have a success status code, return a new empty collection, instead of null. (That will make the controller coding easier.)

In the controller, add a method that calls the manager method.

The new view will use the “List” template.

all-user-accounts

 

Client app – HTML5 (JavaScript)

Make sure that you study the relevant portions of this week’s class notes, including the linked material.

We remind you again about the BTI420 (web apps) code example, Load Data From Web Service. It has an HTML5 app, “ArtistsHTML5Page.html”. (Obviously, it references CSS and JavaScript resources.) For this assignment, we will do something similar.

Create a new asset in the root of the Assign11 project, probably named HTML5Home.html. If you wish, you can copy some of the page/structure content from the BTI420 code example, which will bring a number of benefits, including a document layout/structure, links to CSS, etc. The content inside the div element with classes “container” and “body-content” will be replaced by new content. If you render the ASP.NET MVC web app’s home page in a browser, you can then view its source code, and copy-paste some of the home page content to the HTML5Home.html document. That’s an easy way to get the new content.

Before continuing, edit the navigation menu. Add links to the pages that you will create, below:

  • HTML5Home.html
  • HTML5AppClaims.html
  • HTML5AllUA.html
  • HTML5Login.html

 

In the project’s Scripts folder, create another folder named App. In that folder, create a new JavaScript source code file, probably named “manager.js”. This will be used to hold our functions. Another reminder, you can use the posted code example to guide your work.

 

General format of a pure JavaScript XMLHttpRequest

This is the general format of an XMLHttpRequest in pure JavaScript, with token-handling:

function doSomething() {

  // create an xhr object
  var xhr = new XMLHttpRequest();

  // configure its handler
  xhr.onreadystatechange = function () {

    if (xhr.readyState === 4) {
      // request-response cycle has been completed, so continue

      if (xhr.status === 200) {
        // request was successfully completed, and data was received, so continue

        // If you're interested in seeing the returned JSON...
        // Open the browser developer tools, and look in the JavaScript console
        console.log(xhr.responseText);

        // Get the response data
        var responseData = JSON.parse(xhr.responseText);

        // ADD MORE CODE HERE - do something with the returned object/collection
      }
    }
  }

  // configure the xhr object to fetch content
  xhr.open('get', 'http://localhost:12121/path/to/resource', true);

  // set the request headers 
  xhr.setRequestHeader('Accept', 'application/json'); 

  // fetch the token
  var token = sessionStorage.getItem('token');
  if (!token) {
    token = 'Empty';
  }
  xhr.setRequestHeader('Authorization', 'Bearer ' + token);

  // send the request
  xhr.send(null);
}

 

You will be using this general format to fetch app claims and user accounts, later.

But first, we must design and code the login functionality.

 

Login from an HTML5 app

To support login, we need an HTML web page, and a JavaScript function.

In the root of the project, create an HTML page, probably named HTML5Login.html. If you wish, you can use Solution Explorer to copy-paste the HTML5Home.html source code file, and then edit its contents (titles, and delete the main body content).

Inside the main container, or in the head element, reference the manager.js source code file.

The login form needs two textboxes and a button. Add them now.

Looking for an easy way to do that?

Open the ASP.NET MVC web app’s login page in a browser. Then, view its source code. Copy the HTML Forms code into your new page. Then, edit as follows:

The opening “form” tag needs an id attribute. However, you can delete the other attributes.

The input type=”submit” element must be changed to input type=”button”, with an onclick attribute that has a value which names the to-be-written “get access token” function in your manager.js source code file. For example, onclick=”getAccessToken();”.

 

Next, add a “get access token” function to your JavaScript source code file, manager.js.

The function will generally look like the one above, with a few differences. So, you can use (copy) it, and edit, or use the one in the posted code example. Let’s go top-to-bottom…

The login process needs a user name and password from the HTML Form. Therefore, we must get a reference to the form (that’s why we added an id attribute above).

Then, in the completion handler, get the response data. As you know, it will be a JSON object. One of its properties will be “access_token”. Save its value. How?

In the ASP.NET MVC web app, we used session state. The browser has a similar facility, called HTML5 Web Storage, specifically “session storage”. We can easily use it from JavaScript. “sessionStorage” is a top-level object of the DOM.

To save (set) a value in the browser’s session storage:

sessionStorage.setItem('keyname', 'value');

Later, to read a value from the browser’s session storage (which you saw earlier/above):

var foo = sessionStorage.getItem('keyname');

Continuing with the “login” function’s code…

Near the bottom of the function, we must package the data needed for the request to the token endpoint. This process is a bit different than the one we did earlier, because here, we just create a simple string, using string concatenation. Remember, the string needs a grant type, user name, and password.

While we’re here, create a new request header for the form URL encoded content type.

The XMLHttpRequest object’s “open” statement needs to use the “post” HTTP method.

For the request headers, yes, we need to accept JSON.

Lastly, when we “send” the request, we must send the data package we just created (with the grant type, user name, and password).

login-html5

 

Get all app claims

For this use case, we need an HTML web page, and a JavaScript function.

In the root of the project, create an HTML page, probably named HTML5AppClaims.html. If you wish, you can use Solution Explorer to copy-paste the HTML5Login.html source code file, and then edit its contents (titles, and delete most of the main body content).

As above, this document needs a reference to the manager.js code file. Also, near the bottom, it needs a script element which calls the “get app claims” function that you will write soon.

The page needs a table element/structure, so that it can nicely display a table of app claims. Add it now.

Looking for an easy way to do that?

Open the ASP.NET MVC web app’s “app claims” page in a browser. Then, view its source code. Copy the table structure into your new page. Then, edit as follows:

The table needs an id attribute.

If the table does not have a tbody element, add a tbody element.

Delete the table row data. It will be programmatically (re-)created when the page loads.

 

Next, add a “get app claims” function to your JavaScript source code file, manager.js.

The function will generally look like the one above, with a few differences. So, you can use (copy) it, and edit, or use the one in the posted code example. Let’s go top-to-bottom…

In the completion handler, get the response data. As you know, it will be a JSON collection, which is an array.

Now, do something with the data. You can add more code to the body of this completion handler function, or you can call out to a new separate function that you can write.

Get a reference to the table. (That’s why we added an id attribute above.) Then, go through the collection/array, and build a table row from the data object.

Continuing with the “get app claims” function’s code…

Near the bottom of the function, we must fetch the access token value from the browser memory’s session storage. We use that data to create an “Authorization” request header.

While we’re here, create a new request header to accept a JSON content type as the response.

Lastly, we “send” the request to the correct endpoint.

app-claims-html5

 

Get all user accounts

This process will be similar to the just-completed “get app claims” process.

For this use case, we need an HTML web page, and a JavaScript function.

In the root of the project, create an HTML page, probably named HTML5UA.html. If you wish, you can use Solution Explorer to copy-paste the HTML5AppClaims.html source code file, and then edit its contents (titles, and delete most of the main body content).

As above, this document needs a reference to the manager.js code file. Also, near the bottom, it needs a script element which calls the “get user accounts” function that you will write soon.

The page needs a table element/structure, so that it can nicely display a table of user accounts. Add it now. Use a similar procedure to capture the table structure from the ASP.NET MVC web app version of this functionality, and edit its content.

Next, add a “get user accounts” function to your JavaScript source code file.

The function will generally look like the one above for “get app claims”, with a few differences. Its table processing will be different, and the URL will be different. Other than those, it will be very similar.

all-user-accounts-html5

 

Testing your work

If your client apps work correctly, then your work has been tested. For this assignment, we do NOT use Fiddler.

 

Reminder about academic honesty

You must comply with the College’s academic honesty policy.

Although you may interact and collaborate with others, you must submit your own work.

 

Submitting your work

This Assignment 11 has a modified submission process (and is similar to the process for the previous Assignment 10).

We need both projects to be able to grade your work.

Therefore, here is the modified procedure:

  1. Make copies of both projects
  2. For each project, remove its packages, bin, and obj folders
  3. Zip both projects together (in one zip file), and upload to the designated location on My.Seneca/Blackboard before the due date-and-time

 

For example, assume that you are currently viewing a folder named “A11” in File Explorer.

It should have these two sub-folders:

  1. IA
  2. Assign11

Select both folders, then zip them into one zip file result.

 

 

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

%d bloggers like this: