Wednesday, March 29, 2017

Build an ASP.NET Core 1.1 tag helper that consumes Azure Web API RESTful service

In a previous post, I demonstrated how to create an ASP.NET Core 1.0 tag helper. Since ASP.NET 1.1 was released, some of the packages in the previous post are not supported anymore. Therefore, I am hereby updating this article to fix this issue. I am also using Visual Studio 2017 instead of Visual Studio 2015.

Tag Helpers in ASP.NET Core allow you to create your own tags that fulfill a server-side purpose. In this tutorial, I will show you how to create a tag helper <toon> that accesses a Web API service and displays contents in any razor view page.

The Web API service we will consume in this exercise is located at http://cartoonapi.azurewebsites.net/api/cartoon. It delivers the names of cartoon characters and their respective images.

1) To start with, create an ASP.NET Core Web application named ToonTagHelper in Visual Studio 2017.

2) We need to create a class that closely matches the nature of the Web API JSON object. Therefore, add the following Toon class to a Models folder in your project:
public class Toon {
  public string Name { get; set; }
  public string PictureUrl { get; set; }
}
3) Next, we need to install the We API Client libraries. To that end, execute the following commands from within the Package Manager Console window in Visual Studio 2017:
Install-Package Newtonsoft.Json
Install-Package System.Net.Http
Install-Package System.Runtime.Serialization.Xml
Alternatively, you can run the following commands from within a terminal window:
dotnet add package Newtonsoft.Json
dotnet add package System.Net.Http
dotnet add package System.Runtime.Serialization.Xml

This should add the following dependencies to your .csproj file:
<PackageReference Include="Newtonsoft.Json" Version="10.0.1" />
<PackageReference Include="System.Net.Http" Version="4.3.1" />
<PackageReference Include="System.Runtime.Serialization.Xml" Version="4.3.0" />
4) Add the following tag to the bottom of Views/Home/About.cshtml:


<toon></toon>

5) Create a folder named TagHelpers and add to it a class file named ToonTag.cs. Have the class inherit from TagHelper and implement the ProcessAsync() method as follows:

public async override Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
}


We could have implemented a method Process() instead. However, in our case, it is appropriate to implement ProcessAsync() instead because we are about to make an async call to a remote service.

6) Add the following instance variable to the ToonTagHelper class:
 private string baseUrl = "http://cartoonapi.azurewebsites.net";

7) Annotate the CartoonCharactersTagHelper class with the following:

[HtmlTargetElement("toon")]
[HtmlTargetElement(Attributes = "toon")]


The first annotation defines the tag <toon> and the second defines the “toon” attribute. This means that we have two different ways to produce the same output on a razor .cshtml view.

8) Add the following method to the ToonTagHelper class:

async Task<IEnumerable<Toon>> GetToonsAsync() {
  HttpClient client = new HttpClient();
  client.BaseAddress = new Uri(baseUrl);
  client.DefaultRequestHeaders.Accept.Clear();
  client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
  IEnumerable<Toon> toons = null;
  try {
    // Get all cartoon characters
    HttpResponseMessage response = await client.GetAsync("/api/cartoon");
    if (response.IsSuccessStatusCode)
    {
       string json = await response.Content.ReadAsStringAsync();
      toons = JsonConvert.DeserializeObject<IEnumerable<Toon>>(json);
    }
  } catch (Exception e) {
      System.Diagnostics.Debug.WriteLine(e.ToString());
  }
  return toons;
}



The above code makes a request to the Web API service and returns an IEnumerable<Toon> collection with the results.

9) Add the following code inside the ProcessAsync() method:

IEnumerable<Toon> toons= await GetToonsAsync();
string html = string.Empty;
html += "<table><tr><th>Name</th><th>Picture</th></tr>";
foreach (var item in toons) {
    string photoUrl = baseUrl + "/" + item.PictureUrl;
    html += "<tr>";
    html += "<td>" + item.Name + "</td>";
    html += "<td><img src='" + photoUrl + "' style='width: 50px' /></td>";
    html += "</tr>";
}
html += "</table>";
output.Content.SetHtmlContent(html);


The above code creates a table with the collection of cartoon characters so that it can be displayed wherever the tag helper is used.

11) Register the tag name in the Views/_ViewImports.cshtml file by adding the following to the list of tags that are already there:

@addTagHelper "ToonTagHelper.TagHelpers.ToonTag, ToonTagHelper"

You may need to adjust the above names depending on what you called your app and/or your tag helper class.

11) Compile and run your application, then click on About. You should see the following output:



If you inspect the table in your browser, you will see the following:



The above is using the tag and not the attribute. Edit About.cshtml and comment out “<toon></toon>” and put the following <div> tag with the toon attribute underneath it:
<div toon></div>

Your About.cshtml should now look like this:
@{
    ViewData["Title"] = "About";
}
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>

<p>Use this area to provide additional information.</p>
@*<toon></toon>*@
<div toon></div>
When you run your application. you should see the same output as before. However, upon inspection of the HTML source, you will notice that a <div> tag is the primary container for our code rather than a <toon> tag:



This proves to us that you can either use tags or attributes with TagHelpers in ASP.NET Core.

Friday, March 24, 2017

Token authentication with OpenIdDict in ASP.NET Core 1.1

OpenIddict provides a simple solution to implement an OpenID Connect server for any ASP.NET Core 1.1 application.

The official GitHub project is located at https://github.com/openiddict/openiddict-core. In this tutorial, I will show you how to easily implement OpenIdDict with a very basic ASP.NET Core 1.1 MVC/WebAPI project created using the dotnet command line interface. Let’s get started:
Pre-requisites:

Creating web application project in Visual Studio 2015

To ensure that you have the correct version of .NET Core installed on your computer, execute the following command in a terminal window:

dotnet --version

If the number 1.01 appears then you have the correct version. We need to know what templates we can use to generate our ASP.NET application using the dotnet command line interface. Type the following inside your terminal window:

dotnet new --list

At the bottom of the output, you should see this:

Templates                 Short Name      Language      Tags
----------------------------------------------------------------------
Console Application       console         [C#], F#      Common/Console
Class library             classlib        [C#], F#      Common/Library
Unit Test Project         mstest          [C#], F#      Test/MSTest
xUnit Test Project        xunit           [C#], F#      Test/xUnit
ASP.NET Core Empty        web             [C#]          Web/Empty
ASP.NET Core Web App      mvc             [C#], F#      Web/MVCASP.NET Core Web API      webapi          [C#]          Web/WebAPI
Solution File             sln                           Solution


Let us use the MVC template to generate an ASP.NET Core 1.1 application. In your workspace, create a folder named AspToken and go into that folder as follows:
mkdir AspToken
cd AspToken
Use the following command to create an app with individual authentication and SQLite:

dotnet new mvc --auth Individual --framework netcoreapp1.1

In order to run the application, execute the following commands in sequence:
dotnet restore
dotnet ef database update
bower install bootstrap
dotnet run
Point your browser to http://localhost:5000 and you will see a page that looks like this:

image

Click on Register on the top right-side and create a user. I created a user with email = a@a.a and password = P@$$w0rd.

Once you create the user, logout.

Open the root folder of your application in Visual Studio Code (or any other editor you prefer). In Visual Studio Code, it will look like this:

image

Inside the Controllers folder, create a file named ValuesController.cs and add to it the following code:
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
namespace AspToken.Controllers {
  [Produces("application/json")]
  [Route("api/Values")]
  public class ValuesController : Controller {
    // GET: api/Values
    [HttpGet]
    public IEnumerable<string> Get() {
        return new string[] { "value1", "value2" };
    }

    // GET: api/Values/5
    [HttpGet("{id}", Name = "Get")]
    public string Get(int id) {
        return "value";
    }
    // POST: api/Values
    [HttpPost]
    public void Post([FromBody]string value) {
    }
    // PUT: api/Values/5
    [HttpPut("{id}")]
    public void Put(int id, [FromBody]string value) {
    }
    // DELETE: api/ApiWithActions/5
    [HttpDelete("{id}")]
    public void Delete(int id) {
    }
  }
}
Stop the web server, execute “dotnet build” followed by “dotnet run”. The route that will be used for this controller is “api/Values”. To view the output from this controller, add /api/values to the localhost address in your browser. The following will be what the API looks like in a browser:

image


To enforce authentication, add the [Authorize] annotation to the ValuesController class. If you run your app again and refresh the page, you will see the following:

image


Obviously, a login dialog is of no use to API services because we need to find a way to send our credentials through API calls by passing tokens rather than using a login form. It is at this juncture that we decide to use the OpenIdDict framework.
Adding OpenIdDict to the web application project
1) Create a file in the main solution directory named NuGet.Config and add to it the following XML markup:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <add key="nuget.org" value="
https://api.nuget.org/v3/index.json" />
    <add key="aspnet-contrib" value="
https://www.myget.org/F/aspnet-contrib/api/v3/index.json" />
  </packageSources>
</configuration>


Contents of your root solution directory will look like this:

image


The NuGet.Config file is needed because OpenIdDict is not yet an official Nuget release at the time of writing this article.

2) Update your .csproj project file with the following dependencies:

<!-- OpenIdDict -->
<PackageReference Include="AspNet.Security.OAuth.Validation" Version="1.0.0-*" />
<PackageReference Include="OpenIddict" Version="1.0.0-*" />
<PackageReference Include="OpenIddict.EntityFrameworkCore" Version="1.0.0-*" />
<PackageReference Include="OpenIddict.Mvc" Version="1.0.0-*" />


3) In the Startup.cs file, make the following changes at the top of ConfigureServices() method:

Replace this:
services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlite(Configuration.GetConnectionString("DefaultConnection")));
With this:
services.AddDbContext<ApplicationDbContext>(options =>{
  options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"));
  options.UseOpenIddict();
});
4) Also in the Startup.cs file, add the following code to the ConfigureServices() method right before services AddMvc():

// Configure Identity to use the same JWT claims as OpenIddict instead
// of the legacy WS-Federation claims it uses by default (ClaimTypes),
// which saves you from doing the mapping in your authorization controller.
services.Configure<IdentityOptions>(options =>
{
  options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
  options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
  options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});


services.AddOpenIddict(options =>
{
  // Register the Entity Framework stores.
  options.AddEntityFrameworkCoreStores<ApplicationDbContext>();
  // Register the ASP.NET Core MVC binder used by OpenIddict.
  // Note: if you don't call this method, you won't be able to
  // bind OpenIdConnectRequest or OpenIdConnectResponse parameters.
  options.AddMvcBinders();
  // Enable the token endpoint.
  options.EnableTokenEndpoint("/connect/token");
  // Enable the password flow.
  options.AllowPasswordFlow();
  // During development, you can disable the HTTPS requirement.
  options.DisableHttpsRequirement();
});

The above code sets up an endpoint for login through token authentication. A controller will be added later that provides an endpoint at /connect/token.

Build your app to ensure there are no syntax or compiler errors.

5) Add the following code to the Configure() method in Startup.cs between app.UseIdentity() and app.UseMvc():
app.UseOAuthValidation();
app.UseOpenIddict();
This adds OpenIddict and the OAuth2 token validation middleware to the ASP.NET Core pipeline.

6) In the Startup.cs file, make the following changes in the Configure() method:

Replace this:
app.UseMvc(routes =>
{
  routes.MapRoute(
      name: "default",
      template: "{controller=Home}/{action=Index}/{id?}");
});
With this:
app.UseMvcWithDefaultRoute();
7) You need to add an authorization controller to provide the endpoint /connect/token. Copy the authorization controller from the OpenIdDict project on GitHub and add it to the Controllers folder. Alternatively, you can instead use the code below for the AuthorizationController class:

/*
 * Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 * See https://github.com/openiddict/openiddict-core for more information concerning
 * the license and the contributors participating to this project.
 */

using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using AspNet.Security.OpenIdConnect.Extensions;
using AspNet.Security.OpenIdConnect.Primitives;
using AspNet.Security.OpenIdConnect.Server;
using AspToken.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using OpenIddict.Core;

namespace AspToken.Controllers
{
    public class AuthorizationController : Controller {
        private readonly IOptions<IdentityOptions> _identityOptions;
        private readonly SignInManager<ApplicationUser> _signInManager;
        private readonly UserManager<ApplicationUser> _userManager;

        public AuthorizationController(
            IOptions<IdentityOptions> identityOptions,
            SignInManager<ApplicationUser> signInManager,
            UserManager<ApplicationUser> userManager) {
            _identityOptions = identityOptions;
            _signInManager = signInManager;
            _userManager = userManager;
        }

        [HttpPost("~/connect/token"), Produces("application/json")]
        public async Task<IActionResult> Exchange(OpenIdConnectRequest request) {
            Debug.Assert(request.IsTokenRequest(),
                "The OpenIddict binder for ASP.NET Core MVC is not registered. " +
                "Make sure services.AddOpenIddict().AddMvcBinders() is correctly called.");

            if (request.IsPasswordGrantType()) {
                var user = await _userManager.FindByNameAsync(request.Username);
                if (user == null)  {
                    return BadRequest(new OpenIdConnectResponse
                    {
                        Error = OpenIdConnectConstants.Errors.InvalidGrant,
                        ErrorDescription = "The username/password couple is invalid."
                    });
                }

                // Ensure the user is allowed to sign in.
                if (!await _signInManager.CanSignInAsync(user)) {
                    return BadRequest(new OpenIdConnectResponse {
                        Error = OpenIdConnectConstants.Errors.InvalidGrant,
                        ErrorDescription = "The specified user is not allowed to sign in."
                    });
                }

                // Reject the token request if two-factor authentication has been enabled by the user.
                if (_userManager.SupportsUserTwoFactor && await _userManager.GetTwoFactorEnabledAsync(user)) {
                    return BadRequest(new OpenIdConnectResponse {
                        Error = OpenIdConnectConstants.Errors.InvalidGrant,
                        ErrorDescription = "The specified user is not allowed to sign in."
                    });
                }

                // Ensure the user is not already locked out.
                if (_userManager.SupportsUserLockout && await _userManager.IsLockedOutAsync(user)) {
                    return BadRequest(new OpenIdConnectResponse {
                        Error = OpenIdConnectConstants.Errors.InvalidGrant,
                        ErrorDescription = "The username/password couple is invalid."
                    });
                }

                // Ensure the password is valid.
                if (!await _userManager.CheckPasswordAsync(user, request.Password)) {
                    if (_userManager.SupportsUserLockout) {
                        await _userManager.AccessFailedAsync(user);
                    }

                    return BadRequest(new OpenIdConnectResponse {
                        Error = OpenIdConnectConstants.Errors.InvalidGrant,
                        ErrorDescription = "The username/password couple is invalid."
                    });
                }

                if (_userManager.SupportsUserLockout) {
                    await _userManager.ResetAccessFailedCountAsync(user);
                }

                // Create a new authentication ticket.
                var ticket = await CreateTicketAsync(request, user);

                return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
            } else if (request.IsRefreshTokenGrantType()) {
                // Retrieve the claims principal stored in the refresh token.
                var info = await HttpContext.Authentication.GetAuthenticateInfoAsync(
                    OpenIdConnectServerDefaults.AuthenticationScheme);

                // Retrieve the user profile corresponding to the refresh token.
                // Note: if you want to automatically invalidate the refresh token
                // when the user password/roles change, use the following line instead:
                // var user = _signInManager.ValidateSecurityStampAsync(info.Principal);
                var user = await _userManager.GetUserAsync(info.Principal);
                if (user == null) {
                    return BadRequest(new OpenIdConnectResponse {
                        Error = OpenIdConnectConstants.Errors.InvalidGrant,
                        ErrorDescription = "The refresh token is no longer valid."
                    });
                }

                // Ensure the user is still allowed to sign in.
                if (!await _signInManager.CanSignInAsync(user)) {
                    return BadRequest(new OpenIdConnectResponse {
                        Error = OpenIdConnectConstants.Errors.InvalidGrant,
                        ErrorDescription = "The user is no longer allowed to sign in."
                    });
                }

                // Create a new authentication ticket, but reuse the properties stored
                // in the refresh token, including the scopes originally granted.
                var ticket = await CreateTicketAsync(request, user, info.Properties);

                return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
            }

            return BadRequest(new OpenIdConnectResponse {
                Error = OpenIdConnectConstants.Errors.UnsupportedGrantType,
                ErrorDescription = "The specified grant type is not supported."
            });
        }

        private async Task<AuthenticationTicket> CreateTicketAsync(
            OpenIdConnectRequest request, ApplicationUser user,
            AuthenticationProperties properties = null) {
            // Create a new ClaimsPrincipal containing the claims that
            // will be used to create an id_token, a token or a code.
            var principal = await _signInManager.CreateUserPrincipalAsync(user);

            // Create a new authentication ticket holding the user identity.
            var ticket = new AuthenticationTicket(principal, properties,
                OpenIdConnectServerDefaults.AuthenticationScheme);

            if (!request.IsRefreshTokenGrantType()) {
                // Set the list of scopes granted to the client application.
                // Note: the offline_access scope must be granted
                // to allow OpenIddict to return a refresh token.
                ticket.SetScopes(new[] {
                    OpenIdConnectConstants.Scopes.OpenId,
                    OpenIdConnectConstants.Scopes.Email,
                    OpenIdConnectConstants.Scopes.Profile,
                    OpenIdConnectConstants.Scopes.OfflineAccess,
                    OpenIddictConstants.Scopes.Roles
                }.Intersect(request.GetScopes()));
            }
            ticket.SetResources("resource_server");

            // Note: by default, claims are NOT automatically included in the access and identity tokens.
            // To allow OpenIddict to serialize them, you must attach them a destination, that specifies
            // whether they should be included in access tokens, in identity tokens or in both.

            foreach (var claim in ticket.Principal.Claims) {
                // Never include the security stamp in the access and identity tokens, as it's a secret value.
                if (claim.Type == _identityOptions.Value.ClaimsIdentity.SecurityStampClaimType) {
                    continue;
                }

                var destinations = new List<string> {
                    OpenIdConnectConstants.Destinations.AccessToken
                };

                // Only add the iterated claim to the id_token if the corresponding scope was granted to the client application.
                // The other claims will only be added to the access_token, which is encrypted when using the default format.
                if ((claim.Type == OpenIdConnectConstants.Claims.Name && ticket.HasScope(OpenIdConnectConstants.Scopes.Profile)) ||
                    (claim.Type == OpenIdConnectConstants.Claims.Email && ticket.HasScope(OpenIdConnectConstants.Scopes.Email)) ||
                    (claim.Type == OpenIdConnectConstants.Claims.Role && ticket.HasScope(OpenIddictConstants.Claims.Roles)))
                {
                    destinations.Add(OpenIdConnectConstants.Destinations.IdentityToken);
                }
                claim.SetDestinations(destinations);
            }
            return ticket;
        }
    }

}

Make sure your application builds without any errors.
Testing authorization with postman
Before proceeding with testing token authentication, make sure you already created a user with email and password.

There is a handy Google Chrome extension called “postman” that you can install from the Chrome Web Store. The icon of the extension I am referring to looks like this:

image


In Postman, do the following:

image


1) Select POST for the HTTP method.
2) Enter the Web API login endpoint. In the above example, the login endpoint is http://localhost:5000/connect/token/. In your case, you may have to only correct the port number.
3) Click on Body.
4) Click on x-www.form-urlencoded.
5) In entered the following parameter name/value pairs:
username: a@a.a
password: P@$$w0rd
grant_type: password
6) Finally, click on the Send button to initiate the request. The response from the server will look like this:

{
  "resource": "resource_server",
  "token_type": "Bearer",
  "access_token": "CfDJ8AJFDskine9Jhavjj9lbv4Rr7pEocsejCXl24utNyr58SbYfZ6-7gDEnZE6CtTGkGIo7YI6HbTVNfxBpKRGtJsZS--RcukRJ9r5da1kVzWHa_Lx0E1om5LMlxBuq_t-OTuyXi5izZMdGVYLi9ldcyweP1nowKm-xFnq87TzbZtkEfHhTGxaoCA2UP7prT9kpNFHR--svvgk14U6R_uMkuE7alUteTwiFWE9PgzbcwEygZWwAk4AN07QrnBbK565z96TMaoxx2giJakJefs40q8uw8aSlHcNJv7xLmeLoI8sDCYoyqjPeSdyn-cv2g_9ZEhL1JnaUeXx8pl5YuIFH2nJwum1hwSemtARx14cgm0Bb4hFQORtKwNInKRDn8PuFYjibK66JRDUb33a1408JJrJsnmN6nzatWSe4Z8tBpi1mZ58Vht6qooT8gPW_shQleZh_qrmmZdcmXRfyJ-uULnS1kD2ml6h-9v_hydrP1a5dwpT4DPus58Gas3gZJAWPjY-llSYBHaw9X2q0_4pH-lX4SvlRkCFVdfoL37-VYCxW",
  "expires_in": 3600
}


In order to access our ValuesController, we will need to pass the access_token.

You can now add the access_token to your “Authorization” header, with the value prefixed by “Bearer “.  Example:
Authorization: Bearer CfDJ8BNb0JA0Y9lNgKzWkKXsTwf-4RPIpzDaVFES95ETNNIx-q_Qn2hW2PA5he39PPGHPuSCPadHaOeLWYK5hlJe-ZBLZjojcwjZYJTUNP6uhgG3CikaN-ES1tadOyJmEmaOjLe2QeY09AaziWP3SB7quEIPpFOeKyKMieHuOBVx_-R51XyUyVLRMfI7fWEKeEt35PbXMPYOochUybSLaxrNkQL9x1Wuj0l2igspn4hnpmvG76a_VEuRSxyDfZy6VsukPhxfzhlcrd_bvIchg5uMGEiJmbOvbRbIR5c_6Wya2uD6s5yMk8NYyAE6cebLMU_4sZ0pA2Qkzlilun_5sD7GsdlpE9pbzwv3I6liAI0twnsCoB06e4KD5bJUYCTIdTu-qQ2-GTdSlxk3iiCvHNl72R9t3hRvU8VuKld2g4Wc1PWsKzL1IkFbNIG_2CoHWmaAv09OnnpD3rrE-E6BA4gCkvRTUflf6Dc_yvN03InTs_SNOPyybjtVI_MG5o2CKfRClU9ERvFQYHBeqhZlnt2cLm2SBOx0wbexwBYF0nwWjjxLbWBndkodLqVEMtIOrbptRaacJa2MrpfIMbOvax_xvogqPzwZ8vu97BU4IvgTvpGw52HDLj9UZgN4TC_7ZDFd41ZQla7qvBN2Z4o3NjY-sdFJ6K7uXkx0CI9KFivhG_dpSB4VXoXer5ndDc5sExm1iGhjUe5qLJYnf37cm_a_ov7nYSFnkKeWuLgX0mzc2XQOzztFM8UXSfJmdftEBoD9kz_nNQpSWewkhnVQzDRGFl-UhCbkgM7Z78fYIqylT39-
Back in postman, do the following:

image


1) Select GET for the HTTP method.
2) Enter the ValuesController endpoint. In the above example, the login endpoint is http://localhost:5000/api/values/. Again, you may have to only correct the port number.
3) Click on Headers.
4) Enter Authorization for the header parameter.
5) For the Authorization header parameter value, enter the access_token prefixed by the word Bearer followed by a space.
6) Click on the Send button. The response should be the expected Web API output from the ValuesController as follows:

image


You should now be able to secure your Web API data in ASP.NET Core 1.1 so that only those who are authenticated can view the data.
References:
https://github.com/openiddict/openiddict-core
http://overengineer.net/Using-OpenIddict-to-easily-add-token-authentication-to-your-.NET-web-apps
http://kerryritter.com/authorizing-your-net-core-mvc6-api-requests-with-openiddict-and-identity/
http://stackoverflow.com/questions/41122686/the-type-or-namespace-name-openiddictdbcontext-could-not-be-found
https://github.com/openiddict/openiddict-samples/tree/master/samples/RefreshFlow

Sunday, March 19, 2017

Test Driven Development (TDD) with xunit and .NET Core 1.1

It is assumed that you have .NET Core 1.1 installed on your computer. If you do not already have it, you can obtain .NET Core 1.1 from https://www.microsoft.com/net/download/core.
We will create the test cases for a ET Core 1.1 application named FizzBuzz. Here’s how it goes. If a number is divisible by 3 then you will call out Fizz. If a number is divisible by 5 then you will call out Buzz. If a number is divisible by 3 and 5 then you will call out FizzBuzz. Otherwise, you will just call out the number itself.

Here are some examples:
2 12
4 1 2 Fizz 4
5 1 2 Fizz 4 Buzz
15 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Fuzz 11 Fizz 13 14 FizzBuzz

The above table would represent our four test cases.

Directory & file structure

Create the following directory structure in your workspace directory:
image

Creating the business logic project

There are a number of built-in templates that are available to the developer. You con view these templates by typing the following in a terminal window:

dotnet new template --list

In a command line, while in the src/FizzBuzzLibrary directory, create the library project files by executing the following command:
dotnet new classlib
Similarly, in a command line while the test/FizzBuzzTests directory, create the test project files by executing the following command:
dotnet new xunit
If you open the root FizzBuzz folder in Visual Studio Code, your directories & files will look like this:
 
image

Rename the src/FizzBuzzLibrary/Class1.cs file to src/FizzBuzzLibrary/FizzBuzzMaster.cs. Open src/FizzBuzzLibrary/FizzBuzzMaster.cs in the editor and change the class name from Class1 to FizzBuzzMaster.

Similarly, rename test/FizzBuzzTests/UnitTest1.cs to test/FizzBuzzTests/FizzBuzzTestsMaster.cs. Open test/FizzBuzzTests/FizzBuzzTestsMaster.cs in the editor and change the class name from UnitTest1 to FizzBuzzTestsMaster.

Open the src/FizzBuzzLibrary/FizzBuzzMaster.cs file in the editor. Make sure the namespace is FizzBuzzLibrary and add the following method to the class:
public string GetResult(int nmbr) {  
  string result = "";
  return result;
}
You will notice that the above method is destined to fail. This is the fundamental principal of test driven development whereby methods are initially built to fail.

We should create our test cases. Open test/FizzBuzzTests/FizzBuzzTestsMaster.cs in the editor and replace the Test1() method with the following four test cases:
[Fact]
public void Given2Result12() {
  FizzBuzzMaster fbm = new FizzBuzzMaster();
  var expected = "1 2 ";
  var actual = fbm.GetResult(2);
  Assert.Equal(expected, actual);
}

[Fact]
public void Given4Result12fizz4() {
  FizzBuzzMaster fbm = new FizzBuzzMaster();
  var expected = "1 2 Fizz 4 ";
  var actual = fbm.GetResult(4);
  Assert.Equal(expected, actual);
}

[Fact]
public void Given5Result12fizz4buzz() {
  FizzBuzzMaster fbm = new FizzBuzzMaster();
  var expected = "1 2 Fizz 4 Buzz ";
  var actual = fbm.GetResult(5);
  Assert.Equal(expected, actual);
}

[Fact]
public void Given15Result12fizz4buzzfizz78fizzbuzz11fizzfizz1314fizzbuzz() {
  FizzBuzzMaster fbm = new FizzBuzzMaster();
  var expected = "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz ";
  var actual = fbm.GetResult(15);
  Assert.Equal(expected, actual);
}



Import the following namespace at the top of the above file test/FizzBuzzTests/FizzBuzzTestsMaster.cs:

using FizzBuzzLibrary;

We need to make a reference to the Library project from the test project. This is done by adding the following reference to the test/FizzBuzzTests/FizzBuzzTests.csproj file inside the <ItemGroup> XML block:

<ProjectReference Include="..\..\src\FizzBuzzLibrary\FizzBuzzLibrary.csproj" />
The <ItemGroup> XML block in file test/FizzBuzzTests/FizzBuzzTests.csproj now looks like this:
<ItemGroup>
  <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.0.0" />
  <PackageReference Include="xunit" Version="2.2.0" />
  <PackageReference Include="xunit.runner.visualstudio" Version="2.2.0" />
  <ProjectReference Include="..\..\src\FizzBuzzLibrary\FizzBuzzLibrary.csproj" />
</ItemGroup>

Running our tests

We have built our preliminary business logic and test cases. In addition, we referenced our business logic application into our test cases application. Now let us test things out.

To restore and build our Library application, execute the following commands from within the root FizzBuzz directory:
dotnet restore src/FizzBuzzLibrary
dotnet build src/FizzBuzzLibrary
dotnet restore test/FizzBuzzTests
dotnet build test/FizzBuzzTests
It is now time to run the actual tests. This is done by executing the following command also from within the root FizzBuss directory:

dotnet test test/FizzBuzzTests/FizzBuzzTests.csproj

The test execution shows the following results:
Build started, please wait...
Build completed.

Test run for F:\4870\FizzBuzz\test\FizzBuzzTests\bin\Debug\netcoreapp1.1\FizzBuzzTests.dll(.NETCoreApp,Version=v1.1)
Microsoft (R) Test Execution Command Line Tool Version 15.0.0.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
[xUnit.net 00:00:00.5936585]   Discovering: FizzBuzzTests
[xUnit.net 00:00:00.7169039]   Discovered:  FizzBuzzTests
[xUnit.net 00:00:00.7640234]   Starting:    FizzBuzzTests
[xUnit.net 00:00:00.9084551]     FizzBuzzTests.FizzBuzzTestsMaster.Given4Result12fizz4 [FAIL]
[xUnit.net 00:00:00.9105612]       Assert.Equal() Failure
[xUnit.net 00:00:00.9107578]                 ↓ (pos 0)
[xUnit.net 00:00:00.9108391]       Expected: 1 2 Fizz 4
[xUnit.net 00:00:00.9109069]       Actual:  
[xUnit.net 00:00:00.9109578]                 ↑ (pos 0)
[xUnit.net 00:00:00.9125774]       Stack Trace:
[xUnit.net 00:00:00.9143555]         F:\4870\FizzBuzz\test\FizzBuzzTests\FizzBuzzTestsMaster.cs(22,0): at FizzBuzzTests.FizzBuzzTestsMaster.Given4Result12fizz4()
[xUnit.net 00:00:00.9308443]     FizzBuzzTests.FizzBuzzTestsMaster.Given2Result12 [FAIL]
[xUnit.net 00:00:00.9309901]       Assert.Equal() Failure
[xUnit.net 00:00:00.9310410]                 ↓ (pos 0)
[xUnit.net 00:00:00.9310890]       Expected: 1 2
[xUnit.net 00:00:00.9311260]       Actual:  
[xUnit.net 00:00:00.9312167]                 ↑ (pos 0)
[xUnit.net 00:00:00.9313206]       Stack Trace:
[xUnit.net 00:00:00.9314166]         F:\4870\FizzBuzz\test\FizzBuzzTests\FizzBuzzTestsMaster.cs(14,0): at FizzBuzzTests.FizzBuzzTestsMaster.Given2Result12()
[xUnit.net 00:00:00.9319873]     FizzBuzzTests.FizzBuzzTestsMaster.Given5Result12fizz4buzz [FAIL]
[xUnit.net 00:00:00.9321133]       Assert.Equal() Failure
[xUnit.net 00:00:00.9321913]                 ↓ (pos 0)
[xUnit.net 00:00:00.9322648]       Expected: 1 2 Fizz 4 Buzz
[xUnit.net 00:00:00.9323297]       Actual:  
[xUnit.net 00:00:00.9323876]                 ↑ (pos 0)
[xUnit.net 00:00:00.9324578]       Stack Trace:
[xUnit.net 00:00:00.9325473]         F:\4870\FizzBuzz\test\FizzBuzzTests\FizzBuzzTestsMaster.cs(30,0): at FizzBuzzTests.FizzBuzzTestsMaster.Given5Result12fizz4buzz()
[xUnit.net 00:00:00.9327846]     FizzBuzzTests.FizzBuzzTestsMaster.Given15Result12fizz4buzzfizz78fizzbuzz11fizzfizz1314fizzbuzz [FAIL]
[xUnit.net 00:00:00.9328761]       Assert.Equal() Failure
[xUnit.net 00:00:00.9329459]                 ↓ (pos 0)
[xUnit.net 00:00:00.9330050]       Expected: 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fiz···
[xUnit.net 00:00:00.9331245]       Actual:  
[xUnit.net 00:00:00.9332037]                 ↑ (pos 0)
[xUnit.net 00:00:00.9332891]       Stack Trace:
[xUnit.net 00:00:00.9333733]         F:\4870\FizzBuzz\test\FizzBuzzTests\FizzBuzzTestsMaster.cs(38,0): at FizzBuzzTests.FizzBuzzTestsMaster.Given15Result12fizz4buzzfizz78fizzbuzz11fizzfizz1314fizzbuzz()
[xUnit.net 00:00:00.9356137]   Finished:    FizzBuzzTests
Failed   FizzBuzzTests.FizzBuzzTestsMaster.Given4Result12fizz4
Error Message:
Assert.Equal() Failure
          ↓ (pos 0)
Expected: 1 2 Fizz 4
Actual:  
          ↑ (pos 0)
Stack Trace:
   at FizzBuzzTests.FizzBuzzTestsMaster.Given4Result12fizz4() in F:\4870\FizzBuzz\test\FizzBuzzTests\FizzBuzzTestsMaster.cs:line 22
Failed   FizzBuzzTests.FizzBuzzTestsMaster.Given2Result12
Error Message:
Assert.Equal() Failure
          ↓ (pos 0)
Expected: 1 2
Actual:  
          ↑ (pos 0)
Stack Trace:
   at FizzBuzzTests.FizzBuzzTestsMaster.Given2Result12() in F:\4870\FizzBuzz\test\FizzBuzzTests\FizzBuzzTestsMaster.cs:line 14
Failed   FizzBuzzTests.FizzBuzzTestsMaster.Given5Result12fizz4buzz
Error Message:
Assert.Equal() Failure
          ↓ (pos 0)
Expected: 1 2 Fizz 4 Buzz
Actual:  
          ↑ (pos 0)
Stack Trace:
   at FizzBuzzTests.FizzBuzzTestsMaster.Given5Result12fizz4buzz() in F:\4870\FizzBuzz\test\FizzBuzzTests\FizzBuzzTestsMaster.cs:line 30
Failed   FizzBuzzTests.FizzBuzzTestsMaster.Given15Result12fizz4buzzfizz78fizzbuzz11fizzfizz1314fizzbuzz
Error Message:
Assert.Equal() Failure
          ↓ (pos 0)
Expected: 1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fiz···
Actual:  
          ↑ (pos 0)
Stack Trace:
   at FizzBuzzTests.FizzBuzzTestsMaster.Given15Result12fizz4buzzfizz78fizzbuzz11fizzfizz1314fizzbuzz() in F:\4870\FizzBuzz\test\FizzBuzzTests\FizzBuzzTestsMaster.cs:line 38

Total tests: 4. Passed: 0. Failed: 4. Skipped: 0.
Test Run Failed.
Test execution time: 2.1205 Seconds
Let’s fix all four failed tests by fixing our GetResult() method in src/FizzBuzzLibrary/FizzBuzzMaster.cs. Replace the GetResult() method with the following code:
public string GetResult(int nmbr) {  
    string result = "";

    for (int ndx=1; ndx<nmbr+1; ndx++) {
      if (ndx % 3 == 0 && ndx % 5 ==0) {
      result += "FizzBuzz ";
      } else if (ndx % 5 ==0 ) {
      result += "Buzz ";
      } else if (ndx % 3 ==0 ) {
      result += "Fizz ";                  
      }
      else
      result += ndx.ToString() + " ";
    }

    return result;
}
Build and run your tests again. This should be the outcome:
Build started, please wait...
Build completed.

Test run for F:\4870\FizzBuzz\test\FizzBuzzTests\bin\Debug\netcoreapp1.1\FizzBuzzTests.dll(.NETCoreApp,Version=v1.1)
Microsoft (R) Test Execution Command Line Tool Version 15.0.0.0
Copyright (c) Microsoft Corporation.  All rights reserved.

Starting test execution, please wait...
[xUnit.net 00:00:00.5944016]   Discovering: FizzBuzzTests
[xUnit.net 00:00:00.7260788]   Discovered:  FizzBuzzTests
[xUnit.net 00:00:00.7727980]   Starting:    FizzBuzzTests
[xUnit.net 00:00:00.9098086]   Finished:    FizzBuzzTests

Total tests: 4. Passed: 4. Failed: 0. Skipped: 0.
Test Run Successful.
Test execution time: 1.9927 Seconds
Rejoice that all our tests have successfully passed.